Repository: clin1223/VLDet Branch: main Commit: 172c93519f79 Files: 967 Total size: 5.6 MB Directory structure: gitextract_64k4_27x/ ├── .gitignore ├── LICENSE ├── README.md ├── configs/ │ ├── Base-C2_L_R5021k_640b64_4x.yaml │ ├── Base_OVCOCO_C4_1x.yaml │ ├── BoxSup-C2_Lbase_CLIP_R5021k_640b64.yaml │ ├── BoxSup-C2_Lbase_CLIP_SwinB_896b32.yaml │ ├── BoxSup_OVCOCO_CLIP_R50_1x.yaml │ ├── VLDet_LbaseCCcap_CLIP_R5021k_640b64_2x_ft4x_caption.yaml │ ├── VLDet_LbaseI_CLIP_SwinB_896b32_2x_ft4x_caption.yaml │ └── VLDet_OVCOCO_CLIP_R50_1x_caption.yaml ├── demo.py ├── detectron2/ │ ├── .circleci/ │ │ ├── config.yml │ │ └── import-tests.sh │ ├── .clang-format │ ├── .flake8 │ ├── GETTING_STARTED.md │ ├── INSTALL.md │ ├── LICENSE │ ├── MODEL_ZOO.md │ ├── README.md │ ├── configs/ │ │ ├── Base-RCNN-C4.yaml │ │ ├── Base-RCNN-DilatedC5.yaml │ │ ├── Base-RCNN-FPN.yaml │ │ ├── Base-RetinaNet.yaml │ │ ├── COCO-Detection/ │ │ │ ├── fast_rcnn_R_50_FPN_1x.yaml │ │ │ ├── faster_rcnn_R_101_C4_3x.yaml │ │ │ ├── faster_rcnn_R_101_DC5_3x.yaml │ │ │ ├── faster_rcnn_R_101_FPN_3x.yaml │ │ │ ├── faster_rcnn_R_50_C4_1x.yaml │ │ │ ├── faster_rcnn_R_50_C4_3x.yaml │ │ │ ├── faster_rcnn_R_50_DC5_1x.yaml │ │ │ ├── faster_rcnn_R_50_DC5_3x.yaml │ │ │ ├── faster_rcnn_R_50_FPN_1x.yaml │ │ │ ├── faster_rcnn_R_50_FPN_3x.yaml │ │ │ ├── faster_rcnn_X_101_32x8d_FPN_3x.yaml │ │ │ ├── fcos_R_50_FPN_1x.py │ │ │ ├── retinanet_R_101_FPN_3x.yaml │ │ │ ├── retinanet_R_50_FPN_1x.py │ │ │ ├── retinanet_R_50_FPN_1x.yaml │ │ │ ├── retinanet_R_50_FPN_3x.yaml │ │ │ ├── rpn_R_50_C4_1x.yaml │ │ │ └── rpn_R_50_FPN_1x.yaml │ │ ├── COCO-InstanceSegmentation/ │ │ │ ├── mask_rcnn_R_101_C4_3x.yaml │ │ │ ├── mask_rcnn_R_101_DC5_3x.yaml │ │ │ ├── mask_rcnn_R_101_FPN_3x.yaml │ │ │ ├── mask_rcnn_R_50_C4_1x.py │ │ │ ├── mask_rcnn_R_50_C4_1x.yaml │ │ │ ├── mask_rcnn_R_50_C4_3x.yaml │ │ │ ├── mask_rcnn_R_50_DC5_1x.yaml │ │ │ ├── mask_rcnn_R_50_DC5_3x.yaml │ │ │ ├── mask_rcnn_R_50_FPN_1x.py │ │ │ ├── mask_rcnn_R_50_FPN_1x.yaml │ │ │ ├── mask_rcnn_R_50_FPN_1x_giou.yaml │ │ │ ├── mask_rcnn_R_50_FPN_3x.yaml │ │ │ ├── mask_rcnn_X_101_32x8d_FPN_3x.yaml │ │ │ ├── mask_rcnn_regnetx_4gf_dds_fpn_1x.py │ │ │ └── mask_rcnn_regnety_4gf_dds_fpn_1x.py │ │ ├── COCO-Keypoints/ │ │ │ ├── Base-Keypoint-RCNN-FPN.yaml │ │ │ ├── keypoint_rcnn_R_101_FPN_3x.yaml │ │ │ ├── keypoint_rcnn_R_50_FPN_1x.py │ │ │ ├── keypoint_rcnn_R_50_FPN_1x.yaml │ │ │ ├── keypoint_rcnn_R_50_FPN_3x.yaml │ │ │ └── keypoint_rcnn_X_101_32x8d_FPN_3x.yaml │ │ ├── COCO-PanopticSegmentation/ │ │ │ ├── Base-Panoptic-FPN.yaml │ │ │ ├── panoptic_fpn_R_101_3x.yaml │ │ │ ├── panoptic_fpn_R_50_1x.py │ │ │ ├── panoptic_fpn_R_50_1x.yaml │ │ │ └── panoptic_fpn_R_50_3x.yaml │ │ ├── Cityscapes/ │ │ │ └── mask_rcnn_R_50_FPN.yaml │ │ ├── Detectron1-Comparisons/ │ │ │ ├── README.md │ │ │ ├── faster_rcnn_R_50_FPN_noaug_1x.yaml │ │ │ ├── keypoint_rcnn_R_50_FPN_1x.yaml │ │ │ └── mask_rcnn_R_50_FPN_noaug_1x.yaml │ │ ├── LVISv0.5-InstanceSegmentation/ │ │ │ ├── mask_rcnn_R_101_FPN_1x.yaml │ │ │ ├── mask_rcnn_R_50_FPN_1x.yaml │ │ │ └── mask_rcnn_X_101_32x8d_FPN_1x.yaml │ │ ├── LVISv1-InstanceSegmentation/ │ │ │ ├── mask_rcnn_R_101_FPN_1x.yaml │ │ │ ├── mask_rcnn_R_50_FPN_1x.yaml │ │ │ └── mask_rcnn_X_101_32x8d_FPN_1x.yaml │ │ ├── Misc/ │ │ │ ├── cascade_mask_rcnn_R_50_FPN_1x.yaml │ │ │ ├── cascade_mask_rcnn_R_50_FPN_3x.yaml │ │ │ ├── cascade_mask_rcnn_X_152_32x8d_FPN_IN5k_gn_dconv.yaml │ │ │ ├── mask_rcnn_R_50_FPN_1x_cls_agnostic.yaml │ │ │ ├── mask_rcnn_R_50_FPN_1x_dconv_c3-c5.yaml │ │ │ ├── mask_rcnn_R_50_FPN_3x_dconv_c3-c5.yaml │ │ │ ├── mask_rcnn_R_50_FPN_3x_gn.yaml │ │ │ ├── mask_rcnn_R_50_FPN_3x_syncbn.yaml │ │ │ ├── mmdet_mask_rcnn_R_50_FPN_1x.py │ │ │ ├── panoptic_fpn_R_101_dconv_cascade_gn_3x.yaml │ │ │ ├── scratch_mask_rcnn_R_50_FPN_3x_gn.yaml │ │ │ ├── scratch_mask_rcnn_R_50_FPN_9x_gn.yaml │ │ │ ├── scratch_mask_rcnn_R_50_FPN_9x_syncbn.yaml │ │ │ ├── semantic_R_50_FPN_1x.yaml │ │ │ └── torchvision_imagenet_R_50.py │ │ ├── PascalVOC-Detection/ │ │ │ ├── faster_rcnn_R_50_C4.yaml │ │ │ └── faster_rcnn_R_50_FPN.yaml │ │ ├── common/ │ │ │ ├── README.md │ │ │ ├── coco_schedule.py │ │ │ ├── data/ │ │ │ │ ├── coco.py │ │ │ │ ├── coco_keypoint.py │ │ │ │ ├── coco_panoptic_separated.py │ │ │ │ └── constants.py │ │ │ ├── optim.py │ │ │ └── train.py │ │ ├── new_baselines/ │ │ │ ├── mask_rcnn_R_101_FPN_100ep_LSJ.py │ │ │ ├── mask_rcnn_R_101_FPN_200ep_LSJ.py │ │ │ ├── mask_rcnn_R_101_FPN_400ep_LSJ.py │ │ │ ├── mask_rcnn_R_50_FPN_100ep_LSJ.py │ │ │ ├── mask_rcnn_R_50_FPN_200ep_LSJ.py │ │ │ ├── mask_rcnn_R_50_FPN_400ep_LSJ.py │ │ │ ├── mask_rcnn_R_50_FPN_50ep_LSJ.py │ │ │ ├── mask_rcnn_regnetx_4gf_dds_FPN_100ep_LSJ.py │ │ │ ├── mask_rcnn_regnetx_4gf_dds_FPN_200ep_LSJ.py │ │ │ ├── mask_rcnn_regnetx_4gf_dds_FPN_400ep_LSJ.py │ │ │ ├── mask_rcnn_regnety_4gf_dds_FPN_100ep_LSJ.py │ │ │ ├── mask_rcnn_regnety_4gf_dds_FPN_200ep_LSJ.py │ │ │ └── mask_rcnn_regnety_4gf_dds_FPN_400ep_LSJ.py │ │ └── quick_schedules/ │ │ ├── README.md │ │ ├── cascade_mask_rcnn_R_50_FPN_inference_acc_test.yaml │ │ ├── cascade_mask_rcnn_R_50_FPN_instant_test.yaml │ │ ├── fast_rcnn_R_50_FPN_inference_acc_test.yaml │ │ ├── fast_rcnn_R_50_FPN_instant_test.yaml │ │ ├── keypoint_rcnn_R_50_FPN_inference_acc_test.yaml │ │ ├── keypoint_rcnn_R_50_FPN_instant_test.yaml │ │ ├── keypoint_rcnn_R_50_FPN_normalized_training_acc_test.yaml │ │ ├── keypoint_rcnn_R_50_FPN_training_acc_test.yaml │ │ ├── mask_rcnn_R_50_C4_GCV_instant_test.yaml │ │ ├── mask_rcnn_R_50_C4_inference_acc_test.yaml │ │ ├── mask_rcnn_R_50_C4_instant_test.yaml │ │ ├── mask_rcnn_R_50_C4_training_acc_test.yaml │ │ ├── mask_rcnn_R_50_DC5_inference_acc_test.yaml │ │ ├── mask_rcnn_R_50_FPN_inference_acc_test.yaml │ │ ├── mask_rcnn_R_50_FPN_instant_test.yaml │ │ ├── mask_rcnn_R_50_FPN_pred_boxes_training_acc_test.yaml │ │ ├── mask_rcnn_R_50_FPN_training_acc_test.yaml │ │ ├── panoptic_fpn_R_50_inference_acc_test.yaml │ │ ├── panoptic_fpn_R_50_instant_test.yaml │ │ ├── panoptic_fpn_R_50_training_acc_test.yaml │ │ ├── retinanet_R_50_FPN_inference_acc_test.yaml │ │ ├── retinanet_R_50_FPN_instant_test.yaml │ │ ├── rpn_R_50_FPN_inference_acc_test.yaml │ │ ├── rpn_R_50_FPN_instant_test.yaml │ │ ├── semantic_R_50_FPN_inference_acc_test.yaml │ │ ├── semantic_R_50_FPN_instant_test.yaml │ │ └── semantic_R_50_FPN_training_acc_test.yaml │ ├── datasets/ │ │ ├── README.md │ │ ├── prepare_ade20k_sem_seg.py │ │ ├── prepare_cocofied_lvis.py │ │ ├── prepare_for_tests.sh │ │ └── prepare_panoptic_fpn.py │ ├── demo/ │ │ ├── README.md │ │ ├── demo.py │ │ └── predictor.py │ ├── detectron2/ │ │ ├── __init__.py │ │ ├── checkpoint/ │ │ │ ├── __init__.py │ │ │ ├── c2_model_loading.py │ │ │ ├── catalog.py │ │ │ └── detection_checkpoint.py │ │ ├── config/ │ │ │ ├── __init__.py │ │ │ ├── compat.py │ │ │ ├── config.py │ │ │ ├── defaults.py │ │ │ ├── instantiate.py │ │ │ └── lazy.py │ │ ├── data/ │ │ │ ├── __init__.py │ │ │ ├── benchmark.py │ │ │ ├── build.py │ │ │ ├── catalog.py │ │ │ ├── common.py │ │ │ ├── dataset_mapper.py │ │ │ ├── datasets/ │ │ │ │ ├── README.md │ │ │ │ ├── __init__.py │ │ │ │ ├── builtin.py │ │ │ │ ├── builtin_meta.py │ │ │ │ ├── cityscapes.py │ │ │ │ ├── cityscapes_panoptic.py │ │ │ │ ├── coco.py │ │ │ │ ├── coco_panoptic.py │ │ │ │ ├── lvis.py │ │ │ │ ├── lvis_v0_5_categories.py │ │ │ │ ├── lvis_v1_categories.py │ │ │ │ ├── lvis_v1_category_image_count.py │ │ │ │ ├── pascal_voc.py │ │ │ │ └── register_coco.py │ │ │ ├── detection_utils.py │ │ │ ├── samplers/ │ │ │ │ ├── __init__.py │ │ │ │ ├── distributed_sampler.py │ │ │ │ └── grouped_batch_sampler.py │ │ │ └── transforms/ │ │ │ ├── __init__.py │ │ │ ├── augmentation.py │ │ │ ├── augmentation_impl.py │ │ │ └── transform.py │ │ ├── engine/ │ │ │ ├── __init__.py │ │ │ ├── defaults.py │ │ │ ├── hooks.py │ │ │ ├── launch.py │ │ │ └── train_loop.py │ │ ├── evaluation/ │ │ │ ├── __init__.py │ │ │ ├── cityscapes_evaluation.py │ │ │ ├── coco_evaluation.py │ │ │ ├── evaluator.py │ │ │ ├── fast_eval_api.py │ │ │ ├── lvis_evaluation.py │ │ │ ├── panoptic_evaluation.py │ │ │ ├── pascal_voc_evaluation.py │ │ │ ├── rotated_coco_evaluation.py │ │ │ ├── sem_seg_evaluation.py │ │ │ └── testing.py │ │ ├── export/ │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── api.py │ │ │ ├── c10.py │ │ │ ├── caffe2_export.py │ │ │ ├── caffe2_inference.py │ │ │ ├── caffe2_modeling.py │ │ │ ├── caffe2_patch.py │ │ │ ├── flatten.py │ │ │ ├── shared.py │ │ │ ├── torchscript.py │ │ │ └── torchscript_patch.py │ │ ├── layers/ │ │ │ ├── __init__.py │ │ │ ├── aspp.py │ │ │ ├── batch_norm.py │ │ │ ├── blocks.py │ │ │ ├── csrc/ │ │ │ │ ├── README.md │ │ │ │ ├── ROIAlignRotated/ │ │ │ │ │ ├── ROIAlignRotated.h │ │ │ │ │ ├── ROIAlignRotated_cpu.cpp │ │ │ │ │ └── ROIAlignRotated_cuda.cu │ │ │ │ ├── box_iou_rotated/ │ │ │ │ │ ├── box_iou_rotated.h │ │ │ │ │ ├── box_iou_rotated_cpu.cpp │ │ │ │ │ ├── box_iou_rotated_cuda.cu │ │ │ │ │ └── box_iou_rotated_utils.h │ │ │ │ ├── cocoeval/ │ │ │ │ │ ├── cocoeval.cpp │ │ │ │ │ └── cocoeval.h │ │ │ │ ├── cuda_version.cu │ │ │ │ ├── deformable/ │ │ │ │ │ ├── deform_conv.h │ │ │ │ │ ├── deform_conv_cuda.cu │ │ │ │ │ └── deform_conv_cuda_kernel.cu │ │ │ │ ├── nms_rotated/ │ │ │ │ │ ├── nms_rotated.h │ │ │ │ │ ├── nms_rotated_cpu.cpp │ │ │ │ │ └── nms_rotated_cuda.cu │ │ │ │ └── vision.cpp │ │ │ ├── deform_conv.py │ │ │ ├── losses.py │ │ │ ├── mask_ops.py │ │ │ ├── nms.py │ │ │ ├── roi_align.py │ │ │ ├── roi_align_rotated.py │ │ │ ├── rotated_boxes.py │ │ │ ├── shape_spec.py │ │ │ └── wrappers.py │ │ ├── model_zoo/ │ │ │ ├── __init__.py │ │ │ ├── configs/ │ │ │ │ ├── Base-RCNN-C4.yaml │ │ │ │ ├── Base-RCNN-DilatedC5.yaml │ │ │ │ ├── Base-RCNN-FPN.yaml │ │ │ │ ├── Base-RetinaNet.yaml │ │ │ │ ├── COCO-Detection/ │ │ │ │ │ ├── fast_rcnn_R_50_FPN_1x.yaml │ │ │ │ │ ├── faster_rcnn_R_101_C4_3x.yaml │ │ │ │ │ ├── faster_rcnn_R_101_DC5_3x.yaml │ │ │ │ │ ├── faster_rcnn_R_101_FPN_3x.yaml │ │ │ │ │ ├── faster_rcnn_R_50_C4_1x.yaml │ │ │ │ │ ├── faster_rcnn_R_50_C4_3x.yaml │ │ │ │ │ ├── faster_rcnn_R_50_DC5_1x.yaml │ │ │ │ │ ├── faster_rcnn_R_50_DC5_3x.yaml │ │ │ │ │ ├── faster_rcnn_R_50_FPN_1x.yaml │ │ │ │ │ ├── faster_rcnn_R_50_FPN_3x.yaml │ │ │ │ │ ├── faster_rcnn_X_101_32x8d_FPN_3x.yaml │ │ │ │ │ ├── fcos_R_50_FPN_1x.py │ │ │ │ │ ├── retinanet_R_101_FPN_3x.yaml │ │ │ │ │ ├── retinanet_R_50_FPN_1x.py │ │ │ │ │ ├── retinanet_R_50_FPN_1x.yaml │ │ │ │ │ ├── retinanet_R_50_FPN_3x.yaml │ │ │ │ │ ├── rpn_R_50_C4_1x.yaml │ │ │ │ │ └── rpn_R_50_FPN_1x.yaml │ │ │ │ ├── COCO-InstanceSegmentation/ │ │ │ │ │ ├── mask_rcnn_R_101_C4_3x.yaml │ │ │ │ │ ├── mask_rcnn_R_101_DC5_3x.yaml │ │ │ │ │ ├── mask_rcnn_R_101_FPN_3x.yaml │ │ │ │ │ ├── mask_rcnn_R_50_C4_1x.py │ │ │ │ │ ├── mask_rcnn_R_50_C4_1x.yaml │ │ │ │ │ ├── mask_rcnn_R_50_C4_3x.yaml │ │ │ │ │ ├── mask_rcnn_R_50_DC5_1x.yaml │ │ │ │ │ ├── mask_rcnn_R_50_DC5_3x.yaml │ │ │ │ │ ├── mask_rcnn_R_50_FPN_1x.py │ │ │ │ │ ├── mask_rcnn_R_50_FPN_1x.yaml │ │ │ │ │ ├── mask_rcnn_R_50_FPN_1x_giou.yaml │ │ │ │ │ ├── mask_rcnn_R_50_FPN_3x.yaml │ │ │ │ │ ├── mask_rcnn_X_101_32x8d_FPN_3x.yaml │ │ │ │ │ ├── mask_rcnn_regnetx_4gf_dds_fpn_1x.py │ │ │ │ │ └── mask_rcnn_regnety_4gf_dds_fpn_1x.py │ │ │ │ ├── COCO-Keypoints/ │ │ │ │ │ ├── Base-Keypoint-RCNN-FPN.yaml │ │ │ │ │ ├── keypoint_rcnn_R_101_FPN_3x.yaml │ │ │ │ │ ├── keypoint_rcnn_R_50_FPN_1x.py │ │ │ │ │ ├── keypoint_rcnn_R_50_FPN_1x.yaml │ │ │ │ │ ├── keypoint_rcnn_R_50_FPN_3x.yaml │ │ │ │ │ └── keypoint_rcnn_X_101_32x8d_FPN_3x.yaml │ │ │ │ ├── COCO-PanopticSegmentation/ │ │ │ │ │ ├── Base-Panoptic-FPN.yaml │ │ │ │ │ ├── panoptic_fpn_R_101_3x.yaml │ │ │ │ │ ├── panoptic_fpn_R_50_1x.py │ │ │ │ │ ├── panoptic_fpn_R_50_1x.yaml │ │ │ │ │ └── panoptic_fpn_R_50_3x.yaml │ │ │ │ ├── Cityscapes/ │ │ │ │ │ └── mask_rcnn_R_50_FPN.yaml │ │ │ │ ├── Detectron1-Comparisons/ │ │ │ │ │ ├── README.md │ │ │ │ │ ├── faster_rcnn_R_50_FPN_noaug_1x.yaml │ │ │ │ │ ├── keypoint_rcnn_R_50_FPN_1x.yaml │ │ │ │ │ └── mask_rcnn_R_50_FPN_noaug_1x.yaml │ │ │ │ ├── LVISv0.5-InstanceSegmentation/ │ │ │ │ │ ├── mask_rcnn_R_101_FPN_1x.yaml │ │ │ │ │ ├── mask_rcnn_R_50_FPN_1x.yaml │ │ │ │ │ └── mask_rcnn_X_101_32x8d_FPN_1x.yaml │ │ │ │ ├── LVISv1-InstanceSegmentation/ │ │ │ │ │ ├── mask_rcnn_R_101_FPN_1x.yaml │ │ │ │ │ ├── mask_rcnn_R_50_FPN_1x.yaml │ │ │ │ │ └── mask_rcnn_X_101_32x8d_FPN_1x.yaml │ │ │ │ ├── Misc/ │ │ │ │ │ ├── cascade_mask_rcnn_R_50_FPN_1x.yaml │ │ │ │ │ ├── cascade_mask_rcnn_R_50_FPN_3x.yaml │ │ │ │ │ ├── cascade_mask_rcnn_X_152_32x8d_FPN_IN5k_gn_dconv.yaml │ │ │ │ │ ├── mask_rcnn_R_50_FPN_1x_cls_agnostic.yaml │ │ │ │ │ ├── mask_rcnn_R_50_FPN_1x_dconv_c3-c5.yaml │ │ │ │ │ ├── mask_rcnn_R_50_FPN_3x_dconv_c3-c5.yaml │ │ │ │ │ ├── mask_rcnn_R_50_FPN_3x_gn.yaml │ │ │ │ │ ├── mask_rcnn_R_50_FPN_3x_syncbn.yaml │ │ │ │ │ ├── mmdet_mask_rcnn_R_50_FPN_1x.py │ │ │ │ │ ├── panoptic_fpn_R_101_dconv_cascade_gn_3x.yaml │ │ │ │ │ ├── scratch_mask_rcnn_R_50_FPN_3x_gn.yaml │ │ │ │ │ ├── scratch_mask_rcnn_R_50_FPN_9x_gn.yaml │ │ │ │ │ ├── scratch_mask_rcnn_R_50_FPN_9x_syncbn.yaml │ │ │ │ │ ├── semantic_R_50_FPN_1x.yaml │ │ │ │ │ └── torchvision_imagenet_R_50.py │ │ │ │ ├── PascalVOC-Detection/ │ │ │ │ │ ├── faster_rcnn_R_50_C4.yaml │ │ │ │ │ └── faster_rcnn_R_50_FPN.yaml │ │ │ │ ├── common/ │ │ │ │ │ ├── README.md │ │ │ │ │ ├── coco_schedule.py │ │ │ │ │ ├── data/ │ │ │ │ │ │ ├── coco.py │ │ │ │ │ │ ├── coco_keypoint.py │ │ │ │ │ │ ├── coco_panoptic_separated.py │ │ │ │ │ │ └── constants.py │ │ │ │ │ ├── optim.py │ │ │ │ │ └── train.py │ │ │ │ ├── new_baselines/ │ │ │ │ │ ├── mask_rcnn_R_101_FPN_100ep_LSJ.py │ │ │ │ │ ├── mask_rcnn_R_101_FPN_200ep_LSJ.py │ │ │ │ │ ├── mask_rcnn_R_101_FPN_400ep_LSJ.py │ │ │ │ │ ├── mask_rcnn_R_50_FPN_100ep_LSJ.py │ │ │ │ │ ├── mask_rcnn_R_50_FPN_200ep_LSJ.py │ │ │ │ │ ├── mask_rcnn_R_50_FPN_400ep_LSJ.py │ │ │ │ │ ├── mask_rcnn_R_50_FPN_50ep_LSJ.py │ │ │ │ │ ├── mask_rcnn_regnetx_4gf_dds_FPN_100ep_LSJ.py │ │ │ │ │ ├── mask_rcnn_regnetx_4gf_dds_FPN_200ep_LSJ.py │ │ │ │ │ ├── mask_rcnn_regnetx_4gf_dds_FPN_400ep_LSJ.py │ │ │ │ │ ├── mask_rcnn_regnety_4gf_dds_FPN_100ep_LSJ.py │ │ │ │ │ ├── mask_rcnn_regnety_4gf_dds_FPN_200ep_LSJ.py │ │ │ │ │ └── mask_rcnn_regnety_4gf_dds_FPN_400ep_LSJ.py │ │ │ │ └── quick_schedules/ │ │ │ │ ├── README.md │ │ │ │ ├── cascade_mask_rcnn_R_50_FPN_inference_acc_test.yaml │ │ │ │ ├── cascade_mask_rcnn_R_50_FPN_instant_test.yaml │ │ │ │ ├── fast_rcnn_R_50_FPN_inference_acc_test.yaml │ │ │ │ ├── fast_rcnn_R_50_FPN_instant_test.yaml │ │ │ │ ├── keypoint_rcnn_R_50_FPN_inference_acc_test.yaml │ │ │ │ ├── keypoint_rcnn_R_50_FPN_instant_test.yaml │ │ │ │ ├── keypoint_rcnn_R_50_FPN_normalized_training_acc_test.yaml │ │ │ │ ├── keypoint_rcnn_R_50_FPN_training_acc_test.yaml │ │ │ │ ├── mask_rcnn_R_50_C4_GCV_instant_test.yaml │ │ │ │ ├── mask_rcnn_R_50_C4_inference_acc_test.yaml │ │ │ │ ├── mask_rcnn_R_50_C4_instant_test.yaml │ │ │ │ ├── mask_rcnn_R_50_C4_training_acc_test.yaml │ │ │ │ ├── mask_rcnn_R_50_DC5_inference_acc_test.yaml │ │ │ │ ├── mask_rcnn_R_50_FPN_inference_acc_test.yaml │ │ │ │ ├── mask_rcnn_R_50_FPN_instant_test.yaml │ │ │ │ ├── mask_rcnn_R_50_FPN_pred_boxes_training_acc_test.yaml │ │ │ │ ├── mask_rcnn_R_50_FPN_training_acc_test.yaml │ │ │ │ ├── panoptic_fpn_R_50_inference_acc_test.yaml │ │ │ │ ├── panoptic_fpn_R_50_instant_test.yaml │ │ │ │ ├── panoptic_fpn_R_50_training_acc_test.yaml │ │ │ │ ├── retinanet_R_50_FPN_inference_acc_test.yaml │ │ │ │ ├── retinanet_R_50_FPN_instant_test.yaml │ │ │ │ ├── rpn_R_50_FPN_inference_acc_test.yaml │ │ │ │ ├── rpn_R_50_FPN_instant_test.yaml │ │ │ │ ├── semantic_R_50_FPN_inference_acc_test.yaml │ │ │ │ ├── semantic_R_50_FPN_instant_test.yaml │ │ │ │ └── semantic_R_50_FPN_training_acc_test.yaml │ │ │ └── model_zoo.py │ │ ├── modeling/ │ │ │ ├── __init__.py │ │ │ ├── anchor_generator.py │ │ │ ├── backbone/ │ │ │ │ ├── __init__.py │ │ │ │ ├── backbone.py │ │ │ │ ├── build.py │ │ │ │ ├── fpn.py │ │ │ │ ├── mvit.py │ │ │ │ ├── regnet.py │ │ │ │ ├── resnet.py │ │ │ │ ├── swin.py │ │ │ │ ├── utils.py │ │ │ │ └── vit.py │ │ │ ├── box_regression.py │ │ │ ├── matcher.py │ │ │ ├── meta_arch/ │ │ │ │ ├── __init__.py │ │ │ │ ├── build.py │ │ │ │ ├── dense_detector.py │ │ │ │ ├── fcos.py │ │ │ │ ├── panoptic_fpn.py │ │ │ │ ├── rcnn.py │ │ │ │ ├── retinanet.py │ │ │ │ └── semantic_seg.py │ │ │ ├── mmdet_wrapper.py │ │ │ ├── poolers.py │ │ │ ├── postprocessing.py │ │ │ ├── proposal_generator/ │ │ │ │ ├── __init__.py │ │ │ │ ├── build.py │ │ │ │ ├── proposal_utils.py │ │ │ │ ├── rpn.py │ │ │ │ └── rrpn.py │ │ │ ├── roi_heads/ │ │ │ │ ├── __init__.py │ │ │ │ ├── box_head.py │ │ │ │ ├── cascade_rcnn.py │ │ │ │ ├── fast_rcnn.py │ │ │ │ ├── keypoint_head.py │ │ │ │ ├── mask_head.py │ │ │ │ ├── roi_heads.py │ │ │ │ └── rotated_fast_rcnn.py │ │ │ ├── sampling.py │ │ │ └── test_time_augmentation.py │ │ ├── projects/ │ │ │ ├── README.md │ │ │ └── __init__.py │ │ ├── solver/ │ │ │ ├── __init__.py │ │ │ ├── build.py │ │ │ └── lr_scheduler.py │ │ ├── structures/ │ │ │ ├── __init__.py │ │ │ ├── boxes.py │ │ │ ├── image_list.py │ │ │ ├── instances.py │ │ │ ├── keypoints.py │ │ │ ├── masks.py │ │ │ └── rotated_boxes.py │ │ ├── tracking/ │ │ │ ├── __init__.py │ │ │ ├── base_tracker.py │ │ │ ├── bbox_iou_tracker.py │ │ │ ├── hungarian_tracker.py │ │ │ ├── iou_weighted_hungarian_bbox_iou_tracker.py │ │ │ ├── utils.py │ │ │ └── vanilla_hungarian_bbox_iou_tracker.py │ │ └── utils/ │ │ ├── README.md │ │ ├── __init__.py │ │ ├── analysis.py │ │ ├── collect_env.py │ │ ├── colormap.py │ │ ├── comm.py │ │ ├── develop.py │ │ ├── env.py │ │ ├── events.py │ │ ├── file_io.py │ │ ├── logger.py │ │ ├── memory.py │ │ ├── registry.py │ │ ├── serialize.py │ │ ├── testing.py │ │ ├── video_visualizer.py │ │ └── visualizer.py │ ├── dev/ │ │ ├── README.md │ │ ├── linter.sh │ │ ├── packaging/ │ │ │ ├── README.md │ │ │ ├── build_all_wheels.sh │ │ │ ├── build_wheel.sh │ │ │ ├── gen_install_table.py │ │ │ ├── gen_wheel_index.sh │ │ │ └── pkg_helpers.bash │ │ ├── parse_results.sh │ │ ├── run_inference_tests.sh │ │ └── run_instant_tests.sh │ ├── docker/ │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── deploy.Dockerfile │ │ └── docker-compose.yml │ ├── docs/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── README.md │ │ ├── _static/ │ │ │ └── css/ │ │ │ └── custom.css │ │ ├── conf.py │ │ ├── index.rst │ │ ├── modules/ │ │ │ ├── checkpoint.rst │ │ │ ├── config.rst │ │ │ ├── data.rst │ │ │ ├── data_transforms.rst │ │ │ ├── engine.rst │ │ │ ├── evaluation.rst │ │ │ ├── export.rst │ │ │ ├── fvcore.rst │ │ │ ├── index.rst │ │ │ ├── layers.rst │ │ │ ├── model_zoo.rst │ │ │ ├── modeling.rst │ │ │ ├── solver.rst │ │ │ ├── structures.rst │ │ │ └── utils.rst │ │ ├── notes/ │ │ │ ├── benchmarks.md │ │ │ ├── changelog.md │ │ │ ├── compatibility.md │ │ │ ├── contributing.md │ │ │ └── index.rst │ │ ├── requirements.txt │ │ └── tutorials/ │ │ ├── README.md │ │ ├── augmentation.md │ │ ├── builtin_datasets.md │ │ ├── configs.md │ │ ├── data_loading.md │ │ ├── datasets.md │ │ ├── deployment.md │ │ ├── evaluation.md │ │ ├── extend.md │ │ ├── getting_started.md │ │ ├── index.rst │ │ ├── install.md │ │ ├── lazyconfigs.md │ │ ├── models.md │ │ ├── training.md │ │ └── write-models.md │ ├── projects/ │ │ ├── DeepLab/ │ │ │ ├── README.md │ │ │ ├── configs/ │ │ │ │ └── Cityscapes-SemanticSegmentation/ │ │ │ │ ├── Base-DeepLabV3-OS16-Semantic.yaml │ │ │ │ ├── deeplab_v3_R_103_os16_mg124_poly_90k_bs16.yaml │ │ │ │ └── deeplab_v3_plus_R_103_os16_mg124_poly_90k_bs16.yaml │ │ │ ├── deeplab/ │ │ │ │ ├── __init__.py │ │ │ │ ├── build_solver.py │ │ │ │ ├── config.py │ │ │ │ ├── loss.py │ │ │ │ ├── lr_scheduler.py │ │ │ │ ├── resnet.py │ │ │ │ └── semantic_seg.py │ │ │ └── train_net.py │ │ ├── DensePose/ │ │ │ ├── README.md │ │ │ ├── apply_net.py │ │ │ ├── configs/ │ │ │ │ ├── Base-DensePose-RCNN-FPN.yaml │ │ │ │ ├── HRNet/ │ │ │ │ │ ├── densepose_rcnn_HRFPN_HRNet_w32_s1x.yaml │ │ │ │ │ ├── densepose_rcnn_HRFPN_HRNet_w40_s1x.yaml │ │ │ │ │ └── densepose_rcnn_HRFPN_HRNet_w48_s1x.yaml │ │ │ │ ├── cse/ │ │ │ │ │ ├── Base-DensePose-RCNN-FPN-Human.yaml │ │ │ │ │ ├── Base-DensePose-RCNN-FPN.yaml │ │ │ │ │ ├── densepose_rcnn_R_101_FPN_DL_s1x.yaml │ │ │ │ │ ├── densepose_rcnn_R_101_FPN_DL_soft_s1x.yaml │ │ │ │ │ ├── densepose_rcnn_R_101_FPN_s1x.yaml │ │ │ │ │ ├── densepose_rcnn_R_101_FPN_soft_s1x.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_DL_s1x.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_DL_soft_s1x.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_s1x.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_soft_animals_CA_finetune_16k.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_soft_animals_CA_finetune_4k.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_soft_animals_I0_finetune_16k.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_soft_animals_I0_finetune_i2m_16k.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_soft_animals_I0_finetune_m2m_16k.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_soft_animals_finetune_16k.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_soft_animals_finetune_4k.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_soft_animals_finetune_maskonly_24k.yaml │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_soft_chimps_finetune_4k.yaml │ │ │ │ │ └── densepose_rcnn_R_50_FPN_soft_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_101_FPN_DL_WC1M_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_101_FPN_DL_WC1_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_101_FPN_DL_WC2M_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_101_FPN_DL_WC2_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_101_FPN_DL_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_101_FPN_WC1M_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_101_FPN_WC1_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_101_FPN_WC2M_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_101_FPN_WC2_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_101_FPN_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_101_FPN_s1x_legacy.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_DL_WC1M_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_DL_WC1_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_DL_WC2M_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_DL_WC2_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_DL_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_WC1M_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_WC1_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_WC2M_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_WC2_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_s1x.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_s1x_legacy.yaml │ │ │ │ ├── evolution/ │ │ │ │ │ ├── Base-RCNN-FPN-Atop10P_CA.yaml │ │ │ │ │ ├── densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA.yaml │ │ │ │ │ ├── densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_coarsesegm.yaml │ │ │ │ │ ├── densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_finesegm.yaml │ │ │ │ │ ├── densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_uniform.yaml │ │ │ │ │ └── densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_uv.yaml │ │ │ │ └── quick_schedules/ │ │ │ │ ├── cse/ │ │ │ │ │ ├── densepose_rcnn_R_50_FPN_DL_instant_test.yaml │ │ │ │ │ └── densepose_rcnn_R_50_FPN_soft_animals_finetune_instant_test.yaml │ │ │ │ ├── densepose_rcnn_HRFPN_HRNet_w32_instant_test.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_DL_instant_test.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_TTA_inference_acc_test.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_WC1_instant_test.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_WC2_instant_test.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_inference_acc_test.yaml │ │ │ │ ├── densepose_rcnn_R_50_FPN_instant_test.yaml │ │ │ │ └── densepose_rcnn_R_50_FPN_training_acc_test.yaml │ │ │ ├── densepose/ │ │ │ │ ├── __init__.py │ │ │ │ ├── config.py │ │ │ │ ├── converters/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── base.py │ │ │ │ │ ├── builtin.py │ │ │ │ │ ├── chart_output_hflip.py │ │ │ │ │ ├── chart_output_to_chart_result.py │ │ │ │ │ ├── hflip.py │ │ │ │ │ ├── segm_to_mask.py │ │ │ │ │ ├── to_chart_result.py │ │ │ │ │ └── to_mask.py │ │ │ │ ├── data/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── build.py │ │ │ │ │ ├── combined_loader.py │ │ │ │ │ ├── dataset_mapper.py │ │ │ │ │ ├── datasets/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ ├── builtin.py │ │ │ │ │ │ ├── chimpnsee.py │ │ │ │ │ │ ├── coco.py │ │ │ │ │ │ ├── dataset_type.py │ │ │ │ │ │ └── lvis.py │ │ │ │ │ ├── image_list_dataset.py │ │ │ │ │ ├── inference_based_loader.py │ │ │ │ │ ├── meshes/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ ├── builtin.py │ │ │ │ │ │ └── catalog.py │ │ │ │ │ ├── samplers/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ ├── densepose_base.py │ │ │ │ │ │ ├── densepose_confidence_based.py │ │ │ │ │ │ ├── densepose_cse_base.py │ │ │ │ │ │ ├── densepose_cse_confidence_based.py │ │ │ │ │ │ ├── densepose_cse_uniform.py │ │ │ │ │ │ ├── densepose_uniform.py │ │ │ │ │ │ ├── mask_from_densepose.py │ │ │ │ │ │ └── prediction_to_gt.py │ │ │ │ │ ├── transform/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ └── image.py │ │ │ │ │ ├── utils.py │ │ │ │ │ └── video/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── frame_selector.py │ │ │ │ │ └── video_keyframe_dataset.py │ │ │ │ ├── engine/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ └── trainer.py │ │ │ │ ├── evaluation/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── d2_evaluator_adapter.py │ │ │ │ │ ├── densepose_coco_evaluation.py │ │ │ │ │ ├── evaluator.py │ │ │ │ │ ├── mesh_alignment_evaluator.py │ │ │ │ │ └── tensor_storage.py │ │ │ │ ├── modeling/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── build.py │ │ │ │ │ ├── confidence.py │ │ │ │ │ ├── cse/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ ├── embedder.py │ │ │ │ │ │ ├── utils.py │ │ │ │ │ │ ├── vertex_direct_embedder.py │ │ │ │ │ │ └── vertex_feature_embedder.py │ │ │ │ │ ├── densepose_checkpoint.py │ │ │ │ │ ├── filter.py │ │ │ │ │ ├── hrfpn.py │ │ │ │ │ ├── hrnet.py │ │ │ │ │ ├── inference.py │ │ │ │ │ ├── losses/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ ├── chart.py │ │ │ │ │ │ ├── chart_with_confidences.py │ │ │ │ │ │ ├── cse.py │ │ │ │ │ │ ├── cycle_pix2shape.py │ │ │ │ │ │ ├── cycle_shape2shape.py │ │ │ │ │ │ ├── embed.py │ │ │ │ │ │ ├── embed_utils.py │ │ │ │ │ │ ├── mask.py │ │ │ │ │ │ ├── mask_or_segm.py │ │ │ │ │ │ ├── registry.py │ │ │ │ │ │ ├── segm.py │ │ │ │ │ │ ├── soft_embed.py │ │ │ │ │ │ └── utils.py │ │ │ │ │ ├── predictors/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ ├── chart.py │ │ │ │ │ │ ├── chart_confidence.py │ │ │ │ │ │ ├── chart_with_confidence.py │ │ │ │ │ │ ├── cse.py │ │ │ │ │ │ ├── cse_confidence.py │ │ │ │ │ │ ├── cse_with_confidence.py │ │ │ │ │ │ └── registry.py │ │ │ │ │ ├── roi_heads/ │ │ │ │ │ │ ├── __init__.py │ │ │ │ │ │ ├── deeplab.py │ │ │ │ │ │ ├── registry.py │ │ │ │ │ │ ├── roi_head.py │ │ │ │ │ │ └── v1convx.py │ │ │ │ │ ├── test_time_augmentation.py │ │ │ │ │ └── utils.py │ │ │ │ ├── structures/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── chart.py │ │ │ │ │ ├── chart_confidence.py │ │ │ │ │ ├── chart_result.py │ │ │ │ │ ├── cse.py │ │ │ │ │ ├── cse_confidence.py │ │ │ │ │ ├── data_relative.py │ │ │ │ │ ├── list.py │ │ │ │ │ ├── mesh.py │ │ │ │ │ └── transform_data.py │ │ │ │ ├── utils/ │ │ │ │ │ ├── __init__.py │ │ │ │ │ ├── dbhelper.py │ │ │ │ │ ├── logger.py │ │ │ │ │ └── transform.py │ │ │ │ └── vis/ │ │ │ │ ├── __init__.py │ │ │ │ ├── base.py │ │ │ │ ├── bounding_box.py │ │ │ │ ├── densepose_data_points.py │ │ │ │ ├── densepose_outputs_iuv.py │ │ │ │ ├── densepose_outputs_vertex.py │ │ │ │ ├── densepose_results.py │ │ │ │ ├── densepose_results_textures.py │ │ │ │ └── extractor.py │ │ │ ├── dev/ │ │ │ │ ├── README.md │ │ │ │ ├── run_inference_tests.sh │ │ │ │ └── run_instant_tests.sh │ │ │ ├── doc/ │ │ │ │ ├── BOOTSTRAPPING_PIPELINE.md │ │ │ │ ├── DENSEPOSE_CSE.md │ │ │ │ ├── DENSEPOSE_DATASETS.md │ │ │ │ ├── DENSEPOSE_IUV.md │ │ │ │ ├── GETTING_STARTED.md │ │ │ │ ├── RELEASE_2020_04.md │ │ │ │ ├── RELEASE_2021_03.md │ │ │ │ ├── RELEASE_2021_06.md │ │ │ │ ├── TOOL_APPLY_NET.md │ │ │ │ └── TOOL_QUERY_DB.md │ │ │ ├── query_db.py │ │ │ ├── setup.py │ │ │ ├── tests/ │ │ │ │ ├── common.py │ │ │ │ ├── test_chart_based_annotations_accumulator.py │ │ │ │ ├── test_combine_data_loader.py │ │ │ │ ├── test_cse_annotations_accumulator.py │ │ │ │ ├── test_dataset_loaded_annotations.py │ │ │ │ ├── test_frame_selector.py │ │ │ │ ├── test_image_list_dataset.py │ │ │ │ ├── test_image_resize_transform.py │ │ │ │ ├── test_model_e2e.py │ │ │ │ ├── test_setup.py │ │ │ │ ├── test_structures.py │ │ │ │ ├── test_tensor_storage.py │ │ │ │ └── test_video_keyframe_dataset.py │ │ │ └── train_net.py │ │ ├── MViTv2/ │ │ │ ├── README.md │ │ │ └── configs/ │ │ │ ├── cascade_mask_rcnn_mvitv2_b_3x.py │ │ │ ├── cascade_mask_rcnn_mvitv2_b_in21k_3x.py │ │ │ ├── cascade_mask_rcnn_mvitv2_h_in21k_lsj_3x.py │ │ │ ├── cascade_mask_rcnn_mvitv2_l_in21k_lsj_50ep.py │ │ │ ├── cascade_mask_rcnn_mvitv2_s_3x.py │ │ │ ├── cascade_mask_rcnn_mvitv2_t_3x.py │ │ │ ├── common/ │ │ │ │ ├── coco_loader.py │ │ │ │ └── coco_loader_lsj.py │ │ │ └── mask_rcnn_mvitv2_t_3x.py │ │ ├── Panoptic-DeepLab/ │ │ │ ├── README.md │ │ │ ├── configs/ │ │ │ │ ├── COCO-PanopticSegmentation/ │ │ │ │ │ └── panoptic_deeplab_R_52_os16_mg124_poly_200k_bs64_crop_640_640_coco_dsconv.yaml │ │ │ │ └── Cityscapes-PanopticSegmentation/ │ │ │ │ ├── Base-PanopticDeepLab-OS16.yaml │ │ │ │ ├── panoptic_deeplab_R_52_os16_mg124_poly_90k_bs32_crop_512_1024.yaml │ │ │ │ └── panoptic_deeplab_R_52_os16_mg124_poly_90k_bs32_crop_512_1024_dsconv.yaml │ │ │ ├── panoptic_deeplab/ │ │ │ │ ├── __init__.py │ │ │ │ ├── config.py │ │ │ │ ├── dataset_mapper.py │ │ │ │ ├── panoptic_seg.py │ │ │ │ ├── post_processing.py │ │ │ │ └── target_generator.py │ │ │ └── train_net.py │ │ ├── PointRend/ │ │ │ ├── README.md │ │ │ ├── configs/ │ │ │ │ ├── InstanceSegmentation/ │ │ │ │ │ ├── Base-Implicit-PointRend.yaml │ │ │ │ │ ├── Base-PointRend-RCNN-FPN.yaml │ │ │ │ │ ├── implicit_pointrend_R_50_FPN_1x_coco.yaml │ │ │ │ │ ├── implicit_pointrend_R_50_FPN_3x_coco.yaml │ │ │ │ │ ├── pointrend_rcnn_R_101_FPN_3x_coco.yaml │ │ │ │ │ ├── pointrend_rcnn_R_50_FPN_1x_cityscapes.yaml │ │ │ │ │ ├── pointrend_rcnn_R_50_FPN_1x_coco.yaml │ │ │ │ │ ├── pointrend_rcnn_R_50_FPN_3x_coco.yaml │ │ │ │ │ └── pointrend_rcnn_X_101_32x8d_FPN_3x_coco.yaml │ │ │ │ └── SemanticSegmentation/ │ │ │ │ ├── Base-PointRend-Semantic-FPN.yaml │ │ │ │ └── pointrend_semantic_R_101_FPN_1x_cityscapes.yaml │ │ │ ├── point_rend/ │ │ │ │ ├── __init__.py │ │ │ │ ├── color_augmentation.py │ │ │ │ ├── config.py │ │ │ │ ├── mask_head.py │ │ │ │ ├── point_features.py │ │ │ │ ├── point_head.py │ │ │ │ ├── roi_heads.py │ │ │ │ └── semantic_seg.py │ │ │ └── train_net.py │ │ ├── PointSup/ │ │ │ ├── README.md │ │ │ ├── configs/ │ │ │ │ ├── implicit_pointrend_R_50_FPN_3x_point_sup_point_aug_coco.yaml │ │ │ │ ├── mask_rcnn_R_50_FPN_3x_point_sup_coco.yaml │ │ │ │ └── mask_rcnn_R_50_FPN_3x_point_sup_point_aug_coco.yaml │ │ │ ├── point_sup/ │ │ │ │ ├── __init__.py │ │ │ │ ├── config.py │ │ │ │ ├── dataset_mapper.py │ │ │ │ ├── detection_utils.py │ │ │ │ ├── mask_head.py │ │ │ │ ├── point_utils.py │ │ │ │ └── register_point_annotations.py │ │ │ ├── tools/ │ │ │ │ └── prepare_coco_point_annotations_without_masks.py │ │ │ └── train_net.py │ │ ├── README.md │ │ ├── Rethinking-BatchNorm/ │ │ │ ├── README.md │ │ │ ├── configs/ │ │ │ │ ├── mask_rcnn_BNhead.py │ │ │ │ ├── mask_rcnn_BNhead_batch_stats.py │ │ │ │ ├── mask_rcnn_BNhead_shuffle.py │ │ │ │ ├── mask_rcnn_SyncBNhead.py │ │ │ │ ├── retinanet_SyncBNhead.py │ │ │ │ └── retinanet_SyncBNhead_SharedTraining.py │ │ │ └── retinanet-eval-domain-specific.py │ │ ├── TensorMask/ │ │ │ ├── README.md │ │ │ ├── configs/ │ │ │ │ ├── Base-TensorMask.yaml │ │ │ │ ├── tensormask_R_50_FPN_1x.yaml │ │ │ │ └── tensormask_R_50_FPN_6x.yaml │ │ │ ├── setup.py │ │ │ ├── tensormask/ │ │ │ │ ├── __init__.py │ │ │ │ ├── arch.py │ │ │ │ ├── config.py │ │ │ │ └── layers/ │ │ │ │ ├── __init__.py │ │ │ │ ├── csrc/ │ │ │ │ │ ├── SwapAlign2Nat/ │ │ │ │ │ │ ├── SwapAlign2Nat.h │ │ │ │ │ │ └── SwapAlign2Nat_cuda.cu │ │ │ │ │ └── vision.cpp │ │ │ │ └── swap_align2nat.py │ │ │ ├── tests/ │ │ │ │ ├── __init__.py │ │ │ │ └── test_swap_align2nat.py │ │ │ └── train_net.py │ │ ├── TridentNet/ │ │ │ ├── README.md │ │ │ ├── configs/ │ │ │ │ ├── Base-TridentNet-Fast-C4.yaml │ │ │ │ ├── tridentnet_fast_R_101_C4_3x.yaml │ │ │ │ ├── tridentnet_fast_R_50_C4_1x.yaml │ │ │ │ └── tridentnet_fast_R_50_C4_3x.yaml │ │ │ ├── train_net.py │ │ │ └── tridentnet/ │ │ │ ├── __init__.py │ │ │ ├── config.py │ │ │ ├── trident_backbone.py │ │ │ ├── trident_conv.py │ │ │ ├── trident_rcnn.py │ │ │ └── trident_rpn.py │ │ └── ViTDet/ │ │ ├── README.md │ │ └── configs/ │ │ ├── COCO/ │ │ │ ├── cascade_mask_rcnn_mvitv2_b_in21k_100ep.py │ │ │ ├── cascade_mask_rcnn_mvitv2_h_in21k_36ep.py │ │ │ ├── cascade_mask_rcnn_mvitv2_l_in21k_50ep.py │ │ │ ├── cascade_mask_rcnn_swin_b_in21k_50ep.py │ │ │ ├── cascade_mask_rcnn_swin_l_in21k_50ep.py │ │ │ ├── cascade_mask_rcnn_vitdet_b_100ep.py │ │ │ ├── cascade_mask_rcnn_vitdet_h_75ep.py │ │ │ ├── cascade_mask_rcnn_vitdet_l_100ep.py │ │ │ ├── mask_rcnn_vitdet_b_100ep.py │ │ │ ├── mask_rcnn_vitdet_h_75ep.py │ │ │ └── mask_rcnn_vitdet_l_100ep.py │ │ ├── LVIS/ │ │ │ ├── cascade_mask_rcnn_mvitv2_b_in21k_100ep.py │ │ │ ├── cascade_mask_rcnn_mvitv2_h_in21k_50ep.py │ │ │ ├── cascade_mask_rcnn_mvitv2_l_in21k_50ep.py │ │ │ ├── cascade_mask_rcnn_swin_b_in21k_50ep.py │ │ │ ├── cascade_mask_rcnn_swin_l_in21k_50ep.py │ │ │ ├── cascade_mask_rcnn_vitdet_b_100ep.py │ │ │ ├── cascade_mask_rcnn_vitdet_h_100ep.py │ │ │ ├── cascade_mask_rcnn_vitdet_l_100ep.py │ │ │ ├── mask_rcnn_vitdet_b_100ep.py │ │ │ ├── mask_rcnn_vitdet_h_100ep.py │ │ │ └── mask_rcnn_vitdet_l_100ep.py │ │ └── common/ │ │ └── coco_loader_lsj.py │ ├── setup.cfg │ ├── setup.py │ ├── tests/ │ │ ├── README.md │ │ ├── __init__.py │ │ ├── config/ │ │ │ ├── dir1/ │ │ │ │ ├── dir1_a.py │ │ │ │ └── dir1_b.py │ │ │ ├── root_cfg.py │ │ │ ├── test_instantiate_config.py │ │ │ ├── test_lazy_config.py │ │ │ └── test_yacs_config.py │ │ ├── data/ │ │ │ ├── __init__.py │ │ │ ├── test_coco.py │ │ │ ├── test_coco_evaluation.py │ │ │ ├── test_dataset.py │ │ │ ├── test_detection_utils.py │ │ │ ├── test_rotation_transform.py │ │ │ ├── test_sampler.py │ │ │ └── test_transforms.py │ │ ├── export/ │ │ │ └── test_c10.py │ │ ├── layers/ │ │ │ ├── __init__.py │ │ │ ├── test_blocks.py │ │ │ ├── test_deformable.py │ │ │ ├── test_losses.py │ │ │ ├── test_mask_ops.py │ │ │ ├── test_nms.py │ │ │ ├── test_nms_rotated.py │ │ │ ├── test_roi_align.py │ │ │ └── test_roi_align_rotated.py │ │ ├── modeling/ │ │ │ ├── __init__.py │ │ │ ├── test_anchor_generator.py │ │ │ ├── test_backbone.py │ │ │ ├── test_box2box_transform.py │ │ │ ├── test_fast_rcnn.py │ │ │ ├── test_matcher.py │ │ │ ├── test_mmdet.py │ │ │ ├── test_model_e2e.py │ │ │ ├── test_roi_heads.py │ │ │ ├── test_roi_pooler.py │ │ │ └── test_rpn.py │ │ ├── structures/ │ │ │ ├── __init__.py │ │ │ ├── test_boxes.py │ │ │ ├── test_imagelist.py │ │ │ ├── test_instances.py │ │ │ ├── test_keypoints.py │ │ │ ├── test_masks.py │ │ │ └── test_rotated_boxes.py │ │ ├── test_checkpoint.py │ │ ├── test_engine.py │ │ ├── test_events.py │ │ ├── test_export_caffe2.py │ │ ├── test_export_onnx.py │ │ ├── test_export_torchscript.py │ │ ├── test_model_analysis.py │ │ ├── test_model_zoo.py │ │ ├── test_packaging.py │ │ ├── test_registry.py │ │ ├── test_scheduler.py │ │ ├── test_solver.py │ │ ├── test_visualizer.py │ │ └── tracking/ │ │ ├── __init__.py │ │ ├── test_bbox_iou_tracker.py │ │ ├── test_hungarian_tracker.py │ │ ├── test_iou_weighted_hungarian_bbox_iou_tracker.py │ │ └── test_vanilla_hungarian_bbox_iou_tracker.py │ └── tools/ │ ├── README.md │ ├── __init__.py │ ├── analyze_model.py │ ├── benchmark.py │ ├── convert-torchvision-to-d2.py │ ├── deploy/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── export_model.py │ │ └── torchscript_mask_rcnn.cpp │ ├── lazyconfig_train_net.py │ ├── lightning_train_net.py │ ├── plain_train_net.py │ ├── train_net.py │ ├── visualize_data.py │ └── visualize_json_results.py ├── prepare_datasets.md ├── requirements.txt ├── tools/ │ ├── convert-thirdparty-pretrained-model-to-d2.py │ ├── download_cc.py │ ├── get_cc_tags.py │ ├── get_coco_zeroshot.py │ ├── get_lvis_cat_info.py │ ├── get_tags_for_VLDet_concepts.py │ └── remove_lvis_rare.py ├── train_net.py └── vldet/ ├── __init__.py ├── config.py ├── custom_solver.py ├── data/ │ ├── custom_build_augmentation.py │ ├── custom_dataset_dataloader.py │ ├── custom_dataset_mapper.py │ ├── datasets/ │ │ ├── cc.py │ │ ├── coco_zeroshot.py │ │ ├── imagenet.py │ │ ├── lvis_22k_categories.py │ │ ├── lvis_v1.py │ │ ├── objects365.py │ │ ├── oid.py │ │ └── register_oid.py │ ├── tar_dataset.py │ └── transforms/ │ ├── custom_augmentation_impl.py │ └── custom_transform.py ├── evaluation/ │ ├── custom_coco_eval.py │ └── oideval.py ├── modeling/ │ ├── backbone/ │ │ ├── swintransformer.py │ │ └── timm.py │ ├── debug.py │ ├── meta_arch/ │ │ └── custom_rcnn.py │ ├── roi_heads/ │ │ ├── res5_roi_heads.py │ │ ├── vldet_fast_rcnn.py │ │ ├── vldet_roi_heads.py │ │ └── zero_shot_classifier.py │ ├── text/ │ │ └── text_encoder.py │ └── utils.py └── predictor.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /CenterNet2 CenterNet2 models configs-experimental experiments # output dir index.html data/* slurm/ slurm slurm-output slurm-output/ output instant_test_output inference_test_output *.png *.diff *.jpg !/projects/DensePose/doc/images/*.jpg # compilation and distribution __pycache__ _ext *.pyc *.pyd *.so *.dll *.egg-info/ build/ dist/ wheels/ # pytorch/python/numpy formats *.pth !models/*.pth *.pkl *.ts model_ts*.txt # ipython/jupyter notebooks *.ipynb **/.ipynb_checkpoints/ # Editor temporaries *.swn *.swo *.swp *~ # editor settings .idea .vscode _darcs # project dirs /datasets/* /projects/*/datasets /snippet ================================================ FILE: LICENSE ================================================ VLDet (c) by Chuang Lin VLDet is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. You should have received a copy of the license along with this work. If not, see . Attribution-NonCommercial-ShareAlike 4.0 International ======================================================================= Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More_considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. BY-NC-SA Compatible License means a license listed at creativecommons.org/compatiblelicenses, approved by Creative Commons as essentially the equivalent of this Public License. d. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. e. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. f. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. g. License Elements means the license attributes listed in the name of a Creative Commons Public License. The License Elements of this Public License are Attribution, NonCommercial, and ShareAlike. h. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. i. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. j. Licensor means the individual(s) or entity(ies) granting rights under this Public License. k. NonCommercial means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. l. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. m. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. n. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and b. produce, reproduce, and Share Adapted Material for NonCommercial purposes only. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. Additional offer from the Licensor -- Adapted Material. Every recipient of Adapted Material from You automatically receives an offer from the Licensor to exercise the Licensed Rights in the Adapted Material under the conditions of the Adapter's License You apply. c. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. b. ShareAlike. In addition to the conditions in Section 3(a), if You Share Adapted Material You produce, the following conditions also apply. 1. The Adapter's License You apply must be a Creative Commons license with the same License Elements, this version or later, or a BY-NC-SA Compatible License. 2. You must include the text of, or the URI or hyperlink to, the Adapter's License You apply. You may satisfy this condition in any reasonable manner based on the medium, means, and context in which You Share Adapted Material. 3. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, Adapted Material that restrict exercise of the rights granted under the Adapter's License You apply. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material, including for purposes of Section 3(b); and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” The text of the Creative Commons public licenses is dedicated to the public domain under the CC0 Public Domain Dedication. Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. ================================================ FILE: README.md ================================================ # VLDet: Learning Object-Language Alignments for Open-Vocabulary Object Detection

> [**Learning Object-Language Alignments for Open-Vocabulary Object Detection**](https://arxiv.org/abs/2211.14843), > Chuang Lin, Peize Sun, Yi Jiang, Ping Luo, Lizhen Qu, Gholamreza Haffari, Zehuan Yuan, Jianfei Cai, > *ICLR 2023 ([https://arxiv.org/abs/2211.14843](https://arxiv.org/abs/2211.14843))* ## Highlight We are excited to announce that our paper was accepted to ICLR 2023! 🥳🥳🥳 ## A quick explainable video demo for VLDet https://user-images.githubusercontent.com/6366788/218620999-1eb5c5eb-0479-4dcc-88ca-863f34de25a0.mp4 ## Performance ### Open-Vocabulary on COCO

### Open-Vocabulary on LVIS

## Installation ### Requirements - Linux or macOS with Python ≥ 3.7 - PyTorch ≥ 1.9. Install them together at [pytorch.org](https://pytorch.org) to make sure of this. Note, please check PyTorch version matches that is required by Detectron2. - Detectron2: follow [Detectron2 installation instructions](https://detectron2.readthedocs.io/tutorials/install.html). ### Example conda environment setup ```bash conda create --name VLDet python=3.7 -y conda activate VLDet conda install pytorch torchvision torchaudio cudatoolkit=11.1 -c pytorch-lts -c nvidia # under your working directory git clone https://github.com/clin1223/VLDet.git cd VLDet cd detectron2 pip install -e . cd .. pip install -r requirements.txt ``` ## Features - Directly learn an open-vocabulary object detector from image-text pairs by formulating the task as a bipartite matching problem. - State-of-the-art results on Open-vocabulary LVIS and Open-vocabulary COCO. - Scaling and extending novel object vocabulary easily. ## Benchmark evaluation and training Please first [prepare datasets](prepare_datasets.md). The VLDet models are finetuned on the corresponding [Box-Supervised models](https://drive.google.com/drive/folders/1ngb1mBOUvFpkcUM7D3bgIkMdUj2W5FUa?usp=sharing) (indicated by MODEL.WEIGHTS in the config files). Please train or download the Box-Supervised model and place them under VLDet_ROOT/models/ before training the VLDet models. To train a model, run ``` python train_net.py --num-gpus 8 --config-file /path/to/config/name.yaml ``` To evaluate a model with a trained/ pretrained model, run ``` python train_net.py --num-gpus 8 --config-file /path/to/config/name.yaml --eval-only MODEL.WEIGHTS /path/to/weight.pth ``` Download the trained network weights [here](https://drive.google.com/drive/folders/1ngb1mBOUvFpkcUM7D3bgIkMdUj2W5FUa?usp=sharing). | OV_COCO | box mAP50 | box mAP50_novel | |----------|-----------|-----------------| | [config_RN50](configs/VLDet_OVCOCO_CLIP_R50_1x_caption.yaml) | 45.8 | 32.0 | | OV_LVIS | mask mAP_all | mask mAP_novel | | ------------- | ------------ | -------------- | | [config_RN50](configs/VLDet_LbaseCCcap_CLIP_R5021k_640b64_2x_ft4x_caption.yaml) | 30.1 | 21.7 | | [config_Swin-B](configs/VLDet_LbaseI_CLIP_SwinB_896b32_2x_ft4x_caption.yaml) | 38.1 | 26.3 | ## Citation If you find this project useful for your research, please use the following BibTeX entry. ``` @article{VLDet, title={Learning Object-Language Alignments for Open-Vocabulary Object Detection}, author={Lin, Chuang and Sun, Peize and Jiang, Yi and Luo, Ping and Qu, Lizhen and Haffari, Gholamreza and Yuan, Zehuan and Cai, Jianfei}, journal={arXiv preprint arXiv:2211.14843}, year={2022} } ``` ## License Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. ## Acknowledgement This repository was built on top of [Detectron2](https://github.com/facebookresearch/detectron2), [Detic](https://github.com/facebookresearch/Detic.git), [RegionCLIP](https://github.com/microsoft/RegionCLIP.git) and [OVR-CNN](https://github.com/alirezazareian/ovr-cnn). We thank for their hard work. ================================================ FILE: configs/Base-C2_L_R5021k_640b64_4x.yaml ================================================ MODEL: META_ARCHITECTURE: "CustomRCNN" MASK_ON: True PROPOSAL_GENERATOR: NAME: "CenterNet" WEIGHTS: "models/resnet50_miil_21k.pkl" BACKBONE: NAME: build_p67_timm_fpn_backbone TIMM: BASE_NAME: resnet50_in21k FPN: IN_FEATURES: ["layer3", "layer4", "layer5"] PIXEL_MEAN: [123.675, 116.280, 103.530] PIXEL_STD: [58.395, 57.12, 57.375] ROI_HEADS: NAME: DeticCascadeROIHeads IN_FEATURES: ["p3", "p4", "p5"] IOU_THRESHOLDS: [0.6] NUM_CLASSES: 1203 SCORE_THRESH_TEST: 0.02 NMS_THRESH_TEST: 0.5 ROI_BOX_CASCADE_HEAD: IOUS: [0.6, 0.7, 0.8] ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_FC: 2 POOLER_RESOLUTION: 7 CLS_AGNOSTIC_BBOX_REG: True MULT_PROPOSAL_SCORE: True USE_SIGMOID_CE: True USE_FED_LOSS: True ROI_MASK_HEAD: NAME: "MaskRCNNConvUpsampleHead" NUM_CONV: 4 POOLER_RESOLUTION: 14 CLS_AGNOSTIC_MASK: True CENTERNET: NUM_CLASSES: 1203 REG_WEIGHT: 1. NOT_NORM_REG: True ONLY_PROPOSAL: True WITH_AGN_HM: True INFERENCE_TH: 0.0001 PRE_NMS_TOPK_TRAIN: 4000 POST_NMS_TOPK_TRAIN: 2000 PRE_NMS_TOPK_TEST: 1000 POST_NMS_TOPK_TEST: 256 NMS_TH_TRAIN: 0.9 NMS_TH_TEST: 0.9 POS_WEIGHT: 0.5 NEG_WEIGHT: 0.5 IGNORE_HIGH_FP: 0.85 DATASETS: TRAIN: ("lvis_v1_train",) TEST: ("lvis_v1_val",) DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 NUM_WORKERS: 8 TEST: DETECTIONS_PER_IMAGE: 300 SOLVER: LR_SCHEDULER_NAME: "WarmupCosineLR" CHECKPOINT_PERIOD: 1000000000 WARMUP_ITERS: 10000 WARMUP_FACTOR: 0.0001 USE_CUSTOM_SOLVER: True OPTIMIZER: "ADAMW" MAX_ITER: 90000 IMS_PER_BATCH: 64 BASE_LR: 0.0002 CLIP_GRADIENTS: ENABLED: True INPUT: FORMAT: RGB CUSTOM_AUG: EfficientDetResizeCrop TRAIN_SIZE: 640 OUTPUT_DIR: "./output/VLDet/auto" EVAL_PROPOSAL_AR: False VERSION: 2 FP16: True ================================================ FILE: configs/Base_OVCOCO_C4_1x.yaml ================================================ MODEL: META_ARCHITECTURE: "CustomRCNN" RPN: PRE_NMS_TOPK_TEST: 6000 POST_NMS_TOPK_TEST: 1000 ROI_HEADS: NUM_CLASSES: 65 NAME: "CustomRes5ROIHeads" SHARE_PROJ_V_DIM: 2048 SHARE_PROJ_L_DIM: 1024 WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_BOX_HEAD: CLS_AGNOSTIC_BBOX_REG: True USE_SIGMOID_CE: True USE_ZEROSHOT_CLS: True ZEROSHOT_WEIGHT_PATH: 'datasets/coco/VLDet/coco_nouns_4764_emb.pth' DETECTION_WEIGHT_PATH: 'datasets/coco/VLDet/coco_65_cls_emb.pth' IGNORE_ZERO_CATS: True CAT_FREQ_PATH: 'datasets/coco/zero-shot/instances_train2017_seen_2_del_cat_info.json' ZEROSHOT_WEIGHT_DIM: 1024 DATASETS: TRAIN: ("coco_zeroshot_train_del",) TEST: ("coco_generalized_del_val",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.02 STEPS: (60000, 80000) MAX_ITER: 90000 CHECKPOINT_PERIOD: 10000 INPUT: MIN_SIZE_TRAIN: (800,) VERSION: 2 OUTPUT_DIR: output/release_base_coco FP16: True TEST: EVAL_PERIOD: 10000 ================================================ FILE: configs/BoxSup-C2_Lbase_CLIP_R5021k_640b64.yaml ================================================ _BASE_: "Base-C2_L_R5021k_640b64_4x.yaml" MODEL: WITH_CAPTION: False ROI_BOX_HEAD: USE_ZEROSHOT_CLS: True ZEROSHOT_WEIGHT_PATH: 'datasets/cc3m/VLDet/googlecc_nouns_6250_emb.pth' DETECTION_WEIGHT_PATH: 'datasets/cc3m/VLDet/lvis_1203_cls_emb.pth' ZEROSHOT_WEIGHT_DIM: 1024 SHARE_PROJ_V_DIM: 1024 SHARE_PROJ_L_DIM: 1024 SOLVER: IMS_PER_BATCH: 8 DATASETS: TRAIN: ("lvis_v1_train_norare",) ================================================ FILE: configs/BoxSup-C2_Lbase_CLIP_SwinB_896b32.yaml ================================================ _BASE_: "Base-C2_L_R5021k_640b64_4x.yaml" MODEL: ROI_BOX_HEAD: USE_ZEROSHOT_CLS: True ZEROSHOT_WEIGHT_PATH: 'datasets/cc3m/VLDet/googlecc_nouns_6250_emb.pth' DETECTION_WEIGHT_PATH: 'datasets/cc3m/VLDet/lvis_1203_cls_emb.pth' ZEROSHOT_WEIGHT_DIM: 1024 SHARE_PROJ_V_DIM: 1024 SHARE_PROJ_L_DIM: 1024 WEIGHTS: "models/swin_base_patch4_window7_224_22k.pkl" BACKBONE: NAME: build_swintransformer_fpn_backbone SWIN: SIZE: B-22k FPN: IN_FEATURES: ["swin1", "swin2", "swin3"] SOLVER: MAX_ITER: 180000 IMS_PER_BATCH: 32 BASE_LR: 0.0001 CHECKPOINT_PERIOD: 30000 INPUT: TRAIN_SIZE: 896 DATASETS: TRAIN: ("lvis_v1_train_norare",) ================================================ FILE: configs/BoxSup_OVCOCO_CLIP_R50_1x.yaml ================================================ _BASE_: "Base_OVCOCO_C4_1x.yaml" ================================================ FILE: configs/VLDet_LbaseCCcap_CLIP_R5021k_640b64_2x_ft4x_caption.yaml ================================================ _BASE_: "Base-C2_L_R5021k_640b64_4x.yaml" MODEL: WITH_CAPTION: True SYNC_CAPTION_BATCH: False ROI_BOX_HEAD: ADD_IMAGE_BOX: True # caption loss is added to the image-box USE_ZEROSHOT_CLS: True WS_NUM_PROPS: 32 USE_OT: 'contrastive' OT_LOSS_WEIGHT: 0.01 USE_CAPTION: True CAPTION_WEIGHT: 1.0 ZEROSHOT_WEIGHT_PATH: 'datasets/cc3m/VLDet/googlecc_nouns_6250_emb.pth' DETECTION_WEIGHT_PATH: 'datasets/cc3m/VLDet/lvis_1203_cls_emb.pth' ZEROSHOT_WEIGHT_DIM: 1024 SHARE_PROJ_V_DIM: 1024 SHARE_PROJ_L_DIM: 1024 WEIGHTS: "models/lvis_base.pth" SOLVER: MAX_ITER: 90000 CHECKPOINT_PERIOD: 10000 IMS_PER_BATCH: 64 BASE_LR: 0.0001 WARMUP_ITERS: 1000 WARMUP_FACTOR: 0.001 DATASETS: TRAIN: ("lvis_v1_train_norare", "cc3m_v1_nouns_train_6250tags") DATALOADER: SAMPLER_TRAIN: "MultiDatasetSampler" DATASET_RATIO: [1, 4] USE_DIFF_BS_SIZE: True DATASET_BS: [8, 16] DATASET_INPUT_SIZE: [640, 640] USE_RFS: [True, False] DATASET_INPUT_SCALE: [[0.1, 2.0], [0.5, 1.5]] FILTER_EMPTY_ANNOTATIONS: False MULTI_DATASET_GROUPING: True DATASET_ANN: ['box', 'caption'] NUM_WORKERS: 8 WITH_IMAGE_LABELS: True FP16: True OUTPUT_DIR: output/test TEST: EVAL_PERIOD: 10000 ================================================ FILE: configs/VLDet_LbaseI_CLIP_SwinB_896b32_2x_ft4x_caption.yaml ================================================ _BASE_: "Base-C2_L_R5021k_640b64_4x.yaml" MODEL: WITH_CAPTION: True ROI_BOX_HEAD: ADD_IMAGE_BOX: True USE_ZEROSHOT_CLS: True WS_NUM_PROPS: 5 USE_OT: 'contrastive' OT_LOSS_WEIGHT: 0.05 USE_CAPTION: True CAPTION_WEIGHT: 1.0 USE_ZEROSHOT_CLS: True ZEROSHOT_WEIGHT_PATH: 'datasets/cc3m/VLDet/googlecc_nouns_6250_emb.pth' DETECTION_WEIGHT_PATH: 'datasets/cc3m/VLDet/lvis_1203_cls_emb.pth' ZEROSHOT_WEIGHT_DIM: 1024 SHARE_PROJ_V_DIM: 1024 SHARE_PROJ_L_DIM: 1024 BACKBONE: NAME: build_swintransformer_fpn_backbone SWIN: SIZE: B-22k FPN: IN_FEATURES: ["swin1", "swin2", "swin3"] WEIGHTS: "models/lvis_base_swinB.pth" SOLVER: MAX_ITER: 90000 IMS_PER_BATCH: 32 BASE_LR: 0.0001 WARMUP_ITERS: 1000 WARMUP_FACTOR: 0.001 DATASETS: TRAIN: ("lvis_v1_train_norare", "cc3m_v1_nouns_train_6250tags") DATALOADER: SAMPLER_TRAIN: "MultiDatasetSampler" DATASET_RATIO: [1, 4] USE_DIFF_BS_SIZE: True DATASET_BS: [4, 16] DATASET_INPUT_SIZE: [896, 448] USE_RFS: [True, False] DATASET_INPUT_SCALE: [[0.1, 2.0], [0.5, 1.5]] FILTER_EMPTY_ANNOTATIONS: False MULTI_DATASET_GROUPING: True DATASET_ANN: ['box', 'caption'] NUM_WORKERS: 8 WITH_IMAGE_LABELS: True OUTPUT_DIR: output/lvis_swinB ================================================ FILE: configs/VLDet_OVCOCO_CLIP_R50_1x_caption.yaml ================================================ _BASE_: "Base_OVCOCO_C4_1x.yaml" MODEL: SHARE_PROJ_V_DIM: 2048 WEIGHTS: "models/coco_base.pth" WITH_CAPTION: True SYNC_CAPTION_BATCH: False SHARE_PROJ_L_DIM: 1024 ROI_HEADS: NUM_CLASSES: 65 ROI_BOX_HEAD: WS_NUM_PROPS: 32 ADD_IMAGE_BOX: True NEG_CAP_WEIGHT: 1.0 OT_LOSS_WEIGHT: 0.01 USE_CAPTION: True USE_OT: 'contrastive' ZEROSHOT_WEIGHT_PATH: 'datasets/coco/VLDet/coco_nouns_4764_emb.pth' DETECTION_WEIGHT_PATH: 'datasets/coco/VLDet/coco_65_cls_emb.pth' CAT_FREQ_PATH: 'datasets/coco/zero-shot/instances_train2017_seen_2_del_cat_info.json' ZEROSHOT_WEIGHT_DIM: 1024 CAPTION_WEIGHT: 1.0 SOLVER: IMS_PER_BATCH: 32 BASE_LR: 0.02 STEPS: (60000, 80000) CHECKPOINT_PERIOD: 10000 MAX_ITER: 90000 CLIP_GRADIENTS: ENABLED: True DATASETS: TRAIN: ("coco_zeroshot_train_del", "coco_caption_nouns_train_4764tags",) TEST: ("coco_generalized_del_val",) INPUT: CUSTOM_AUG: ResizeShortestEdge MIN_SIZE_TRAIN_SAMPLING: range MIN_SIZE_TRAIN: (800, 800) DATALOADER: SAMPLER_TRAIN: "MultiDatasetSampler" DATASET_RATIO: [1, 4] USE_DIFF_BS_SIZE: True DATASET_BS: [2, 8] USE_RFS: [False, False] DATASET_MIN_SIZES: [[800, 800], [400, 400]] DATASET_MAX_SIZES: [1333, 667] FILTER_EMPTY_ANNOTATIONS: False MULTI_DATASET_GROUPING: True DATASET_ANN: ['box', 'caption'] NUM_WORKERS: 8 WITH_IMAGE_LABELS: True OUTPUT_DIR: output/test FP16: True TEST: EVAL_PERIOD: 10000 ================================================ FILE: demo.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import argparse import glob import multiprocessing as mp import numpy as np import os import tempfile import time import warnings import cv2 import tqdm import sys import mss from detectron2.config import get_cfg from detectron2.data.detection_utils import read_image from detectron2.utils.logger import setup_logger sys.path.insert(0, 'CenterNet2/projects/CenterNet2/') from centernet.config import add_centernet_config from vldet.config import add_vldet_config from vldet.predictor import VisualizationDemo # Fake a video capture object OpenCV style - half width, half height of first screen using MSS class ScreenGrab: def __init__(self): self.sct = mss.mss() m0 = self.sct.monitors[0] self.monitor = {'top': 0, 'left': 0, 'width': m0['width'] / 2, 'height': m0['height'] / 2} def read(self): img = np.array(self.sct.grab(self.monitor)) nf = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR) return (True, nf) def isOpened(self): return True def release(self): return True # constants WINDOW_NAME = "Detic" def setup_cfg(args): cfg = get_cfg() if args.cpu: cfg.MODEL.DEVICE="cpu" add_centernet_config(cfg) add_vldet_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) # Set score_threshold for builtin models cfg.MODEL.RETINANET.SCORE_THRESH_TEST = args.confidence_threshold cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = args.confidence_threshold cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH = args.confidence_threshold cfg.MODEL.ROI_BOX_HEAD.ZEROSHOT_WEIGHT_PATH = 'rand' # load later if not args.pred_all_class: cfg.MODEL.ROI_HEADS.ONE_CLASS_PER_PROPOSAL = True cfg.freeze() return cfg def get_parser(): parser = argparse.ArgumentParser(description="Detectron2 demo for builtin configs") parser.add_argument( "--config-file", default="configs/quick_schedules/mask_rcnn_R_50_FPN_inference_acc_test.yaml", metavar="FILE", help="path to config file", ) parser.add_argument("--webcam", help="Take inputs from webcam.") parser.add_argument("--cpu", action='store_true', help="Use CPU only.") parser.add_argument("--video-input", help="Path to video file.") parser.add_argument( "--input", nargs="+", help="A list of space separated input images; " "or a single glob pattern such as 'directory/*.jpg'", ) parser.add_argument( "--output", help="A file or directory to save output visualizations. " "If not given, will show output in an OpenCV window.", ) parser.add_argument( "--vocabulary", default="lvis", choices=['lvis', 'openimages', 'objects365', 'coco', 'custom'], help="", ) parser.add_argument( "--custom_vocabulary", default="", help="", ) parser.add_argument("--pred_all_class", action='store_true') parser.add_argument( "--confidence-threshold", type=float, default=0.5, help="Minimum score for instance predictions to be shown", ) parser.add_argument( "--opts", help="Modify config options using the command-line 'KEY VALUE' pairs", default=[], nargs=argparse.REMAINDER, ) return parser def test_opencv_video_format(codec, file_ext): with tempfile.TemporaryDirectory(prefix="video_format_test") as dir: filename = os.path.join(dir, "test_file" + file_ext) writer = cv2.VideoWriter( filename=filename, fourcc=cv2.VideoWriter_fourcc(*codec), fps=float(30), frameSize=(10, 10), isColor=True, ) [writer.write(np.zeros((10, 10, 3), np.uint8)) for _ in range(30)] writer.release() if os.path.isfile(filename): return True return False if __name__ == "__main__": mp.set_start_method("spawn", force=True) args = get_parser().parse_args() setup_logger(name="fvcore") logger = setup_logger() logger.info("Arguments: " + str(args)) cfg = setup_cfg(args) demo = VisualizationDemo(cfg, args) if args.input: if len(args.input) == 1: args.input = glob.glob(os.path.expanduser(args.input[0])) assert args.input, "The input path(s) was not found" for path in tqdm.tqdm(args.input, disable=not args.output): img = read_image(path, format="BGR") start_time = time.time() predictions, visualized_output = demo.run_on_image(img) logger.info( "{}: {} in {:.2f}s".format( path, "detected {} instances".format(len(predictions["instances"])) if "instances" in predictions else "finished", time.time() - start_time, ) ) if args.output: if os.path.isdir(args.output): assert os.path.isdir(args.output), args.output out_filename = os.path.join(args.output, os.path.basename(path)) else: assert len(args.input) == 1, "Please specify a directory with args.output" out_filename = args.output visualized_output.save(out_filename) else: cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL) cv2.imshow(WINDOW_NAME, visualized_output.get_image()[:, :, ::-1]) if cv2.waitKey(0) == 27: break # esc to quit elif args.webcam: assert args.input is None, "Cannot have both --input and --webcam!" assert args.output is None, "output not yet supported with --webcam!" if args.webcam == "screen": cam = ScreenGrab() else: cam = cv2.VideoCapture(int(args.webcam)) for vis in tqdm.tqdm(demo.run_on_video(cam)): cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL) cv2.imshow(WINDOW_NAME, vis) if cv2.waitKey(1) == 27: break # esc to quit cam.release() cv2.destroyAllWindows() elif args.video_input: video = cv2.VideoCapture(args.video_input) width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)) frames_per_second = video.get(cv2.CAP_PROP_FPS) num_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT)) basename = os.path.basename(args.video_input) codec, file_ext = ( ("x264", ".mkv") if test_opencv_video_format("x264", ".mkv") else ("mp4v", ".mp4") ) if codec == ".mp4v": warnings.warn("x264 codec not available, switching to mp4v") if args.output: if os.path.isdir(args.output): output_fname = os.path.join(args.output, basename) output_fname = os.path.splitext(output_fname)[0] + file_ext else: output_fname = args.output assert not os.path.isfile(output_fname), output_fname output_file = cv2.VideoWriter( filename=output_fname, # some installation of opencv may not support x264 (due to its license), # you can try other format (e.g. MPEG) fourcc=cv2.VideoWriter_fourcc(*codec), fps=float(frames_per_second), frameSize=(width, height), isColor=True, ) assert os.path.isfile(args.video_input) for vis_frame in tqdm.tqdm(demo.run_on_video(video), total=num_frames): if args.output: output_file.write(vis_frame) else: cv2.namedWindow(basename, cv2.WINDOW_NORMAL) cv2.imshow(basename, vis_frame) if cv2.waitKey(1) == 27: break # esc to quit video.release() if args.output: output_file.release() else: cv2.destroyAllWindows() ================================================ FILE: detectron2/.circleci/config.yml ================================================ version: 2.1 # ------------------------------------------------------------------------------------- # Environments to run the jobs in # ------------------------------------------------------------------------------------- cpu: &cpu machine: image: ubuntu-2004:202107-02 resource_class: medium gpu: &gpu machine: # NOTE: use a cuda version that's supported by all our pytorch versions image: ubuntu-1604-cuda-11.1:202012-01 resource_class: gpu.nvidia.small windows-cpu: &windows_cpu machine: resource_class: windows.medium image: windows-server-2019-vs2019:stable shell: powershell.exe # windows-gpu: &windows_gpu # machine: # resource_class: windows.gpu.nvidia.medium # image: windows-server-2019-nvidia:stable version_parameters: &version_parameters parameters: pytorch_version: type: string torchvision_version: type: string pytorch_index: type: string # use test wheels index to have access to RC wheels # https://download.pytorch.org/whl/test/torch_test.html default: "https://download.pytorch.org/whl/torch_stable.html" python_version: # NOTE: only affect linux type: string default: '3.7.9' environment: PYTORCH_VERSION: << parameters.pytorch_version >> TORCHVISION_VERSION: << parameters.torchvision_version >> PYTORCH_INDEX: << parameters.pytorch_index >> PYTHON_VERSION: << parameters.python_version>> # point datasets to ~/.torch so it's cached in CI DETECTRON2_DATASETS: ~/.torch/datasets # ------------------------------------------------------------------------------------- # Re-usable commands # ------------------------------------------------------------------------------------- # install_nvidia_driver: &install_nvidia_driver # - run: # name: Install nvidia driver # working_directory: ~/ # command: | # wget -q 'https://s3.amazonaws.com/ossci-linux/nvidia_driver/NVIDIA-Linux-x86_64-430.40.run' # sudo /bin/bash ./NVIDIA-Linux-x86_64-430.40.run -s --no-drm # nvidia-smi add_ssh_keys: &add_ssh_keys # https://circleci.com/docs/2.0/add-ssh-key/ - add_ssh_keys: fingerprints: - "e4:13:f2:22:d4:49:e8:e4:57:5a:ac:20:2f:3f:1f:ca" install_python: &install_python - run: name: Install Python working_directory: ~/ command: | # upgrade pyenv cd /opt/circleci/.pyenv/plugins/python-build/../.. && git pull && cd - pyenv install -s $PYTHON_VERSION pyenv global $PYTHON_VERSION python --version which python pip install --upgrade pip setup_venv: &setup_venv - run: name: Setup Virtual Env working_directory: ~/ command: | python -m venv ~/venv echo ". ~/venv/bin/activate" >> $BASH_ENV . ~/venv/bin/activate python --version which python which pip pip install --upgrade pip setup_venv_win: &setup_venv_win - run: name: Setup Virtual Env for Windows command: | pip install virtualenv python -m virtualenv env .\env\Scripts\activate python --version which python which pip install_linux_dep: &install_linux_dep - run: name: Install Dependencies command: | # disable crash coredump, so unittests fail fast sudo systemctl stop apport.service # install from github to get latest; install iopath first since fvcore depends on it pip install --progress-bar off -U 'git+https://github.com/facebookresearch/iopath' pip install --progress-bar off -U 'git+https://github.com/facebookresearch/fvcore' # Don't use pytest-xdist: cuda tests are unstable under multi-process workers. pip install --progress-bar off ninja opencv-python-headless pytest tensorboard pycocotools onnx pip install --progress-bar off torch==$PYTORCH_VERSION -f $PYTORCH_INDEX if [[ "$TORCHVISION_VERSION" == "master" ]]; then pip install git+https://github.com/pytorch/vision.git else pip install --progress-bar off torchvision==$TORCHVISION_VERSION -f $PYTORCH_INDEX fi python -c 'import torch; print("CUDA:", torch.cuda.is_available())' gcc --version install_detectron2: &install_detectron2 - run: name: Install Detectron2 command: | # Remove first, in case it's in the CI cache pip uninstall -y detectron2 pip install --progress-bar off -e .[all] python -m detectron2.utils.collect_env ./datasets/prepare_for_tests.sh run_unittests: &run_unittests - run: name: Run Unit Tests command: | pytest -sv --durations=15 tests # parallel causes some random failures uninstall_tests: &uninstall_tests - run: name: Run Tests After Uninstalling command: | pip uninstall -y detectron2 # Remove built binaries rm -rf build/ detectron2/*.so # Tests that code is importable without installation PYTHONPATH=. ./.circleci/import-tests.sh # ------------------------------------------------------------------------------------- # Jobs to run # ------------------------------------------------------------------------------------- jobs: linux_cpu_tests: <<: *cpu <<: *version_parameters working_directory: ~/detectron2 steps: - checkout # Cache the venv directory that contains python, dependencies, and checkpoints # Refresh the key when dependencies should be updated (e.g. when pytorch releases) - restore_cache: keys: - cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210827 - <<: *install_python - <<: *install_linux_dep - <<: *install_detectron2 - <<: *run_unittests - <<: *uninstall_tests - save_cache: paths: - /opt/circleci/.pyenv - ~/.torch key: cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210827 linux_gpu_tests: <<: *gpu <<: *version_parameters working_directory: ~/detectron2 steps: - checkout - restore_cache: keys: - cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210827 - <<: *install_python - <<: *install_linux_dep - <<: *install_detectron2 - <<: *run_unittests - <<: *uninstall_tests - save_cache: paths: - /opt/circleci/.pyenv - ~/.torch key: cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210827 windows_cpu_build: <<: *windows_cpu <<: *version_parameters steps: - <<: *add_ssh_keys - checkout - <<: *setup_venv_win # Cache the env directory that contains dependencies - restore_cache: keys: - cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210404 - run: name: Install Dependencies command: | pip install certifi --ignore-installed # required on windows to workaround some cert issue pip install numpy cython # required on windows before pycocotools pip install opencv-python-headless pytest-xdist pycocotools tensorboard onnx pip install -U git+https://github.com/facebookresearch/iopath pip install -U git+https://github.com/facebookresearch/fvcore pip install torch==$env:PYTORCH_VERSION torchvision==$env:TORCHVISION_VERSION -f $env:PYTORCH_INDEX - save_cache: paths: - env key: cache-{{ arch }}-<< parameters.pytorch_version >>-{{ .Branch }}-20210404 - <<: *install_detectron2 # TODO: unittest fails for now workflows: version: 2 regular_test: jobs: - linux_cpu_tests: name: linux_cpu_tests_pytorch1.10 pytorch_version: '1.10.0+cpu' torchvision_version: '0.11.1+cpu' - linux_gpu_tests: name: linux_gpu_tests_pytorch1.8 pytorch_version: '1.8.1+cu111' torchvision_version: '0.9.1+cu111' - linux_gpu_tests: name: linux_gpu_tests_pytorch1.9 pytorch_version: '1.9+cu111' torchvision_version: '0.10+cu111' - linux_gpu_tests: name: linux_gpu_tests_pytorch1.10 pytorch_version: '1.10+cu111' torchvision_version: '0.11.1+cu111' - linux_gpu_tests: name: linux_gpu_tests_pytorch1.10_python39 pytorch_version: '1.10+cu111' torchvision_version: '0.11.1+cu111' python_version: '3.9.6' - windows_cpu_build: pytorch_version: '1.10+cpu' torchvision_version: '0.11.1+cpu' ================================================ FILE: detectron2/.circleci/import-tests.sh ================================================ #!/bin/bash -e # Copyright (c) Facebook, Inc. and its affiliates. # Test that import works without building detectron2. # Check that _C is not importable python -c "from detectron2 import _C" > /dev/null 2>&1 && { echo "This test should be run without building detectron2." exit 1 } # Check that other modules are still importable, even when _C is not importable python -c "from detectron2 import modeling" python -c "from detectron2 import modeling, data" python -c "from detectron2 import evaluation, export, checkpoint" python -c "from detectron2 import utils, engine" ================================================ FILE: detectron2/.clang-format ================================================ AccessModifierOffset: -1 AlignAfterOpenBracket: AlwaysBreak AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlinesLeft: true AlignOperands: false AlignTrailingComments: false AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: true BinPackArguments: false BinPackParameters: false BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false AfterFunction: false AfterNamespace: false AfterObjCDeclaration: false AfterStruct: false AfterUnion: false BeforeCatch: false BeforeElse: false IndentBraces: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Attach BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakAfterJavaFieldAnnotations: false BreakStringLiterals: false ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ForEachMacros: [ FOR_EACH, FOR_EACH_R, FOR_EACH_RANGE, ] IncludeCategories: - Regex: '^<.*\.h(pp)?>' Priority: 1 - Regex: '^<.*' Priority: 2 - Regex: '.*' Priority: 3 IndentCaseLabels: true IndentWidth: 2 IndentWrappedFunctionNames: false KeepEmptyLinesAtTheStartOfBlocks: false MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBlockIndentWidth: 2 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: false PenaltyBreakBeforeFirstCallParameter: 1 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Left ReflowComments: true SortIncludes: true SpaceAfterCStyleCast: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp11 TabWidth: 8 UseTab: Never ================================================ FILE: detectron2/.flake8 ================================================ # This is an example .flake8 config, used when developing *Black* itself. # Keep in sync with setup.cfg which is used for source packages. [flake8] ignore = W503, E203, E221, C901, C408, E741, C407, B017, F811, C101, EXE001, EXE002 max-line-length = 100 max-complexity = 18 select = B,C,E,F,W,T4,B9 exclude = build per-file-ignores = **/__init__.py:F401,F403,E402 **/configs/**.py:F401,E402 configs/**.py:F401,E402 **/tests/config/**.py:F401,E402 tests/config/**.py:F401,E402 ================================================ FILE: detectron2/GETTING_STARTED.md ================================================ ## Getting Started with Detectron2 This document provides a brief intro of the usage of builtin command-line tools in detectron2. For a tutorial that involves actual coding with the API, see our [Colab Notebook](https://colab.research.google.com/drive/16jcaJoc6bCFAQ96jDe2HwtXj7BMD_-m5) which covers how to run inference with an existing model, and how to train a builtin model on a custom dataset. ### Inference Demo with Pre-trained Models 1. Pick a model and its config file from [model zoo](MODEL_ZOO.md), for example, `mask_rcnn_R_50_FPN_3x.yaml`. 2. We provide `demo.py` that is able to demo builtin configs. Run it with: ``` cd demo/ python demo.py --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml \ --input input1.jpg input2.jpg \ [--other-options] --opts MODEL.WEIGHTS detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl ``` The configs are made for training, therefore we need to specify `MODEL.WEIGHTS` to a model from model zoo for evaluation. This command will run the inference and show visualizations in an OpenCV window. For details of the command line arguments, see `demo.py -h` or look at its source code to understand its behavior. Some common arguments are: * To run __on your webcam__, replace `--input files` with `--webcam`. * To run __on a video__, replace `--input files` with `--video-input video.mp4`. * To run __on cpu__, add `MODEL.DEVICE cpu` after `--opts`. * To save outputs to a directory (for images) or a file (for webcam or video), use `--output`. ### Training & Evaluation in Command Line We provide two scripts in "tools/plain_train_net.py" and "tools/train_net.py", that are made to train all the configs provided in detectron2. You may want to use it as a reference to write your own training script. Compared to "train_net.py", "plain_train_net.py" supports fewer default features. It also includes fewer abstraction, therefore is easier to add custom logic. To train a model with "train_net.py", first setup the corresponding datasets following [datasets/README.md](./datasets/README.md), then run: ``` cd tools/ ./train_net.py --num-gpus 8 \ --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml ``` The configs are made for 8-GPU training. To train on 1 GPU, you may need to [change some parameters](https://arxiv.org/abs/1706.02677), e.g.: ``` ./train_net.py \ --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml \ --num-gpus 1 SOLVER.IMS_PER_BATCH 2 SOLVER.BASE_LR 0.0025 ``` To evaluate a model's performance, use ``` ./train_net.py \ --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml \ --eval-only MODEL.WEIGHTS /path/to/checkpoint_file ``` For more options, see `./train_net.py -h`. ### Use Detectron2 APIs in Your Code See our [Colab Notebook](https://colab.research.google.com/drive/16jcaJoc6bCFAQ96jDe2HwtXj7BMD_-m5) to learn how to use detectron2 APIs to: 1. run inference with an existing model 2. train a builtin model on a custom dataset See [detectron2/projects](https://github.com/facebookresearch/detectron2/tree/main/projects) for more ways to build your project on detectron2. ================================================ FILE: detectron2/INSTALL.md ================================================ ## Installation ### Requirements - Linux or macOS with Python ≥ 3.7 - PyTorch ≥ 1.8 and [torchvision](https://github.com/pytorch/vision/) that matches the PyTorch installation. Install them together at [pytorch.org](https://pytorch.org) to make sure of this - OpenCV is optional but needed by demo and visualization ### Build Detectron2 from Source gcc & g++ ≥ 5.4 are required. [ninja](https://ninja-build.org/) is optional but recommended for faster build. After having them, run: ``` python -m pip install 'git+https://github.com/facebookresearch/detectron2.git' # (add --user if you don't have permission) # Or, to install it from a local clone: git clone https://github.com/facebookresearch/detectron2.git python -m pip install -e detectron2 # On macOS, you may need to prepend the above commands with a few environment variables: CC=clang CXX=clang++ ARCHFLAGS="-arch x86_64" python -m pip install ... ``` To __rebuild__ detectron2 that's built from a local clone, use `rm -rf build/ **/*.so` to clean the old build first. You often need to rebuild detectron2 after reinstalling PyTorch. ### Install Pre-Built Detectron2 (Linux only) Choose from this table to install [v0.6 (Oct 2021)](https://github.com/facebookresearch/detectron2/releases):
CUDA torch 1.10torch 1.9torch 1.8
11.3
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu113/torch1.10/index.html
11.1
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu111/torch1.10/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu111/torch1.9/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu111/torch1.8/index.html
10.2
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu102/torch1.10/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu102/torch1.9/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu102/torch1.8/index.html
10.1
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu101/torch1.8/index.html
cpu
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cpu/torch1.10/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cpu/torch1.9/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cpu/torch1.8/index.html
Note that: 1. The pre-built packages have to be used with corresponding version of CUDA and the official package of PyTorch. Otherwise, please build detectron2 from source. 2. New packages are released every few months. Therefore, packages may not contain latest features in the main branch and may not be compatible with the main branch of a research project that uses detectron2 (e.g. those in [projects](projects)). ### Common Installation Issues Click each issue for its solutions:

Undefined symbols that looks like "TH..","at::Tensor...","torch..."
This usually happens when detectron2 or torchvision is not compiled with the version of PyTorch you're running. If the error comes from a pre-built torchvision, uninstall torchvision and pytorch and reinstall them following [pytorch.org](http://pytorch.org). So the versions will match. If the error comes from a pre-built detectron2, check [release notes](https://github.com/facebookresearch/detectron2/releases), uninstall and reinstall the correct pre-built detectron2 that matches pytorch version. If the error comes from detectron2 or torchvision that you built manually from source, remove files you built (`build/`, `**/*.so`) and rebuild it so it can pick up the version of pytorch currently in your environment. If the above instructions do not resolve this problem, please provide an environment (e.g. a dockerfile) that can reproduce the issue.
Missing torch dynamic libraries, OR segmentation fault immediately when using detectron2. This usually happens when detectron2 or torchvision is not compiled with the version of PyTorch you're running. See the previous common issue for the solution.
Undefined C++ symbols (e.g. "GLIBCXX..") or C++ symbols not found.
Usually it's because the library is compiled with a newer C++ compiler but run with an old C++ runtime. This often happens with old anaconda. It may help to run `conda update libgcc` to upgrade its runtime. The fundamental solution is to avoid the mismatch, either by compiling using older version of C++ compiler, or run the code with proper C++ runtime. To run the code with a specific C++ runtime, you can use environment variable `LD_PRELOAD=/path/to/libstdc++.so`.
"nvcc not found" or "Not compiled with GPU support" or "Detectron2 CUDA Compiler: not available".
CUDA is not found when building detectron2. You should make sure ``` python -c 'import torch; from torch.utils.cpp_extension import CUDA_HOME; print(torch.cuda.is_available(), CUDA_HOME)' ``` print `(True, a directory with cuda)` at the time you build detectron2. Most models can run inference (but not training) without GPU support. To use CPUs, set `MODEL.DEVICE='cpu'` in the config.
"invalid device function" or "no kernel image is available for execution".
Two possibilities: * You build detectron2 with one version of CUDA but run it with a different version. To check whether it is the case, use `python -m detectron2.utils.collect_env` to find out inconsistent CUDA versions. In the output of this command, you should expect "Detectron2 CUDA Compiler", "CUDA_HOME", "PyTorch built with - CUDA" to contain cuda libraries of the same version. When they are inconsistent, you need to either install a different build of PyTorch (or build by yourself) to match your local CUDA installation, or install a different version of CUDA to match PyTorch. * PyTorch/torchvision/Detectron2 is not built for the correct GPU SM architecture (aka. compute capability). The architecture included by PyTorch/detectron2/torchvision is available in the "architecture flags" in `python -m detectron2.utils.collect_env`. It must include the architecture of your GPU, which can be found at [developer.nvidia.com/cuda-gpus](https://developer.nvidia.com/cuda-gpus). If you're using pre-built PyTorch/detectron2/torchvision, they have included support for most popular GPUs already. If not supported, you need to build them from source. When building detectron2/torchvision from source, they detect the GPU device and build for only the device. This means the compiled code may not work on a different GPU device. To recompile them for the correct architecture, remove all installed/compiled files, and rebuild them with the `TORCH_CUDA_ARCH_LIST` environment variable set properly. For example, `export TORCH_CUDA_ARCH_LIST="6.0;7.0"` makes it compile for both P100s and V100s.
Undefined CUDA symbols; Cannot open libcudart.so
The version of NVCC you use to build detectron2 or torchvision does not match the version of CUDA you are running with. This often happens when using anaconda's CUDA runtime. Use `python -m detectron2.utils.collect_env` to find out inconsistent CUDA versions. In the output of this command, you should expect "Detectron2 CUDA Compiler", "CUDA_HOME", "PyTorch built with - CUDA" to contain cuda libraries of the same version. When they are inconsistent, you need to either install a different build of PyTorch (or build by yourself) to match your local CUDA installation, or install a different version of CUDA to match PyTorch.
C++ compilation errors from NVCC / NVRTC, or "Unsupported gpu architecture"
A few possibilities: 1. Local CUDA/NVCC version has to match the CUDA version of your PyTorch. Both can be found in `python collect_env.py` (download from [here](./detectron2/utils/collect_env.py)). When they are inconsistent, you need to either install a different build of PyTorch (or build by yourself) to match your local CUDA installation, or install a different version of CUDA to match PyTorch. 2. Local CUDA/NVCC version shall support the SM architecture (a.k.a. compute capability) of your GPU. The capability of your GPU can be found at [developer.nvidia.com/cuda-gpus](https://developer.nvidia.com/cuda-gpus). The capability supported by NVCC is listed at [here](https://gist.github.com/ax3l/9489132). If your NVCC version is too old, this can be workaround by setting environment variable `TORCH_CUDA_ARCH_LIST` to a lower, supported capability. 3. The combination of NVCC and GCC you use is incompatible. You need to change one of their versions. See [here](https://gist.github.com/ax3l/9489132) for some valid combinations. Notably, CUDA<=10.1.105 doesn't support GCC>7.3. The CUDA/GCC version used by PyTorch can be found by `print(torch.__config__.show())`.
"ImportError: cannot import name '_C'".
Please build and install detectron2 following the instructions above. Or, if you are running code from detectron2's root directory, `cd` to a different one. Otherwise you may not import the code that you installed.
Any issue on windows.
Detectron2 is continuously built on windows with [CircleCI](https://app.circleci.com/pipelines/github/facebookresearch/detectron2?branch=main). However we do not provide official support for it. PRs that improves code compatibility on windows are welcome.
ONNX conversion segfault after some "TraceWarning".
The ONNX package is compiled with a too old compiler. Please build and install ONNX from its source code using a compiler whose version is closer to what's used by PyTorch (available in `torch.__config__.show()`).
"library not found for -lstdc++" on older version of MacOS
See [this stackoverflow answer](https://stackoverflow.com/questions/56083725/macos-build-issues-lstdc-not-found-while-building-python-package).
### Installation inside specific environments: * __Colab__: see our [Colab Tutorial](https://colab.research.google.com/drive/16jcaJoc6bCFAQ96jDe2HwtXj7BMD_-m5) which has step-by-step instructions. * __Docker__: The official [Dockerfile](docker) installs detectron2 with a few simple commands. ================================================ FILE: detectron2/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: detectron2/MODEL_ZOO.md ================================================ # Detectron2 Model Zoo and Baselines ## Introduction This file documents a large collection of baselines trained with detectron2 in Sep-Oct, 2019. All numbers were obtained on [Big Basin](https://engineering.fb.com/data-center-engineering/introducing-big-basin-our-next-generation-ai-hardware/) servers with 8 NVIDIA V100 GPUs & NVLink. The speed numbers are periodically updated with latest PyTorch/CUDA/cuDNN versions. You can access these models from code using [detectron2.model_zoo](https://detectron2.readthedocs.io/modules/model_zoo.html) APIs. In addition to these official baseline models, you can find more models in [projects/](projects/). #### How to Read the Tables * The "Name" column contains a link to the config file. Models can be reproduced using `tools/train_net.py` with the corresponding yaml config file, or `tools/lazyconfig_train_net.py` for python config files. * Training speed is averaged across the entire training. We keep updating the speed with latest version of detectron2/pytorch/etc., so they might be different from the `metrics` file. Training speed for multi-machine jobs is not provided. * Inference speed is measured by `tools/train_net.py --eval-only`, or [inference_on_dataset()](https://detectron2.readthedocs.io/modules/evaluation.html#detectron2.evaluation.inference_on_dataset), with batch size 1 in detectron2 directly. Measuring it with custom code may introduce other overhead. Actual deployment in production should in general be faster than the given inference speed due to more optimizations. * The *model id* column is provided for ease of reference. To check downloaded file integrity, any model on this page contains its md5 prefix in its file name. * Training curves and other statistics can be found in `metrics` for each model. #### Common Settings for COCO Models * All COCO models were trained on `train2017` and evaluated on `val2017`. * The default settings are __not directly comparable__ with Detectron's standard settings. For example, our default training data augmentation uses scale jittering in addition to horizontal flipping. To make fair comparisons with Detectron's settings, see [Detectron1-Comparisons](configs/Detectron1-Comparisons/) for accuracy comparison, and [benchmarks](https://detectron2.readthedocs.io/notes/benchmarks.html) for speed comparison. * For Faster/Mask R-CNN, we provide baselines based on __3 different backbone combinations__: * __FPN__: Use a ResNet+FPN backbone with standard conv and FC heads for mask and box prediction, respectively. It obtains the best speed/accuracy tradeoff, but the other two are still useful for research. * __C4__: Use a ResNet conv4 backbone with conv5 head. The original baseline in the Faster R-CNN paper. * __DC5__ (Dilated-C5): Use a ResNet conv5 backbone with dilations in conv5, and standard conv and FC heads for mask and box prediction, respectively. This is used by the Deformable ConvNet paper. * Most models are trained with the 3x schedule (~37 COCO epochs). Although 1x models are heavily under-trained, we provide some ResNet-50 models with the 1x (~12 COCO epochs) training schedule for comparison when doing quick research iteration. #### ImageNet Pretrained Models It's common to initialize from backbone models pre-trained on ImageNet classification tasks. The following backbone models are available: * [R-50.pkl](https://dl.fbaipublicfiles.com/detectron2/ImageNetPretrained/MSRA/R-50.pkl): converted copy of [MSRA's original ResNet-50](https://github.com/KaimingHe/deep-residual-networks) model. * [R-101.pkl](https://dl.fbaipublicfiles.com/detectron2/ImageNetPretrained/MSRA/R-101.pkl): converted copy of [MSRA's original ResNet-101](https://github.com/KaimingHe/deep-residual-networks) model. * [X-101-32x8d.pkl](https://dl.fbaipublicfiles.com/detectron2/ImageNetPretrained/FAIR/X-101-32x8d.pkl): ResNeXt-101-32x8d model trained with Caffe2 at FB. * [R-50.pkl (torchvision)](https://dl.fbaipublicfiles.com/detectron2/ImageNetPretrained/torchvision/R-50.pkl): converted copy of [torchvision's ResNet-50](https://pytorch.org/docs/stable/torchvision/models.html#torchvision.models.resnet50) model. More details can be found in [the conversion script](tools/convert-torchvision-to-d2.py). Note that the above models have __different__ format from those provided in Detectron: we do not fuse BatchNorm into an affine layer. Pretrained models in Detectron's format can still be used. For example: * [X-152-32x8d-IN5k.pkl](https://dl.fbaipublicfiles.com/detectron/ImageNetPretrained/25093814/X-152-32x8d-IN5k.pkl): ResNeXt-152-32x8d model trained on ImageNet-5k with Caffe2 at FB (see ResNeXt paper for details on ImageNet-5k). * [R-50-GN.pkl](https://dl.fbaipublicfiles.com/detectron/ImageNetPretrained/47261647/R-50-GN.pkl): ResNet-50 with Group Normalization. * [R-101-GN.pkl](https://dl.fbaipublicfiles.com/detectron/ImageNetPretrained/47592356/R-101-GN.pkl): ResNet-101 with Group Normalization. These models require slightly different settings regarding normalization and architecture. See the model zoo configs for reference. #### License All models available for download through this document are licensed under the [Creative Commons Attribution-ShareAlike 3.0 license](https://creativecommons.org/licenses/by-sa/3.0/). ### COCO Object Detection Baselines #### Faster R-CNN:
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
model id download
R50-C4 1x 0.551 0.102 4.8 35.7 137257644 model | metrics
R50-DC5 1x 0.380 0.068 5.0 37.3 137847829 model | metrics
R50-FPN 1x 0.210 0.038 3.0 37.9 137257794 model | metrics
R50-C4 3x 0.543 0.104 4.8 38.4 137849393 model | metrics
R50-DC5 3x 0.378 0.070 5.0 39.0 137849425 model | metrics
R50-FPN 3x 0.209 0.038 3.0 40.2 137849458 model | metrics
R101-C4 3x 0.619 0.139 5.9 41.1 138204752 model | metrics
R101-DC5 3x 0.452 0.086 6.1 40.6 138204841 model | metrics
R101-FPN 3x 0.286 0.051 4.1 42.0 137851257 model | metrics
X101-FPN 3x 0.638 0.098 6.7 43.0 139173657 model | metrics
#### RetinaNet:
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
model id download
R50 1x 0.205 0.041 4.1 37.4 190397773 model | metrics
R50 3x 0.205 0.041 4.1 38.7 190397829 model | metrics
R101 3x 0.291 0.054 5.2 40.4 190397697 model | metrics
#### RPN & Fast R-CNN:
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
prop.
AR
model id download
RPN R50-C4 1x 0.130 0.034 1.5 51.6 137258005 model | metrics
RPN R50-FPN 1x 0.186 0.032 2.7 58.0 137258492 model | metrics
Fast R-CNN R50-FPN 1x 0.140 0.029 2.6 37.8 137635226 model | metrics
### COCO Instance Segmentation Baselines with Mask R-CNN
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
model id download
R50-C4 1x 0.584 0.110 5.2 36.8 32.2 137259246 model | metrics
R50-DC5 1x 0.471 0.076 6.5 38.3 34.2 137260150 model | metrics
R50-FPN 1x 0.261 0.043 3.4 38.6 35.2 137260431 model | metrics
R50-C4 3x 0.575 0.111 5.2 39.8 34.4 137849525 model | metrics
R50-DC5 3x 0.470 0.076 6.5 40.0 35.9 137849551 model | metrics
R50-FPN 3x 0.261 0.043 3.4 41.0 37.2 137849600 model | metrics
R101-C4 3x 0.652 0.145 6.3 42.6 36.7 138363239 model | metrics
R101-DC5 3x 0.545 0.092 7.6 41.9 37.3 138363294 model | metrics
R101-FPN 3x 0.340 0.056 4.6 42.9 38.6 138205316 model | metrics
X101-FPN 3x 0.690 0.103 7.2 44.3 39.5 139653917 model | metrics
#### New baselines using Large-Scale Jitter and Longer Training Schedule The following baselines of COCO Instance Segmentation with Mask R-CNN are generated using a longer training schedule and large-scale jitter as described in Google's [Simple Copy-Paste Data Augmentation](https://arxiv.org/pdf/2012.07177.pdf) paper. These models are trained from scratch using random initialization. These baselines exceed the previous Mask R-CNN baselines. In the following table, one epoch consists of training on 118000 COCO images.
Name epochs train
time
(s/im)
inference
time
(s/im)
box
AP
mask
AP
model id download
R50-FPN 100 0.376 0.069 44.6 40.3 42047764 model | metrics
R50-FPN 200 0.376 0.069 46.3 41.7 42047638 model | metrics
R50-FPN 400 0.376 0.069 47.4 42.5 42019571 model | metrics
R101-FPN 100 0.518 0.073 46.4 41.6 42025812 model | metrics
R101-FPN 200 0.518 0.073 48.0 43.1 42131867 model | metrics
R101-FPN 400 0.518 0.073 48.9 43.7 42073830 model | metrics
regnetx_4gf_dds_FPN 100 0.474 0.071 46.0 41.3 42047771 model | metrics
regnetx_4gf_dds_FPN 200 0.474 0.071 48.1 43.1 42132721 model | metrics
regnetx_4gf_dds_FPN 400 0.474 0.071 48.6 43.5 42025447 model | metrics
regnety_4gf_dds_FPN 100 0.487 0.073 46.1 41.6 42047784 model | metrics
regnety_4gf_dds_FPN 200 0.487 0.072 47.8 43.0 42047642 model | metrics
regnety_4gf_dds_FPN 400 0.487 0.072 48.2 43.3 42045954 model | metrics
### COCO Person Keypoint Detection Baselines with Keypoint R-CNN
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
kp.
AP
model id download
R50-FPN 1x 0.315 0.072 5.0 53.6 64.0 137261548 model | metrics
R50-FPN 3x 0.316 0.066 5.0 55.4 65.5 137849621 model | metrics
R101-FPN 3x 0.390 0.076 6.1 56.4 66.1 138363331 model | metrics
X101-FPN 3x 0.738 0.121 8.7 57.3 66.0 139686956 model | metrics
### COCO Panoptic Segmentation Baselines with Panoptic FPN
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
PQ model id download
R50-FPN 1x 0.304 0.053 4.8 37.6 34.7 39.4 139514544 model | metrics
R50-FPN 3x 0.302 0.053 4.8 40.0 36.5 41.5 139514569 model | metrics
R101-FPN 3x 0.392 0.066 6.0 42.4 38.5 43.0 139514519 model | metrics
### LVIS Instance Segmentation Baselines with Mask R-CNN Mask R-CNN baselines on the [LVIS dataset](https://lvisdataset.org), v0.5. These baselines are described in Table 3(c) of the [LVIS paper](https://arxiv.org/abs/1908.03195). NOTE: the 1x schedule here has the same amount of __iterations__ as the COCO 1x baselines. They are roughly 24 epochs of LVISv0.5 data. The final results of these configs have large variance across different runs.
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
model id download
R50-FPN 1x 0.292 0.107 7.1 23.6 24.4 144219072 model | metrics
R101-FPN 1x 0.371 0.114 7.8 25.6 25.9 144219035 model | metrics
X101-FPN 1x 0.712 0.151 10.2 26.7 27.1 144219108 model | metrics
### Cityscapes & Pascal VOC Baselines Simple baselines for * Mask R-CNN on Cityscapes instance segmentation (initialized from COCO pre-training, then trained on Cityscapes fine annotations only) * Faster R-CNN on PASCAL VOC object detection (trained on VOC 2007 train+val + VOC 2012 train+val, tested on VOC 2007 using 11-point interpolated AP)
Name train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
box
AP50
mask
AP
model id download
R50-FPN, Cityscapes 0.240 0.078 4.4 36.5 142423278 model | metrics
R50-C4, VOC 0.537 0.081 4.8 51.9 80.3 142202221 model | metrics
### Other Settings Ablations for Deformable Conv and Cascade R-CNN:
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
model id download
Baseline R50-FPN 1x 0.261 0.043 3.4 38.6 35.2 137260431 model | metrics
Deformable Conv 1x 0.342 0.048 3.5 41.5 37.5 138602867 model | metrics
Cascade R-CNN 1x 0.317 0.052 4.0 42.1 36.4 138602847 model | metrics
Baseline R50-FPN 3x 0.261 0.043 3.4 41.0 37.2 137849600 model | metrics
Deformable Conv 3x 0.349 0.047 3.5 42.7 38.5 144998336 model | metrics
Cascade R-CNN 3x 0.328 0.053 4.0 44.3 38.5 144998488 model | metrics
Ablations for normalization methods, and a few models trained from scratch following [Rethinking ImageNet Pre-training](https://arxiv.org/abs/1811.08883). (Note: The baseline uses `2fc` head while the others use [`4conv1fc` head](https://arxiv.org/abs/1803.08494))
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
model id download
Baseline R50-FPN 3x 0.261 0.043 3.4 41.0 37.2 137849600 model | metrics
GN 3x 0.309 0.060 5.6 42.6 38.6 138602888 model | metrics
SyncBN 3x 0.345 0.053 5.5 41.9 37.8 169527823 model | metrics
GN (from scratch) 3x 0.338 0.061 7.2 39.9 36.6 138602908 model | metrics
GN (from scratch) 9x N/A 0.061 7.2 43.7 39.6 183808979 model | metrics
SyncBN (from scratch) 9x N/A 0.055 7.2 43.6 39.3 184226666 model | metrics
A few very large models trained for a long time, for demo purposes. They are trained using multiple machines:
Name inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
PQ model id download
Panoptic FPN R101 0.098 11.4 47.4 41.3 46.1 139797668 model | metrics
Mask R-CNN X152 0.234 15.1 50.2 44.0 18131413 model | metrics
above + test-time aug. 51.9 45.9
================================================ FILE: detectron2/README.md ================================================ Support Ukraine - Help Provide Humanitarian Aid to Ukraine. Detectron2 is Facebook AI Research's next generation library that provides state-of-the-art detection and segmentation algorithms. It is the successor of [Detectron](https://github.com/facebookresearch/Detectron/) and [maskrcnn-benchmark](https://github.com/facebookresearch/maskrcnn-benchmark/). It supports a number of computer vision research projects and production applications in Facebook.

## Learn More about Detectron2 Explain Like I’m 5: Detectron2 | Using Machine Learning with Detectron2 :-------------------------:|:-------------------------: [![Explain Like I’m 5: Detectron2](https://img.youtube.com/vi/1oq1Ye7dFqc/0.jpg)](https://www.youtube.com/watch?v=1oq1Ye7dFqc) | [![Using Machine Learning with Detectron2](https://img.youtube.com/vi/eUSgtfK4ivk/0.jpg)](https://www.youtube.com/watch?v=eUSgtfK4ivk) ## What's New * Includes new capabilities such as panoptic segmentation, Densepose, Cascade R-CNN, rotated bounding boxes, PointRend, DeepLab, ViTDet, MViTv2 etc. * Used as a library to support building [research projects](projects/) on top of it. * Models can be exported to TorchScript format or Caffe2 format for deployment. * It [trains much faster](https://detectron2.readthedocs.io/notes/benchmarks.html). See our [blog post](https://ai.facebook.com/blog/-detectron2-a-pytorch-based-modular-object-detection-library-/) to see more demos and learn about detectron2. ## Installation See [installation instructions](https://detectron2.readthedocs.io/tutorials/install.html). ## Getting Started See [Getting Started with Detectron2](https://detectron2.readthedocs.io/tutorials/getting_started.html), and the [Colab Notebook](https://colab.research.google.com/drive/16jcaJoc6bCFAQ96jDe2HwtXj7BMD_-m5) to learn about basic usage. Learn more at our [documentation](https://detectron2.readthedocs.org). And see [projects/](projects/) for some projects that are built on top of detectron2. ## Model Zoo and Baselines We provide a large set of baseline results and trained models available for download in the [Detectron2 Model Zoo](MODEL_ZOO.md). ## License Detectron2 is released under the [Apache 2.0 license](LICENSE). ## Citing Detectron2 If you use Detectron2 in your research or wish to refer to the baseline results published in the [Model Zoo](MODEL_ZOO.md), please use the following BibTeX entry. ```BibTeX @misc{wu2019detectron2, author = {Yuxin Wu and Alexander Kirillov and Francisco Massa and Wan-Yen Lo and Ross Girshick}, title = {Detectron2}, howpublished = {\url{https://github.com/facebookresearch/detectron2}}, year = {2019} } ``` ================================================ FILE: detectron2/configs/Base-RCNN-C4.yaml ================================================ MODEL: META_ARCHITECTURE: "GeneralizedRCNN" RPN: PRE_NMS_TOPK_TEST: 6000 POST_NMS_TOPK_TEST: 1000 ROI_HEADS: NAME: "Res5ROIHeads" DATASETS: TRAIN: ("coco_2017_train",) TEST: ("coco_2017_val",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.02 STEPS: (60000, 80000) MAX_ITER: 90000 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) VERSION: 2 ================================================ FILE: detectron2/configs/Base-RCNN-DilatedC5.yaml ================================================ MODEL: META_ARCHITECTURE: "GeneralizedRCNN" RESNETS: OUT_FEATURES: ["res5"] RES5_DILATION: 2 RPN: IN_FEATURES: ["res5"] PRE_NMS_TOPK_TEST: 6000 POST_NMS_TOPK_TEST: 1000 ROI_HEADS: NAME: "StandardROIHeads" IN_FEATURES: ["res5"] ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_FC: 2 POOLER_RESOLUTION: 7 ROI_MASK_HEAD: NAME: "MaskRCNNConvUpsampleHead" NUM_CONV: 4 POOLER_RESOLUTION: 14 DATASETS: TRAIN: ("coco_2017_train",) TEST: ("coco_2017_val",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.02 STEPS: (60000, 80000) MAX_ITER: 90000 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) VERSION: 2 ================================================ FILE: detectron2/configs/Base-RCNN-FPN.yaml ================================================ MODEL: META_ARCHITECTURE: "GeneralizedRCNN" BACKBONE: NAME: "build_resnet_fpn_backbone" RESNETS: OUT_FEATURES: ["res2", "res3", "res4", "res5"] FPN: IN_FEATURES: ["res2", "res3", "res4", "res5"] ANCHOR_GENERATOR: SIZES: [[32], [64], [128], [256], [512]] # One size for each in feature map ASPECT_RATIOS: [[0.5, 1.0, 2.0]] # Three aspect ratios (same for all in feature maps) RPN: IN_FEATURES: ["p2", "p3", "p4", "p5", "p6"] PRE_NMS_TOPK_TRAIN: 2000 # Per FPN level PRE_NMS_TOPK_TEST: 1000 # Per FPN level # Detectron1 uses 2000 proposals per-batch, # (See "modeling/rpn/rpn_outputs.py" for details of this legacy issue) # which is approximately 1000 proposals per-image since the default batch size for FPN is 2. POST_NMS_TOPK_TRAIN: 1000 POST_NMS_TOPK_TEST: 1000 ROI_HEADS: NAME: "StandardROIHeads" IN_FEATURES: ["p2", "p3", "p4", "p5"] ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_FC: 2 POOLER_RESOLUTION: 7 ROI_MASK_HEAD: NAME: "MaskRCNNConvUpsampleHead" NUM_CONV: 4 POOLER_RESOLUTION: 14 DATASETS: TRAIN: ("coco_2017_train",) TEST: ("coco_2017_val",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.02 STEPS: (60000, 80000) MAX_ITER: 90000 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) VERSION: 2 ================================================ FILE: detectron2/configs/Base-RetinaNet.yaml ================================================ MODEL: META_ARCHITECTURE: "RetinaNet" BACKBONE: NAME: "build_retinanet_resnet_fpn_backbone" RESNETS: OUT_FEATURES: ["res3", "res4", "res5"] ANCHOR_GENERATOR: SIZES: !!python/object/apply:eval ["[[x, x * 2**(1.0/3), x * 2**(2.0/3) ] for x in [32, 64, 128, 256, 512 ]]"] FPN: IN_FEATURES: ["res3", "res4", "res5"] RETINANET: IOU_THRESHOLDS: [0.4, 0.5] IOU_LABELS: [0, -1, 1] SMOOTH_L1_LOSS_BETA: 0.0 DATASETS: TRAIN: ("coco_2017_train",) TEST: ("coco_2017_val",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.01 # Note that RetinaNet uses a different default learning rate STEPS: (60000, 80000) MAX_ITER: 90000 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) VERSION: 2 ================================================ FILE: detectron2/configs/COCO-Detection/fast_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False LOAD_PROPOSALS: True RESNETS: DEPTH: 50 PROPOSAL_GENERATOR: NAME: "PrecomputedProposals" DATASETS: TRAIN: ("coco_2017_train",) PROPOSAL_FILES_TRAIN: ("detectron2://COCO-Detection/rpn_R_50_FPN_1x/137258492/coco_2017_train_box_proposals_21bc3a.pkl", ) TEST: ("coco_2017_val",) PROPOSAL_FILES_TEST: ("detectron2://COCO-Detection/rpn_R_50_FPN_1x/137258492/coco_2017_val_box_proposals_ee0dad.pkl", ) DATALOADER: # proposals are part of the dataset_dicts, and take a lot of RAM NUM_WORKERS: 2 ================================================ FILE: detectron2/configs/COCO-Detection/faster_rcnn_R_101_C4_3x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: False RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-Detection/faster_rcnn_R_101_DC5_3x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: False RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-Detection/faster_rcnn_R_101_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: False RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-Detection/faster_rcnn_R_50_C4_1x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 ================================================ FILE: detectron2/configs/COCO-Detection/faster_rcnn_R_50_C4_3x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-Detection/faster_rcnn_R_50_DC5_1x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 ================================================ FILE: detectron2/configs/COCO-Detection/faster_rcnn_R_50_DC5_3x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-Detection/faster_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 ================================================ FILE: detectron2/configs/COCO-Detection/faster_rcnn_R_50_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-Detection/faster_rcnn_X_101_32x8d_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: MASK_ON: False WEIGHTS: "detectron2://ImageNetPretrained/FAIR/X-101-32x8d.pkl" PIXEL_STD: [57.375, 57.120, 58.395] RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-Detection/fcos_R_50_FPN_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.fcos import model from ..common.train import train dataloader.train.mapper.use_instance_mask = False optimizer.lr = 0.01 model.backbone.bottom_up.freeze_at = 2 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/configs/COCO-Detection/retinanet_R_101_FPN_3x.yaml ================================================ _BASE_: "../Base-RetinaNet.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-Detection/retinanet_R_50_FPN_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.retinanet import model from ..common.train import train dataloader.train.mapper.use_instance_mask = False model.backbone.bottom_up.freeze_at = 2 optimizer.lr = 0.01 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/configs/COCO-Detection/retinanet_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RetinaNet.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ================================================ FILE: detectron2/configs/COCO-Detection/retinanet_R_50_FPN_3x.yaml ================================================ _BASE_: "../Base-RetinaNet.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-Detection/rpn_R_50_C4_1x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: META_ARCHITECTURE: "ProposalNetwork" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 RPN: PRE_NMS_TOPK_TEST: 12000 POST_NMS_TOPK_TEST: 2000 ================================================ FILE: detectron2/configs/COCO-Detection/rpn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "ProposalNetwork" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 RPN: POST_NMS_TOPK_TEST: 2000 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_101_C4_3x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: True RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_101_DC5_3x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: True RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_101_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: True RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_C4_1x.py ================================================ from ..common.train import train from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.mask_rcnn_c4 import model model.backbone.freeze_at = 2 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_C4_1x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_C4_3x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_1x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_3x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.mask_rcnn_fpn import model from ..common.train import train model.backbone.bottom_up.freeze_at = 2 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x_giou.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 RPN: BBOX_REG_LOSS_TYPE: "giou" BBOX_REG_LOSS_WEIGHT: 2.0 ROI_BOX_HEAD: BBOX_REG_LOSS_TYPE: "giou" BBOX_REG_LOSS_WEIGHT: 10.0 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_X_101_32x8d_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: MASK_ON: True WEIGHTS: "detectron2://ImageNetPretrained/FAIR/X-101-32x8d.pkl" PIXEL_STD: [57.375, 57.120, 58.395] RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_regnetx_4gf_dds_fpn_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.mask_rcnn_fpn import model from ..common.train import train from detectron2.config import LazyCall as L from detectron2.modeling.backbone import RegNet from detectron2.modeling.backbone.regnet import SimpleStem, ResBottleneckBlock # Replace default ResNet with RegNetX-4GF from the DDS paper. Config source: # https://github.com/facebookresearch/pycls/blob/2c152a6e5d913e898cca4f0a758f41e6b976714d/configs/dds_baselines/regnetx/RegNetX-4.0GF_dds_8gpu.yaml#L4-L9 # noqa model.backbone.bottom_up = L(RegNet)( stem_class=SimpleStem, stem_width=32, block_class=ResBottleneckBlock, depth=23, w_a=38.65, w_0=96, w_m=2.43, group_width=40, freeze_at=2, norm="FrozenBN", out_features=["s1", "s2", "s3", "s4"], ) model.pixel_std = [57.375, 57.120, 58.395] optimizer.weight_decay = 5e-5 train.init_checkpoint = ( "https://dl.fbaipublicfiles.com/pycls/dds_baselines/160906383/RegNetX-4.0GF_dds_8gpu.pyth" ) # RegNets benefit from enabling cudnn benchmark mode train.cudnn_benchmark = True ================================================ FILE: detectron2/configs/COCO-InstanceSegmentation/mask_rcnn_regnety_4gf_dds_fpn_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.mask_rcnn_fpn import model from ..common.train import train from detectron2.config import LazyCall as L from detectron2.modeling.backbone import RegNet from detectron2.modeling.backbone.regnet import SimpleStem, ResBottleneckBlock # Replace default ResNet with RegNetY-4GF from the DDS paper. Config source: # https://github.com/facebookresearch/pycls/blob/2c152a6e5d913e898cca4f0a758f41e6b976714d/configs/dds_baselines/regnety/RegNetY-4.0GF_dds_8gpu.yaml#L4-L10 # noqa model.backbone.bottom_up = L(RegNet)( stem_class=SimpleStem, stem_width=32, block_class=ResBottleneckBlock, depth=22, w_a=31.41, w_0=96, w_m=2.24, group_width=64, se_ratio=0.25, freeze_at=2, norm="FrozenBN", out_features=["s1", "s2", "s3", "s4"], ) model.pixel_std = [57.375, 57.120, 58.395] optimizer.weight_decay = 5e-5 train.init_checkpoint = ( "https://dl.fbaipublicfiles.com/pycls/dds_baselines/160906838/RegNetY-4.0GF_dds_8gpu.pyth" ) # RegNets benefit from enabling cudnn benchmark mode train.cudnn_benchmark = True ================================================ FILE: detectron2/configs/COCO-Keypoints/Base-Keypoint-RCNN-FPN.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: KEYPOINT_ON: True ROI_HEADS: NUM_CLASSES: 1 ROI_BOX_HEAD: SMOOTH_L1_BETA: 0.5 # Keypoint AP degrades (though box AP improves) when using plain L1 loss RPN: # Detectron1 uses 2000 proposals per-batch, but this option is per-image in detectron2. # 1000 proposals per-image is found to hurt box AP. # Therefore we increase it to 1500 per-image. POST_NMS_TOPK_TRAIN: 1500 DATASETS: TRAIN: ("keypoints_coco_2017_train",) TEST: ("keypoints_coco_2017_val",) ================================================ FILE: detectron2/configs/COCO-Keypoints/keypoint_rcnn_R_101_FPN_3x.yaml ================================================ _BASE_: "Base-Keypoint-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-Keypoints/keypoint_rcnn_R_50_FPN_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco_keypoint import dataloader from ..common.models.keypoint_rcnn_fpn import model from ..common.train import train model.backbone.bottom_up.freeze_at = 2 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/configs/COCO-Keypoints/keypoint_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "Base-Keypoint-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ================================================ FILE: detectron2/configs/COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml ================================================ _BASE_: "Base-Keypoint-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-Keypoints/keypoint_rcnn_X_101_32x8d_FPN_3x.yaml ================================================ _BASE_: "Base-Keypoint-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/FAIR/X-101-32x8d.pkl" PIXEL_STD: [57.375, 57.120, 58.395] RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-PanopticSegmentation/Base-Panoptic-FPN.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "PanopticFPN" MASK_ON: True SEM_SEG_HEAD: LOSS_WEIGHT: 0.5 DATASETS: TRAIN: ("coco_2017_train_panoptic_separated",) TEST: ("coco_2017_val_panoptic_separated",) DATALOADER: FILTER_EMPTY_ANNOTATIONS: False ================================================ FILE: detectron2/configs/COCO-PanopticSegmentation/panoptic_fpn_R_101_3x.yaml ================================================ _BASE_: "Base-Panoptic-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/COCO-PanopticSegmentation/panoptic_fpn_R_50_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco_panoptic_separated import dataloader from ..common.models.panoptic_fpn import model from ..common.train import train model.backbone.bottom_up.freeze_at = 2 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/configs/COCO-PanopticSegmentation/panoptic_fpn_R_50_1x.yaml ================================================ _BASE_: "Base-Panoptic-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ================================================ FILE: detectron2/configs/COCO-PanopticSegmentation/panoptic_fpn_R_50_3x.yaml ================================================ _BASE_: "Base-Panoptic-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/Cityscapes/mask_rcnn_R_50_FPN.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: # WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" # For better, more stable performance initialize from COCO WEIGHTS: "detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl" MASK_ON: True ROI_HEADS: NUM_CLASSES: 8 # This is similar to the setting used in Mask R-CNN paper, Appendix A # But there are some differences, e.g., we did not initialize the output # layer using the corresponding classes from COCO INPUT: MIN_SIZE_TRAIN: (800, 832, 864, 896, 928, 960, 992, 1024) MIN_SIZE_TRAIN_SAMPLING: "choice" MIN_SIZE_TEST: 1024 MAX_SIZE_TRAIN: 2048 MAX_SIZE_TEST: 2048 DATASETS: TRAIN: ("cityscapes_fine_instance_seg_train",) TEST: ("cityscapes_fine_instance_seg_val",) SOLVER: BASE_LR: 0.01 STEPS: (18000,) MAX_ITER: 24000 IMS_PER_BATCH: 8 TEST: EVAL_PERIOD: 8000 ================================================ FILE: detectron2/configs/Detectron1-Comparisons/README.md ================================================ Detectron2 model zoo's experimental settings and a few implementation details are different from Detectron. The differences in implementation details are shared in [Compatibility with Other Libraries](../../docs/notes/compatibility.md). The differences in model zoo's experimental settings include: * Use scale augmentation during training. This improves AP with lower training cost. * Use L1 loss instead of smooth L1 loss for simplicity. This sometimes improves box AP but may affect other AP. * Use `POOLER_SAMPLING_RATIO=0` instead of 2. This does not significantly affect AP. * Use `ROIAlignV2`. This does not significantly affect AP. In this directory, we provide a few configs that __do not__ have the above changes. They mimic Detectron's behavior as close as possible, and provide a fair comparison of accuracy and speed against Detectron.
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
kp.
AP
model id download
Faster R-CNN 1x 0.219 0.038 3.1 36.9 137781054 model | metrics
Keypoint R-CNN 1x 0.313 0.071 5.0 53.1 64.2 137781195 model | metrics
Mask R-CNN 1x 0.273 0.043 3.4 37.8 34.9 137781281 model | metrics
## Comparisons: * Faster R-CNN: Detectron's AP is 36.7, similar to ours. * Keypoint R-CNN: Detectron's AP is box 53.6, keypoint 64.2. Fixing a Detectron's [bug](https://github.com/facebookresearch/Detectron/issues/459) lead to a drop in box AP, and can be compensated back by some parameter tuning. * Mask R-CNN: Detectron's AP is box 37.7, mask 33.9. We're 1 AP better in mask AP, due to more correct implementation. See [this article](https://ppwwyyxx.com/blog/2021/Where-are-Pixels/) for details. For speed comparison, see [benchmarks](https://detectron2.readthedocs.io/notes/benchmarks.html). ================================================ FILE: detectron2/configs/Detectron1-Comparisons/faster_rcnn_R_50_FPN_noaug_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 # Detectron1 uses smooth L1 loss with some magic beta values. # The defaults are changed to L1 loss in Detectron2. RPN: SMOOTH_L1_BETA: 0.1111 ROI_BOX_HEAD: SMOOTH_L1_BETA: 1.0 POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" INPUT: # no scale augmentation MIN_SIZE_TRAIN: (800, ) ================================================ FILE: detectron2/configs/Detectron1-Comparisons/keypoint_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" KEYPOINT_ON: True RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 1 ROI_KEYPOINT_HEAD: POOLER_RESOLUTION: 14 POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" # Detectron1 uses smooth L1 loss with some magic beta values. # The defaults are changed to L1 loss in Detectron2. ROI_BOX_HEAD: SMOOTH_L1_BETA: 1.0 POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" RPN: SMOOTH_L1_BETA: 0.1111 # Detectron1 uses 2000 proposals per-batch, but this option is per-image in detectron2 # 1000 proposals per-image is found to hurt box AP. # Therefore we increase it to 1500 per-image. POST_NMS_TOPK_TRAIN: 1500 DATASETS: TRAIN: ("keypoints_coco_2017_train",) TEST: ("keypoints_coco_2017_val",) ================================================ FILE: detectron2/configs/Detectron1-Comparisons/mask_rcnn_R_50_FPN_noaug_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 # Detectron1 uses smooth L1 loss with some magic beta values. # The defaults are changed to L1 loss in Detectron2. RPN: SMOOTH_L1_BETA: 0.1111 ROI_BOX_HEAD: SMOOTH_L1_BETA: 1.0 POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" ROI_MASK_HEAD: POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" INPUT: # no scale augmentation MIN_SIZE_TRAIN: (800, ) ================================================ FILE: detectron2/configs/LVISv0.5-InstanceSegmentation/mask_rcnn_R_101_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: True RESNETS: DEPTH: 101 ROI_HEADS: NUM_CLASSES: 1230 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v0.5_train",) TEST: ("lvis_v0.5_val",) TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/configs/LVISv0.5-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 1230 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v0.5_train",) TEST: ("lvis_v0.5_val",) TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/configs/LVISv0.5-InstanceSegmentation/mask_rcnn_X_101_32x8d_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/FAIR/X-101-32x8d.pkl" PIXEL_STD: [57.375, 57.120, 58.395] MASK_ON: True RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 101 ROI_HEADS: NUM_CLASSES: 1230 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v0.5_train",) TEST: ("lvis_v0.5_val",) TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/configs/LVISv1-InstanceSegmentation/mask_rcnn_R_101_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: True RESNETS: DEPTH: 101 ROI_HEADS: NUM_CLASSES: 1203 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v1_train",) TEST: ("lvis_v1_val",) TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 SOLVER: STEPS: (120000, 160000) MAX_ITER: 180000 # 180000 * 16 / 100000 ~ 28.8 epochs DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/configs/LVISv1-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 1203 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v1_train",) TEST: ("lvis_v1_val",) TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 SOLVER: STEPS: (120000, 160000) MAX_ITER: 180000 # 180000 * 16 / 100000 ~ 28.8 epochs DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/configs/LVISv1-InstanceSegmentation/mask_rcnn_X_101_32x8d_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/FAIR/X-101-32x8d.pkl" PIXEL_STD: [57.375, 57.120, 58.395] MASK_ON: True RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 101 ROI_HEADS: NUM_CLASSES: 1203 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v1_train",) TEST: ("lvis_v1_val",) SOLVER: STEPS: (120000, 160000) MAX_ITER: 180000 # 180000 * 16 / 100000 ~ 28.8 epochs TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/configs/Misc/cascade_mask_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ROI_HEADS: NAME: CascadeROIHeads ROI_BOX_HEAD: CLS_AGNOSTIC_BBOX_REG: True RPN: POST_NMS_TOPK_TRAIN: 2000 ================================================ FILE: detectron2/configs/Misc/cascade_mask_rcnn_R_50_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ROI_HEADS: NAME: CascadeROIHeads ROI_BOX_HEAD: CLS_AGNOSTIC_BBOX_REG: True RPN: POST_NMS_TOPK_TRAIN: 2000 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/Misc/cascade_mask_rcnn_X_152_32x8d_FPN_IN5k_gn_dconv.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: MASK_ON: True WEIGHTS: "catalog://ImageNetPretrained/FAIR/X-152-32x8d-IN5k" RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 152 DEFORM_ON_PER_STAGE: [False, True, True, True] ROI_HEADS: NAME: "CascadeROIHeads" ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_CONV: 4 NUM_FC: 1 NORM: "GN" CLS_AGNOSTIC_BBOX_REG: True ROI_MASK_HEAD: NUM_CONV: 8 NORM: "GN" RPN: POST_NMS_TOPK_TRAIN: 2000 SOLVER: IMS_PER_BATCH: 128 STEPS: (35000, 45000) MAX_ITER: 50000 BASE_LR: 0.16 INPUT: MIN_SIZE_TRAIN: (640, 864) MIN_SIZE_TRAIN_SAMPLING: "range" MAX_SIZE_TRAIN: 1440 CROP: ENABLED: True TEST: EVAL_PERIOD: 2500 ================================================ FILE: detectron2/configs/Misc/mask_rcnn_R_50_FPN_1x_cls_agnostic.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ROI_BOX_HEAD: CLS_AGNOSTIC_BBOX_REG: True ROI_MASK_HEAD: CLS_AGNOSTIC_MASK: True ================================================ FILE: detectron2/configs/Misc/mask_rcnn_R_50_FPN_1x_dconv_c3-c5.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 DEFORM_ON_PER_STAGE: [False, True, True, True] # on Res3,Res4,Res5 DEFORM_MODULATED: False ================================================ FILE: detectron2/configs/Misc/mask_rcnn_R_50_FPN_3x_dconv_c3-c5.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 DEFORM_ON_PER_STAGE: [False, True, True, True] # on Res3,Res4,Res5 DEFORM_MODULATED: False SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/Misc/mask_rcnn_R_50_FPN_3x_gn.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "catalog://ImageNetPretrained/FAIR/R-50-GN" MASK_ON: True RESNETS: DEPTH: 50 NORM: "GN" STRIDE_IN_1X1: False FPN: NORM: "GN" ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_CONV: 4 NUM_FC: 1 NORM: "GN" ROI_MASK_HEAD: NORM: "GN" SOLVER: # 3x schedule STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/configs/Misc/mask_rcnn_R_50_FPN_3x_syncbn.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 NORM: "SyncBN" STRIDE_IN_1X1: True FPN: NORM: "SyncBN" ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_CONV: 4 NUM_FC: 1 NORM: "SyncBN" ROI_MASK_HEAD: NORM: "SyncBN" SOLVER: # 3x schedule STEPS: (210000, 250000) MAX_ITER: 270000 TEST: PRECISE_BN: ENABLED: True ================================================ FILE: detectron2/configs/Misc/mmdet_mask_rcnn_R_50_FPN_1x.py ================================================ # An example config to train a mmdetection model using detectron2. from ..common.data.coco import dataloader from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.optim import SGD as optimizer from ..common.train import train from ..common.data.constants import constants from detectron2.modeling.mmdet_wrapper import MMDetDetector from detectron2.config import LazyCall as L model = L(MMDetDetector)( detector=dict( type="MaskRCNN", pretrained="torchvision://resnet50", backbone=dict( type="ResNet", depth=50, num_stages=4, out_indices=(0, 1, 2, 3), frozen_stages=1, norm_cfg=dict(type="BN", requires_grad=True), norm_eval=True, style="pytorch", ), neck=dict(type="FPN", in_channels=[256, 512, 1024, 2048], out_channels=256, num_outs=5), rpn_head=dict( type="RPNHead", in_channels=256, feat_channels=256, anchor_generator=dict( type="AnchorGenerator", scales=[8], ratios=[0.5, 1.0, 2.0], strides=[4, 8, 16, 32, 64], ), bbox_coder=dict( type="DeltaXYWHBBoxCoder", target_means=[0.0, 0.0, 0.0, 0.0], target_stds=[1.0, 1.0, 1.0, 1.0], ), loss_cls=dict(type="CrossEntropyLoss", use_sigmoid=True, loss_weight=1.0), loss_bbox=dict(type="L1Loss", loss_weight=1.0), ), roi_head=dict( type="StandardRoIHead", bbox_roi_extractor=dict( type="SingleRoIExtractor", roi_layer=dict(type="RoIAlign", output_size=7, sampling_ratio=0), out_channels=256, featmap_strides=[4, 8, 16, 32], ), bbox_head=dict( type="Shared2FCBBoxHead", in_channels=256, fc_out_channels=1024, roi_feat_size=7, num_classes=80, bbox_coder=dict( type="DeltaXYWHBBoxCoder", target_means=[0.0, 0.0, 0.0, 0.0], target_stds=[0.1, 0.1, 0.2, 0.2], ), reg_class_agnostic=False, loss_cls=dict(type="CrossEntropyLoss", use_sigmoid=False, loss_weight=1.0), loss_bbox=dict(type="L1Loss", loss_weight=1.0), ), mask_roi_extractor=dict( type="SingleRoIExtractor", roi_layer=dict(type="RoIAlign", output_size=14, sampling_ratio=0), out_channels=256, featmap_strides=[4, 8, 16, 32], ), mask_head=dict( type="FCNMaskHead", num_convs=4, in_channels=256, conv_out_channels=256, num_classes=80, loss_mask=dict(type="CrossEntropyLoss", use_mask=True, loss_weight=1.0), ), ), # model training and testing settings train_cfg=dict( rpn=dict( assigner=dict( type="MaxIoUAssigner", pos_iou_thr=0.7, neg_iou_thr=0.3, min_pos_iou=0.3, match_low_quality=True, ignore_iof_thr=-1, ), sampler=dict( type="RandomSampler", num=256, pos_fraction=0.5, neg_pos_ub=-1, add_gt_as_proposals=False, ), allowed_border=-1, pos_weight=-1, debug=False, ), rpn_proposal=dict( nms_pre=2000, max_per_img=1000, nms=dict(type="nms", iou_threshold=0.7), min_bbox_size=0, ), rcnn=dict( assigner=dict( type="MaxIoUAssigner", pos_iou_thr=0.5, neg_iou_thr=0.5, min_pos_iou=0.5, match_low_quality=True, ignore_iof_thr=-1, ), sampler=dict( type="RandomSampler", num=512, pos_fraction=0.25, neg_pos_ub=-1, add_gt_as_proposals=True, ), mask_size=28, pos_weight=-1, debug=False, ), ), test_cfg=dict( rpn=dict( nms_pre=1000, max_per_img=1000, nms=dict(type="nms", iou_threshold=0.7), min_bbox_size=0, ), rcnn=dict( score_thr=0.05, nms=dict(type="nms", iou_threshold=0.5), max_per_img=100, mask_thr_binary=0.5, ), ), ), pixel_mean=constants.imagenet_rgb256_mean, pixel_std=constants.imagenet_rgb256_std, ) dataloader.train.mapper.image_format = "RGB" # torchvision pretrained model train.init_checkpoint = None # pretrained model is loaded inside backbone ================================================ FILE: detectron2/configs/Misc/panoptic_fpn_R_101_dconv_cascade_gn_3x.yaml ================================================ # A large PanopticFPN for demo purposes. # Use GN on backbone to support semantic seg. # Use Cascade + Deform Conv to improve localization. _BASE_: "../COCO-PanopticSegmentation/Base-Panoptic-FPN.yaml" MODEL: WEIGHTS: "catalog://ImageNetPretrained/FAIR/R-101-GN" RESNETS: DEPTH: 101 NORM: "GN" DEFORM_ON_PER_STAGE: [False, True, True, True] STRIDE_IN_1X1: False FPN: NORM: "GN" ROI_HEADS: NAME: CascadeROIHeads ROI_BOX_HEAD: CLS_AGNOSTIC_BBOX_REG: True ROI_MASK_HEAD: NORM: "GN" RPN: POST_NMS_TOPK_TRAIN: 2000 SOLVER: STEPS: (105000, 125000) MAX_ITER: 135000 IMS_PER_BATCH: 32 BASE_LR: 0.04 ================================================ FILE: detectron2/configs/Misc/scratch_mask_rcnn_R_50_FPN_3x_gn.yaml ================================================ _BASE_: "mask_rcnn_R_50_FPN_3x_gn.yaml" MODEL: # Train from random initialization. WEIGHTS: "" # It makes sense to divide by STD when training from scratch # But it seems to make no difference on the results and C2's models didn't do this. # So we keep things consistent with C2. # PIXEL_STD: [57.375, 57.12, 58.395] MASK_ON: True BACKBONE: FREEZE_AT: 0 # NOTE: Please refer to Rethinking ImageNet Pre-training https://arxiv.org/abs/1811.08883 # to learn what you need for training from scratch. ================================================ FILE: detectron2/configs/Misc/scratch_mask_rcnn_R_50_FPN_9x_gn.yaml ================================================ _BASE_: "mask_rcnn_R_50_FPN_3x_gn.yaml" MODEL: PIXEL_STD: [57.375, 57.12, 58.395] WEIGHTS: "" MASK_ON: True RESNETS: STRIDE_IN_1X1: False BACKBONE: FREEZE_AT: 0 SOLVER: # 9x schedule IMS_PER_BATCH: 64 # 4x the standard STEPS: (187500, 197500) # last 60/4==15k and last 20/4==5k MAX_ITER: 202500 # 90k * 9 / 4 BASE_LR: 0.08 TEST: EVAL_PERIOD: 2500 # NOTE: Please refer to Rethinking ImageNet Pre-training https://arxiv.org/abs/1811.08883 # to learn what you need for training from scratch. ================================================ FILE: detectron2/configs/Misc/scratch_mask_rcnn_R_50_FPN_9x_syncbn.yaml ================================================ _BASE_: "mask_rcnn_R_50_FPN_3x_syncbn.yaml" MODEL: PIXEL_STD: [57.375, 57.12, 58.395] WEIGHTS: "" MASK_ON: True RESNETS: STRIDE_IN_1X1: False BACKBONE: FREEZE_AT: 0 SOLVER: # 9x schedule IMS_PER_BATCH: 64 # 4x the standard STEPS: (187500, 197500) # last 60/4==15k and last 20/4==5k MAX_ITER: 202500 # 90k * 9 / 4 BASE_LR: 0.08 TEST: EVAL_PERIOD: 2500 # NOTE: Please refer to Rethinking ImageNet Pre-training https://arxiv.org/abs/1811.08883 # to learn what you need for training from scratch. ================================================ FILE: detectron2/configs/Misc/semantic_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "SemanticSegmentor" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 DATASETS: TRAIN: ("coco_2017_train_panoptic_stuffonly",) TEST: ("coco_2017_val_panoptic_stuffonly",) INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) ================================================ FILE: detectron2/configs/Misc/torchvision_imagenet_R_50.py ================================================ """ An example config file to train a ImageNet classifier with detectron2. Model and dataloader both come from torchvision. This shows how to use detectron2 as a general engine for any new models and tasks. To run, use the following command: python tools/lazyconfig_train_net.py --config-file configs/Misc/torchvision_imagenet_R_50.py \ --num-gpus 8 dataloader.train.dataset.root=/path/to/imagenet/ """ import torch from torch import nn from torch.nn import functional as F from omegaconf import OmegaConf import torchvision from torchvision.transforms import transforms as T from torchvision.models.resnet import ResNet, Bottleneck from fvcore.common.param_scheduler import MultiStepParamScheduler from detectron2.solver import WarmupParamScheduler from detectron2.solver.build import get_default_optimizer_params from detectron2.config import LazyCall as L from detectron2.model_zoo import get_config from detectron2.data.samplers import TrainingSampler, InferenceSampler from detectron2.evaluation import DatasetEvaluator from detectron2.utils import comm """ Note: Here we put reusable code (models, evaluation, data) together with configs just as a proof-of-concept, to easily demonstrate what's needed to train a ImageNet classifier in detectron2. Writing code in configs offers extreme flexibility but is often not a good engineering practice. In practice, you might want to put code in your project and import them instead. """ def build_data_loader(dataset, batch_size, num_workers, training=True): return torch.utils.data.DataLoader( dataset, sampler=(TrainingSampler if training else InferenceSampler)(len(dataset)), batch_size=batch_size, num_workers=num_workers, pin_memory=True, ) class ClassificationNet(nn.Module): def __init__(self, model: nn.Module): super().__init__() self.model = model @property def device(self): return list(self.model.parameters())[0].device def forward(self, inputs): image, label = inputs pred = self.model(image.to(self.device)) if self.training: label = label.to(self.device) return F.cross_entropy(pred, label) else: return pred class ClassificationAcc(DatasetEvaluator): def reset(self): self.corr = self.total = 0 def process(self, inputs, outputs): image, label = inputs self.corr += (outputs.argmax(dim=1).cpu() == label.cpu()).sum().item() self.total += len(label) def evaluate(self): all_corr_total = comm.all_gather([self.corr, self.total]) corr = sum(x[0] for x in all_corr_total) total = sum(x[1] for x in all_corr_total) return {"accuracy": corr / total} # --- End of code that could be in a project and be imported dataloader = OmegaConf.create() dataloader.train = L(build_data_loader)( dataset=L(torchvision.datasets.ImageNet)( root="/path/to/imagenet", split="train", transform=L(T.Compose)( transforms=[ L(T.RandomResizedCrop)(size=224), L(T.RandomHorizontalFlip)(), T.ToTensor(), L(T.Normalize)(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)), ] ), ), batch_size=256 // 8, num_workers=4, training=True, ) dataloader.test = L(build_data_loader)( dataset=L(torchvision.datasets.ImageNet)( root="${...train.dataset.root}", split="val", transform=L(T.Compose)( transforms=[ L(T.Resize)(size=256), L(T.CenterCrop)(size=224), T.ToTensor(), L(T.Normalize)(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)), ] ), ), batch_size=256 // 8, num_workers=4, training=False, ) dataloader.evaluator = L(ClassificationAcc)() model = L(ClassificationNet)( model=(ResNet)(block=Bottleneck, layers=[3, 4, 6, 3], zero_init_residual=True) ) optimizer = L(torch.optim.SGD)( params=L(get_default_optimizer_params)(), lr=0.1, momentum=0.9, weight_decay=1e-4, ) lr_multiplier = L(WarmupParamScheduler)( scheduler=L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01, 0.001], milestones=[30, 60, 90, 100] ), warmup_length=1 / 100, warmup_factor=0.1, ) train = get_config("common/train.py").train train.init_checkpoint = None train.max_iter = 100 * 1281167 // 256 ================================================ FILE: detectron2/configs/PascalVOC-Detection/faster_rcnn_R_50_C4.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 20 INPUT: MIN_SIZE_TRAIN: (480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800) MIN_SIZE_TEST: 800 DATASETS: TRAIN: ('voc_2007_trainval', 'voc_2012_trainval') TEST: ('voc_2007_test',) SOLVER: STEPS: (12000, 16000) MAX_ITER: 18000 # 17.4 epochs WARMUP_ITERS: 100 ================================================ FILE: detectron2/configs/PascalVOC-Detection/faster_rcnn_R_50_FPN.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 20 INPUT: MIN_SIZE_TRAIN: (480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800) MIN_SIZE_TEST: 800 DATASETS: TRAIN: ('voc_2007_trainval', 'voc_2012_trainval') TEST: ('voc_2007_test',) SOLVER: STEPS: (12000, 16000) MAX_ITER: 18000 # 17.4 epochs WARMUP_ITERS: 100 ================================================ FILE: detectron2/configs/common/README.md ================================================ This directory provides definitions for a few common models, dataloaders, scheduler, and optimizers that are often used in training. The definition of these objects are provided in the form of lazy instantiation: their arguments can be edited by users before constructing the objects. They can be imported, or loaded by `model_zoo.get_config` API in users' own configs. ================================================ FILE: detectron2/configs/common/coco_schedule.py ================================================ from fvcore.common.param_scheduler import MultiStepParamScheduler from detectron2.config import LazyCall as L from detectron2.solver import WarmupParamScheduler def default_X_scheduler(num_X): """ Returns the config for a default multi-step LR scheduler such as "1x", "3x", commonly referred to in papers, where every 1x has the total length of 1440k training images (~12 COCO epochs). LR is decayed twice at the end of training following the strategy defined in "Rethinking ImageNet Pretraining", Sec 4. Args: num_X: a positive real number Returns: DictConfig: configs that define the multiplier for LR during training """ # total number of iterations assuming 16 batch size, using 1440000/16=90000 total_steps_16bs = num_X * 90000 if num_X <= 2: scheduler = L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], # note that scheduler is scale-invariant. This is equivalent to # milestones=[6, 8, 9] milestones=[60000, 80000, 90000], ) else: scheduler = L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], milestones=[total_steps_16bs - 60000, total_steps_16bs - 20000, total_steps_16bs], ) return L(WarmupParamScheduler)( scheduler=scheduler, warmup_length=1000 / total_steps_16bs, warmup_method="linear", warmup_factor=0.001, ) lr_multiplier_1x = default_X_scheduler(1) lr_multiplier_2x = default_X_scheduler(2) lr_multiplier_3x = default_X_scheduler(3) lr_multiplier_6x = default_X_scheduler(6) lr_multiplier_9x = default_X_scheduler(9) ================================================ FILE: detectron2/configs/common/data/coco.py ================================================ from omegaconf import OmegaConf import detectron2.data.transforms as T from detectron2.config import LazyCall as L from detectron2.data import ( DatasetMapper, build_detection_test_loader, build_detection_train_loader, get_detection_dataset_dicts, ) from detectron2.evaluation import COCOEvaluator dataloader = OmegaConf.create() dataloader.train = L(build_detection_train_loader)( dataset=L(get_detection_dataset_dicts)(names="coco_2017_train"), mapper=L(DatasetMapper)( is_train=True, augmentations=[ L(T.ResizeShortestEdge)( short_edge_length=(640, 672, 704, 736, 768, 800), sample_style="choice", max_size=1333, ), L(T.RandomFlip)(horizontal=True), ], image_format="BGR", use_instance_mask=True, ), total_batch_size=16, num_workers=4, ) dataloader.test = L(build_detection_test_loader)( dataset=L(get_detection_dataset_dicts)(names="coco_2017_val", filter_empty=False), mapper=L(DatasetMapper)( is_train=False, augmentations=[ L(T.ResizeShortestEdge)(short_edge_length=800, max_size=1333), ], image_format="${...train.mapper.image_format}", ), num_workers=4, ) dataloader.evaluator = L(COCOEvaluator)( dataset_name="${..test.dataset.names}", ) ================================================ FILE: detectron2/configs/common/data/coco_keypoint.py ================================================ from detectron2.data.detection_utils import create_keypoint_hflip_indices from .coco import dataloader dataloader.train.dataset.min_keypoints = 1 dataloader.train.dataset.names = "keypoints_coco_2017_train" dataloader.test.dataset.names = "keypoints_coco_2017_val" dataloader.train.mapper.update( use_instance_mask=False, use_keypoint=True, keypoint_hflip_indices=create_keypoint_hflip_indices(dataloader.train.dataset.names), ) ================================================ FILE: detectron2/configs/common/data/coco_panoptic_separated.py ================================================ from detectron2.config import LazyCall as L from detectron2.evaluation import ( COCOEvaluator, COCOPanopticEvaluator, DatasetEvaluators, SemSegEvaluator, ) from .coco import dataloader dataloader.train.dataset.names = "coco_2017_train_panoptic_separated" dataloader.train.dataset.filter_empty = False dataloader.test.dataset.names = "coco_2017_val_panoptic_separated" dataloader.evaluator = [ L(COCOEvaluator)( dataset_name="${...test.dataset.names}", ), L(SemSegEvaluator)( dataset_name="${...test.dataset.names}", ), L(COCOPanopticEvaluator)( dataset_name="${...test.dataset.names}", ), ] ================================================ FILE: detectron2/configs/common/data/constants.py ================================================ constants = dict( imagenet_rgb256_mean=[123.675, 116.28, 103.53], imagenet_rgb256_std=[58.395, 57.12, 57.375], imagenet_bgr256_mean=[103.530, 116.280, 123.675], # When using pre-trained models in Detectron1 or any MSRA models, # std has been absorbed into its conv1 weights, so the std needs to be set 1. # Otherwise, you can use [57.375, 57.120, 58.395] (ImageNet std) imagenet_bgr256_std=[1.0, 1.0, 1.0], ) ================================================ FILE: detectron2/configs/common/optim.py ================================================ import torch from detectron2.config import LazyCall as L from detectron2.solver.build import get_default_optimizer_params SGD = L(torch.optim.SGD)( params=L(get_default_optimizer_params)( # params.model is meant to be set to the model object, before instantiating # the optimizer. weight_decay_norm=0.0 ), lr=0.02, momentum=0.9, weight_decay=1e-4, ) AdamW = L(torch.optim.AdamW)( params=L(get_default_optimizer_params)( # params.model is meant to be set to the model object, before instantiating # the optimizer. base_lr="${..lr}", weight_decay_norm=0.0, ), lr=1e-4, betas=(0.9, 0.999), weight_decay=0.1, ) ================================================ FILE: detectron2/configs/common/train.py ================================================ # Common training-related configs that are designed for "tools/lazyconfig_train_net.py" # You can use your own instead, together with your own train_net.py train = dict( output_dir="./output", init_checkpoint="", max_iter=90000, amp=dict(enabled=False), # options for Automatic Mixed Precision ddp=dict( # options for DistributedDataParallel broadcast_buffers=False, find_unused_parameters=False, fp16_compression=False, ), checkpointer=dict(period=5000, max_to_keep=100), # options for PeriodicCheckpointer eval_period=5000, log_period=20, device="cuda" # ... ) ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_R_101_FPN_100ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) model.backbone.bottom_up.stages.depth = 101 ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_R_101_FPN_200ep_LSJ.py ================================================ from .mask_rcnn_R_101_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 2 # 100ep -> 200ep lr_multiplier.scheduler.milestones = [ milestone * 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_R_101_FPN_400ep_LSJ.py ================================================ from .mask_rcnn_R_101_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 4 # 100ep -> 400ep lr_multiplier.scheduler.milestones = [ milestone * 4 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_R_50_FPN_100ep_LSJ.py ================================================ import detectron2.data.transforms as T from detectron2.config.lazy import LazyCall as L from detectron2.layers.batch_norm import NaiveSyncBatchNorm from detectron2.solver import WarmupParamScheduler from fvcore.common.param_scheduler import MultiStepParamScheduler from ..common.data.coco import dataloader from ..common.models.mask_rcnn_fpn import model from ..common.optim import SGD as optimizer from ..common.train import train # train from scratch train.init_checkpoint = "" train.amp.enabled = True train.ddp.fp16_compression = True model.backbone.bottom_up.freeze_at = 0 # SyncBN # fmt: off model.backbone.bottom_up.stem.norm = \ model.backbone.bottom_up.stages.norm = \ model.backbone.norm = "SyncBN" # Using NaiveSyncBatchNorm becase heads may have empty input. That is not supported by # torch.nn.SyncBatchNorm. We can remove this after # https://github.com/pytorch/pytorch/issues/36530 is fixed. model.roi_heads.box_head.conv_norm = \ model.roi_heads.mask_head.conv_norm = lambda c: NaiveSyncBatchNorm(c, stats_mode="N") # fmt: on # 2conv in RPN: # https://github.com/tensorflow/tpu/blob/b24729de804fdb751b06467d3dce0637fa652060/models/official/detection/modeling/architecture/heads.py#L95-L97 # noqa: E501, B950 model.proposal_generator.head.conv_dims = [-1, -1] # 4conv1fc box head model.roi_heads.box_head.conv_dims = [256, 256, 256, 256] model.roi_heads.box_head.fc_dims = [1024] # resize_and_crop_image in: # https://github.com/tensorflow/tpu/blob/b24729de804fdb751b06467d3dce0637fa652060/models/official/detection/utils/input_utils.py#L127 # noqa: E501, B950 image_size = 1024 dataloader.train.mapper.augmentations = [ L(T.ResizeScale)( min_scale=0.1, max_scale=2.0, target_height=image_size, target_width=image_size ), L(T.FixedSizeCrop)(crop_size=(image_size, image_size)), L(T.RandomFlip)(horizontal=True), ] # recompute boxes due to cropping dataloader.train.mapper.recompute_boxes = True # larger batch-size. dataloader.train.total_batch_size = 64 # Equivalent to 100 epochs. # 100 ep = 184375 iters * 64 images/iter / 118000 images/ep train.max_iter = 184375 lr_multiplier = L(WarmupParamScheduler)( scheduler=L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], milestones=[163889, 177546], num_updates=train.max_iter, ), warmup_length=500 / train.max_iter, warmup_factor=0.067, ) optimizer.lr = 0.1 optimizer.weight_decay = 4e-5 ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_R_50_FPN_200ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 2 # 100ep -> 200ep lr_multiplier.scheduler.milestones = [ milestone * 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_R_50_FPN_400ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 4 # 100ep -> 400ep lr_multiplier.scheduler.milestones = [ milestone * 4 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_R_50_FPN_50ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter //= 2 # 100ep -> 50ep lr_multiplier.scheduler.milestones = [ milestone // 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_regnetx_4gf_dds_FPN_100ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) from detectron2.config import LazyCall as L from detectron2.modeling.backbone import RegNet from detectron2.modeling.backbone.regnet import SimpleStem, ResBottleneckBlock # Config source: # https://github.com/facebookresearch/detectron2/blob/main/configs/COCO-InstanceSegmentation/mask_rcnn_regnetx_4gf_dds_fpn_1x.py # noqa model.backbone.bottom_up = L(RegNet)( stem_class=SimpleStem, stem_width=32, block_class=ResBottleneckBlock, depth=23, w_a=38.65, w_0=96, w_m=2.43, group_width=40, norm="SyncBN", out_features=["s1", "s2", "s3", "s4"], ) model.pixel_std = [57.375, 57.120, 58.395] # RegNets benefit from enabling cudnn benchmark mode train.cudnn_benchmark = True ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_regnetx_4gf_dds_FPN_200ep_LSJ.py ================================================ from .mask_rcnn_regnetx_4gf_dds_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 2 # 100ep -> 200ep lr_multiplier.scheduler.milestones = [ milestone * 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_regnetx_4gf_dds_FPN_400ep_LSJ.py ================================================ from .mask_rcnn_regnetx_4gf_dds_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 4 # 100ep -> 400ep lr_multiplier.scheduler.milestones = [ milestone * 4 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_regnety_4gf_dds_FPN_100ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) from detectron2.config import LazyCall as L from detectron2.modeling.backbone import RegNet from detectron2.modeling.backbone.regnet import SimpleStem, ResBottleneckBlock # Config source: # https://github.com/facebookresearch/detectron2/blob/main/configs/COCO-InstanceSegmentation/mask_rcnn_regnety_4gf_dds_fpn_1x.py # noqa model.backbone.bottom_up = L(RegNet)( stem_class=SimpleStem, stem_width=32, block_class=ResBottleneckBlock, depth=22, w_a=31.41, w_0=96, w_m=2.24, group_width=64, se_ratio=0.25, norm="SyncBN", out_features=["s1", "s2", "s3", "s4"], ) model.pixel_std = [57.375, 57.120, 58.395] # RegNets benefit from enabling cudnn benchmark mode train.cudnn_benchmark = True ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_regnety_4gf_dds_FPN_200ep_LSJ.py ================================================ from .mask_rcnn_regnety_4gf_dds_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 2 # 100ep -> 200ep lr_multiplier.scheduler.milestones = [ milestone * 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/configs/new_baselines/mask_rcnn_regnety_4gf_dds_FPN_400ep_LSJ.py ================================================ from .mask_rcnn_regnety_4gf_dds_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 4 # 100ep -> 400ep lr_multiplier.scheduler.milestones = [ milestone * 4 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/configs/quick_schedules/README.md ================================================ These are quick configs for performance or accuracy regression tracking purposes. * `*instance_test.yaml`: can train on 2 GPUs. They are used to test whether the training can successfully finish. They are not expected to produce reasonable training results. * `*inference_acc_test.yaml`: They should be run using `--eval-only`. They run inference using pre-trained models and verify the results are as expected. * `*training_acc_test.yaml`: They should be trained on 8 GPUs. They finish in about an hour and verify the training accuracy is within the normal range. ================================================ FILE: detectron2/configs/quick_schedules/cascade_mask_rcnn_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../Misc/cascade_mask_rcnn_R_50_FPN_3x.yaml" MODEL: WEIGHTS: "detectron2://Misc/cascade_mask_rcnn_R_50_FPN_3x/144998488/model_final_480dd8.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 50.18, 0.02], ["segm", "AP", 43.87, 0.02]] ================================================ FILE: detectron2/configs/quick_schedules/cascade_mask_rcnn_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../Misc/cascade_mask_rcnn_R_50_FPN_3x.yaml" DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/configs/quick_schedules/fast_rcnn_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../COCO-Detection/fast_rcnn_R_50_FPN_1x.yaml" MODEL: WEIGHTS: "detectron2://COCO-Detection/fast_rcnn_R_50_FPN_1x/137635226/model_final_e5f7ce.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 45.70, 0.02]] ================================================ FILE: detectron2/configs/quick_schedules/fast_rcnn_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../COCO-Detection/fast_rcnn_R_50_FPN_1x.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" DATASETS: TRAIN: ("coco_2017_val_100",) PROPOSAL_FILES_TRAIN: ("detectron2://COCO-Detection/rpn_R_50_FPN_1x/137258492/coco_2017_val_box_proposals_ee0dad.pkl", ) TEST: ("coco_2017_val_100",) PROPOSAL_FILES_TEST: ("detectron2://COCO-Detection/rpn_R_50_FPN_1x/137258492/coco_2017_val_box_proposals_ee0dad.pkl", ) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/configs/quick_schedules/keypoint_rcnn_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x/137849621/model_final_a6e10b.pkl" DATASETS: TEST: ("keypoints_coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 52.47, 0.02], ["keypoints", "AP", 67.36, 0.02]] ================================================ FILE: detectron2/configs/quick_schedules/keypoint_rcnn_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" KEYPOINT_ON: True ROI_HEADS: NUM_CLASSES: 1 DATASETS: TRAIN: ("keypoints_coco_2017_val_100",) TEST: ("keypoints_coco_2017_val_100",) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/configs/quick_schedules/keypoint_rcnn_R_50_FPN_normalized_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" KEYPOINT_ON: True RESNETS: DEPTH: 50 ROI_HEADS: BATCH_SIZE_PER_IMAGE: 256 NUM_CLASSES: 1 ROI_KEYPOINT_HEAD: POOLER_RESOLUTION: 14 POOLER_SAMPLING_RATIO: 2 NORMALIZE_LOSS_BY_VISIBLE_KEYPOINTS: False LOSS_WEIGHT: 4.0 ROI_BOX_HEAD: SMOOTH_L1_BETA: 1.0 # Keypoint AP degrades when using plain L1 loss RPN: SMOOTH_L1_BETA: 0.2 # Keypoint AP degrades when using plain L1 loss DATASETS: TRAIN: ("keypoints_coco_2017_val",) TEST: ("keypoints_coco_2017_val",) INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) SOLVER: WARMUP_FACTOR: 0.33333333 WARMUP_ITERS: 100 STEPS: (5500, 5800) MAX_ITER: 6000 TEST: EXPECTED_RESULTS: [["bbox", "AP", 55.35, 1.0], ["keypoints", "AP", 76.91, 1.0]] ================================================ FILE: detectron2/configs/quick_schedules/keypoint_rcnn_R_50_FPN_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" KEYPOINT_ON: True RESNETS: DEPTH: 50 ROI_HEADS: BATCH_SIZE_PER_IMAGE: 256 NUM_CLASSES: 1 ROI_KEYPOINT_HEAD: POOLER_RESOLUTION: 14 POOLER_SAMPLING_RATIO: 2 ROI_BOX_HEAD: SMOOTH_L1_BETA: 1.0 # Keypoint AP degrades when using plain L1 loss RPN: SMOOTH_L1_BETA: 0.2 # Keypoint AP degrades when using plain L1 loss DATASETS: TRAIN: ("keypoints_coco_2017_val",) TEST: ("keypoints_coco_2017_val",) INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) SOLVER: WARMUP_FACTOR: 0.33333333 WARMUP_ITERS: 100 STEPS: (5500, 5800) MAX_ITER: 6000 TEST: EXPECTED_RESULTS: [["bbox", "AP", 53.5, 1.0], ["keypoints", "AP", 72.4, 1.0]] ================================================ FILE: detectron2/configs/quick_schedules/mask_rcnn_R_50_C4_GCV_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: BASE_LR: 0.001 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 CLIP_GRADIENTS: ENABLED: True CLIP_TYPE: "value" CLIP_VALUE: 1.0 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/configs/quick_schedules/mask_rcnn_R_50_C4_inference_acc_test.yaml ================================================ _BASE_: "../COCO-InstanceSegmentation/mask_rcnn_R_50_C4_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_C4_3x/137849525/model_final_4ce675.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 47.37, 0.02], ["segm", "AP", 40.99, 0.02]] ================================================ FILE: detectron2/configs/quick_schedules/mask_rcnn_R_50_C4_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: BASE_LR: 0.001 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/configs/quick_schedules/mask_rcnn_R_50_C4_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ROI_HEADS: BATCH_SIZE_PER_IMAGE: 256 MASK_ON: True DATASETS: TRAIN: ("coco_2017_val",) TEST: ("coco_2017_val",) INPUT: MIN_SIZE_TRAIN: (600,) MAX_SIZE_TRAIN: 1000 MIN_SIZE_TEST: 800 MAX_SIZE_TEST: 1000 SOLVER: IMS_PER_BATCH: 8 # base uses 16 WARMUP_FACTOR: 0.33333 WARMUP_ITERS: 100 STEPS: (11000, 11600) MAX_ITER: 12000 TEST: EXPECTED_RESULTS: [["bbox", "AP", 41.88, 0.7], ["segm", "AP", 33.79, 0.5]] ================================================ FILE: detectron2/configs/quick_schedules/mask_rcnn_R_50_DC5_inference_acc_test.yaml ================================================ _BASE_: "../COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_3x/137849551/model_final_84107b.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 47.44, 0.02], ["segm", "AP", 42.94, 0.02]] ================================================ FILE: detectron2/configs/quick_schedules/mask_rcnn_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 47.34, 0.02], ["segm", "AP", 42.67, 0.02], ["bbox_TTA", "AP", 49.11, 0.02], ["segm_TTA", "AP", 45.04, 0.02]] AUG: ENABLED: True MIN_SIZES: (700, 800) # to save some time ================================================ FILE: detectron2/configs/quick_schedules/mask_rcnn_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/configs/quick_schedules/mask_rcnn_R_50_FPN_pred_boxes_training_acc_test.yaml ================================================ _BASE_: "./mask_rcnn_R_50_FPN_training_acc_test.yaml" MODEL: ROI_BOX_HEAD: TRAIN_ON_PRED_BOXES: True TEST: EXPECTED_RESULTS: [["bbox", "AP", 42.6, 1.0], ["segm", "AP", 35.8, 0.8]] ================================================ FILE: detectron2/configs/quick_schedules/mask_rcnn_R_50_FPN_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ROI_HEADS: BATCH_SIZE_PER_IMAGE: 256 MASK_ON: True DATASETS: TRAIN: ("coco_2017_val",) TEST: ("coco_2017_val",) INPUT: MIN_SIZE_TRAIN: (600,) MAX_SIZE_TRAIN: 1000 MIN_SIZE_TEST: 800 MAX_SIZE_TEST: 1000 SOLVER: WARMUP_FACTOR: 0.3333333 WARMUP_ITERS: 100 STEPS: (5500, 5800) MAX_ITER: 6000 TEST: EXPECTED_RESULTS: [["bbox", "AP", 42.5, 1.0], ["segm", "AP", 35.8, 0.8]] ================================================ FILE: detectron2/configs/quick_schedules/panoptic_fpn_R_50_inference_acc_test.yaml ================================================ _BASE_: "../COCO-PanopticSegmentation/panoptic_fpn_R_50_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-PanopticSegmentation/panoptic_fpn_R_50_3x/139514569/model_final_c10459.pkl" DATASETS: TEST: ("coco_2017_val_100_panoptic_separated",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 46.47, 0.02], ["segm", "AP", 43.39, 0.02], ["sem_seg", "mIoU", 42.55, 0.02], ["panoptic_seg", "PQ", 38.99, 0.02]] ================================================ FILE: detectron2/configs/quick_schedules/panoptic_fpn_R_50_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "PanopticFPN" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 SEM_SEG_HEAD: LOSS_WEIGHT: 0.5 DATASETS: TRAIN: ("coco_2017_val_100_panoptic_separated",) TEST: ("coco_2017_val_100_panoptic_separated",) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 1 ================================================ FILE: detectron2/configs/quick_schedules/panoptic_fpn_R_50_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "PanopticFPN" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 SEM_SEG_HEAD: LOSS_WEIGHT: 0.5 DATASETS: TRAIN: ("coco_2017_val_panoptic_separated",) TEST: ("coco_2017_val_panoptic_separated",) SOLVER: BASE_LR: 0.01 WARMUP_FACTOR: 0.001 WARMUP_ITERS: 500 STEPS: (5500,) MAX_ITER: 7000 TEST: EXPECTED_RESULTS: [["bbox", "AP", 46.70, 1.1], ["segm", "AP", 39.0, 0.7], ["sem_seg", "mIoU", 64.73, 1.3], ["panoptic_seg", "PQ", 48.13, 0.8]] ================================================ FILE: detectron2/configs/quick_schedules/retinanet_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../COCO-Detection/retinanet_R_50_FPN_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-Detection/retinanet_R_50_FPN_3x/190397829/model_final_5bd44e.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 44.45, 0.02]] ================================================ FILE: detectron2/configs/quick_schedules/retinanet_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../COCO-Detection/retinanet_R_50_FPN_1x.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/configs/quick_schedules/rpn_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../COCO-Detection/rpn_R_50_FPN_1x.yaml" MODEL: WEIGHTS: "detectron2://COCO-Detection/rpn_R_50_FPN_1x/137258492/model_final_02ce48.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["box_proposals", "AR@1000", 58.16, 0.02]] ================================================ FILE: detectron2/configs/quick_schedules/rpn_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../COCO-Detection/rpn_R_50_FPN_1x.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: STEPS: (30,) MAX_ITER: 40 BASE_LR: 0.005 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/configs/quick_schedules/semantic_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "SemanticSegmentor" WEIGHTS: "detectron2://semantic_R_50_FPN_1x/111802073/model_final_c18079783c55a94968edc28b7101c5f0.pkl" RESNETS: DEPTH: 50 DATASETS: TEST: ("coco_2017_val_100_panoptic_stuffonly",) TEST: EXPECTED_RESULTS: [["sem_seg", "mIoU", 39.53, 0.02], ["sem_seg", "mACC", 51.50, 0.02]] ================================================ FILE: detectron2/configs/quick_schedules/semantic_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "SemanticSegmentor" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 DATASETS: TRAIN: ("coco_2017_val_100_panoptic_stuffonly",) TEST: ("coco_2017_val_100_panoptic_stuffonly",) INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/configs/quick_schedules/semantic_R_50_FPN_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "SemanticSegmentor" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 DATASETS: TRAIN: ("coco_2017_val_panoptic_stuffonly",) TEST: ("coco_2017_val_panoptic_stuffonly",) SOLVER: BASE_LR: 0.01 WARMUP_FACTOR: 0.001 WARMUP_ITERS: 300 STEPS: (5500,) MAX_ITER: 7000 TEST: EXPECTED_RESULTS: [["sem_seg", "mIoU", 76.51, 1.0], ["sem_seg", "mACC", 83.25, 1.0]] INPUT: # no scale augmentation MIN_SIZE_TRAIN: (800, ) ================================================ FILE: detectron2/datasets/README.md ================================================ # Use Builtin Datasets A dataset can be used by accessing [DatasetCatalog](https://detectron2.readthedocs.io/modules/data.html#detectron2.data.DatasetCatalog) for its data, or [MetadataCatalog](https://detectron2.readthedocs.io/modules/data.html#detectron2.data.MetadataCatalog) for its metadata (class names, etc). This document explains how to setup the builtin datasets so they can be used by the above APIs. [Use Custom Datasets](https://detectron2.readthedocs.io/tutorials/datasets.html) gives a deeper dive on how to use `DatasetCatalog` and `MetadataCatalog`, and how to add new datasets to them. Detectron2 has builtin support for a few datasets. The datasets are assumed to exist in a directory specified by the environment variable `DETECTRON2_DATASETS`. Under this directory, detectron2 will look for datasets in the structure described below, if needed. ``` $DETECTRON2_DATASETS/ coco/ lvis/ cityscapes/ VOC20{07,12}/ ``` You can set the location for builtin datasets by `export DETECTRON2_DATASETS=/path/to/datasets`. If left unset, the default is `./datasets` relative to your current working directory. The [model zoo](https://github.com/facebookresearch/detectron2/blob/master/MODEL_ZOO.md) contains configs and models that use these builtin datasets. ## Expected dataset structure for [COCO instance/keypoint detection](https://cocodataset.org/#download): ``` coco/ annotations/ instances_{train,val}2017.json person_keypoints_{train,val}2017.json {train,val}2017/ # image files that are mentioned in the corresponding json ``` You can use the 2014 version of the dataset as well. Some of the builtin tests (`dev/run_*_tests.sh`) uses a tiny version of the COCO dataset, which you can download with `./datasets/prepare_for_tests.sh`. ## Expected dataset structure for PanopticFPN: Extract panoptic annotations from [COCO website](https://cocodataset.org/#download) into the following structure: ``` coco/ annotations/ panoptic_{train,val}2017.json panoptic_{train,val}2017/ # png annotations panoptic_stuff_{train,val}2017/ # generated by the script mentioned below ``` Install panopticapi by: ``` pip install git+https://github.com/cocodataset/panopticapi.git ``` Then, run `python datasets/prepare_panoptic_fpn.py`, to extract semantic annotations from panoptic annotations. ## Expected dataset structure for [LVIS instance segmentation](https://www.lvisdataset.org/dataset): ``` coco/ {train,val,test}2017/ lvis/ lvis_v0.5_{train,val}.json lvis_v0.5_image_info_test.json lvis_v1_{train,val}.json lvis_v1_image_info_test{,_challenge}.json ``` Install lvis-api by: ``` pip install git+https://github.com/lvis-dataset/lvis-api.git ``` To evaluate models trained on the COCO dataset using LVIS annotations, run `python datasets/prepare_cocofied_lvis.py` to prepare "cocofied" LVIS annotations. ## Expected dataset structure for [cityscapes](https://www.cityscapes-dataset.com/downloads/): ``` cityscapes/ gtFine/ train/ aachen/ color.png, instanceIds.png, labelIds.png, polygons.json, labelTrainIds.png ... val/ test/ # below are generated Cityscapes panoptic annotation cityscapes_panoptic_train.json cityscapes_panoptic_train/ cityscapes_panoptic_val.json cityscapes_panoptic_val/ cityscapes_panoptic_test.json cityscapes_panoptic_test/ leftImg8bit/ train/ val/ test/ ``` Install cityscapes scripts by: ``` pip install git+https://github.com/mcordts/cityscapesScripts.git ``` Note: to create labelTrainIds.png, first prepare the above structure, then run cityscapesescript with: ``` CITYSCAPES_DATASET=/path/to/abovementioned/cityscapes python cityscapesscripts/preparation/createTrainIdLabelImgs.py ``` These files are not needed for instance segmentation. Note: to generate Cityscapes panoptic dataset, run cityscapesescript with: ``` CITYSCAPES_DATASET=/path/to/abovementioned/cityscapes python cityscapesscripts/preparation/createPanopticImgs.py ``` These files are not needed for semantic and instance segmentation. ## Expected dataset structure for [Pascal VOC](http://host.robots.ox.ac.uk/pascal/VOC/index.html): ``` VOC20{07,12}/ Annotations/ ImageSets/ Main/ trainval.txt test.txt # train.txt or val.txt, if you use these splits JPEGImages/ ``` ## Expected dataset structure for [ADE20k Scene Parsing](http://sceneparsing.csail.mit.edu/): ``` ADEChallengeData2016/ annotations/ annotations_detectron2/ images/ objectInfo150.txt ``` The directory `annotations_detectron2` is generated by running `python datasets/prepare_ade20k_sem_seg.py`. ================================================ FILE: detectron2/datasets/prepare_ade20k_sem_seg.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import os from pathlib import Path import tqdm from PIL import Image def convert(input, output): img = np.asarray(Image.open(input)) assert img.dtype == np.uint8 img = img - 1 # 0 (ignore) becomes 255. others are shifted by 1 Image.fromarray(img).save(output) if __name__ == "__main__": dataset_dir = Path(os.getenv("DETECTRON2_DATASETS", "datasets")) / "ADEChallengeData2016" for name in ["training", "validation"]: annotation_dir = dataset_dir / "annotations" / name output_dir = dataset_dir / "annotations_detectron2" / name output_dir.mkdir(parents=True, exist_ok=True) for file in tqdm.tqdm(list(annotation_dir.iterdir())): output_file = output_dir / file.name convert(file, output_file) ================================================ FILE: detectron2/datasets/prepare_cocofied_lvis.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import copy import json import os from collections import defaultdict # This mapping is extracted from the official LVIS mapping: # https://github.com/lvis-dataset/lvis-api/blob/master/data/coco_to_synset.json COCO_SYNSET_CATEGORIES = [ {"synset": "person.n.01", "coco_cat_id": 1}, {"synset": "bicycle.n.01", "coco_cat_id": 2}, {"synset": "car.n.01", "coco_cat_id": 3}, {"synset": "motorcycle.n.01", "coco_cat_id": 4}, {"synset": "airplane.n.01", "coco_cat_id": 5}, {"synset": "bus.n.01", "coco_cat_id": 6}, {"synset": "train.n.01", "coco_cat_id": 7}, {"synset": "truck.n.01", "coco_cat_id": 8}, {"synset": "boat.n.01", "coco_cat_id": 9}, {"synset": "traffic_light.n.01", "coco_cat_id": 10}, {"synset": "fireplug.n.01", "coco_cat_id": 11}, {"synset": "stop_sign.n.01", "coco_cat_id": 13}, {"synset": "parking_meter.n.01", "coco_cat_id": 14}, {"synset": "bench.n.01", "coco_cat_id": 15}, {"synset": "bird.n.01", "coco_cat_id": 16}, {"synset": "cat.n.01", "coco_cat_id": 17}, {"synset": "dog.n.01", "coco_cat_id": 18}, {"synset": "horse.n.01", "coco_cat_id": 19}, {"synset": "sheep.n.01", "coco_cat_id": 20}, {"synset": "beef.n.01", "coco_cat_id": 21}, {"synset": "elephant.n.01", "coco_cat_id": 22}, {"synset": "bear.n.01", "coco_cat_id": 23}, {"synset": "zebra.n.01", "coco_cat_id": 24}, {"synset": "giraffe.n.01", "coco_cat_id": 25}, {"synset": "backpack.n.01", "coco_cat_id": 27}, {"synset": "umbrella.n.01", "coco_cat_id": 28}, {"synset": "bag.n.04", "coco_cat_id": 31}, {"synset": "necktie.n.01", "coco_cat_id": 32}, {"synset": "bag.n.06", "coco_cat_id": 33}, {"synset": "frisbee.n.01", "coco_cat_id": 34}, {"synset": "ski.n.01", "coco_cat_id": 35}, {"synset": "snowboard.n.01", "coco_cat_id": 36}, {"synset": "ball.n.06", "coco_cat_id": 37}, {"synset": "kite.n.03", "coco_cat_id": 38}, {"synset": "baseball_bat.n.01", "coco_cat_id": 39}, {"synset": "baseball_glove.n.01", "coco_cat_id": 40}, {"synset": "skateboard.n.01", "coco_cat_id": 41}, {"synset": "surfboard.n.01", "coco_cat_id": 42}, {"synset": "tennis_racket.n.01", "coco_cat_id": 43}, {"synset": "bottle.n.01", "coco_cat_id": 44}, {"synset": "wineglass.n.01", "coco_cat_id": 46}, {"synset": "cup.n.01", "coco_cat_id": 47}, {"synset": "fork.n.01", "coco_cat_id": 48}, {"synset": "knife.n.01", "coco_cat_id": 49}, {"synset": "spoon.n.01", "coco_cat_id": 50}, {"synset": "bowl.n.03", "coco_cat_id": 51}, {"synset": "banana.n.02", "coco_cat_id": 52}, {"synset": "apple.n.01", "coco_cat_id": 53}, {"synset": "sandwich.n.01", "coco_cat_id": 54}, {"synset": "orange.n.01", "coco_cat_id": 55}, {"synset": "broccoli.n.01", "coco_cat_id": 56}, {"synset": "carrot.n.01", "coco_cat_id": 57}, {"synset": "frank.n.02", "coco_cat_id": 58}, {"synset": "pizza.n.01", "coco_cat_id": 59}, {"synset": "doughnut.n.02", "coco_cat_id": 60}, {"synset": "cake.n.03", "coco_cat_id": 61}, {"synset": "chair.n.01", "coco_cat_id": 62}, {"synset": "sofa.n.01", "coco_cat_id": 63}, {"synset": "pot.n.04", "coco_cat_id": 64}, {"synset": "bed.n.01", "coco_cat_id": 65}, {"synset": "dining_table.n.01", "coco_cat_id": 67}, {"synset": "toilet.n.02", "coco_cat_id": 70}, {"synset": "television_receiver.n.01", "coco_cat_id": 72}, {"synset": "laptop.n.01", "coco_cat_id": 73}, {"synset": "mouse.n.04", "coco_cat_id": 74}, {"synset": "remote_control.n.01", "coco_cat_id": 75}, {"synset": "computer_keyboard.n.01", "coco_cat_id": 76}, {"synset": "cellular_telephone.n.01", "coco_cat_id": 77}, {"synset": "microwave.n.02", "coco_cat_id": 78}, {"synset": "oven.n.01", "coco_cat_id": 79}, {"synset": "toaster.n.02", "coco_cat_id": 80}, {"synset": "sink.n.01", "coco_cat_id": 81}, {"synset": "electric_refrigerator.n.01", "coco_cat_id": 82}, {"synset": "book.n.01", "coco_cat_id": 84}, {"synset": "clock.n.01", "coco_cat_id": 85}, {"synset": "vase.n.01", "coco_cat_id": 86}, {"synset": "scissors.n.01", "coco_cat_id": 87}, {"synset": "teddy.n.01", "coco_cat_id": 88}, {"synset": "hand_blower.n.01", "coco_cat_id": 89}, {"synset": "toothbrush.n.01", "coco_cat_id": 90}, ] def cocofy_lvis(input_filename, output_filename): """ Filter LVIS instance segmentation annotations to remove all categories that are not included in COCO. The new json files can be used to evaluate COCO AP using `lvis-api`. The category ids in the output json are the incontiguous COCO dataset ids. Args: input_filename (str): path to the LVIS json file. output_filename (str): path to the COCOfied json file. """ with open(input_filename, "r") as f: lvis_json = json.load(f) lvis_annos = lvis_json.pop("annotations") cocofied_lvis = copy.deepcopy(lvis_json) lvis_json["annotations"] = lvis_annos # Mapping from lvis cat id to coco cat id via synset lvis_cat_id_to_synset = {cat["id"]: cat["synset"] for cat in lvis_json["categories"]} synset_to_coco_cat_id = {x["synset"]: x["coco_cat_id"] for x in COCO_SYNSET_CATEGORIES} # Synsets that we will keep in the dataset synsets_to_keep = set(synset_to_coco_cat_id.keys()) coco_cat_id_with_instances = defaultdict(int) new_annos = [] ann_id = 1 for ann in lvis_annos: lvis_cat_id = ann["category_id"] synset = lvis_cat_id_to_synset[lvis_cat_id] if synset not in synsets_to_keep: continue coco_cat_id = synset_to_coco_cat_id[synset] new_ann = copy.deepcopy(ann) new_ann["category_id"] = coco_cat_id new_ann["id"] = ann_id ann_id += 1 new_annos.append(new_ann) coco_cat_id_with_instances[coco_cat_id] += 1 cocofied_lvis["annotations"] = new_annos for image in cocofied_lvis["images"]: for key in ["not_exhaustive_category_ids", "neg_category_ids"]: new_category_list = [] for lvis_cat_id in image[key]: synset = lvis_cat_id_to_synset[lvis_cat_id] if synset not in synsets_to_keep: continue coco_cat_id = synset_to_coco_cat_id[synset] new_category_list.append(coco_cat_id) coco_cat_id_with_instances[coco_cat_id] += 1 image[key] = new_category_list coco_cat_id_with_instances = set(coco_cat_id_with_instances.keys()) new_categories = [] for cat in lvis_json["categories"]: synset = cat["synset"] if synset not in synsets_to_keep: continue coco_cat_id = synset_to_coco_cat_id[synset] if coco_cat_id not in coco_cat_id_with_instances: continue new_cat = copy.deepcopy(cat) new_cat["id"] = coco_cat_id new_categories.append(new_cat) cocofied_lvis["categories"] = new_categories with open(output_filename, "w") as f: json.dump(cocofied_lvis, f) print("{} is COCOfied and stored in {}.".format(input_filename, output_filename)) if __name__ == "__main__": dataset_dir = os.path.join(os.getenv("DETECTRON2_DATASETS", "datasets"), "lvis") for s in ["lvis_v0.5_train", "lvis_v0.5_val"]: print("Start COCOfing {}.".format(s)) cocofy_lvis( os.path.join(dataset_dir, "{}.json".format(s)), os.path.join(dataset_dir, "{}_cocofied.json".format(s)), ) ================================================ FILE: detectron2/datasets/prepare_for_tests.sh ================================================ #!/bin/bash -e # Copyright (c) Facebook, Inc. and its affiliates. # Download the mini dataset (coco val2017_100, with only 100 images) # to be used in unittests & integration tests. cd "${0%/*}" BASE=https://dl.fbaipublicfiles.com/detectron2 ROOT=${DETECTRON2_DATASETS:-./} ROOT=${ROOT/#\~/$HOME} # expand ~ to HOME mkdir -p $ROOT/coco/annotations for anno in instances_val2017_100 \ person_keypoints_val2017_100 ; do dest=$ROOT/coco/annotations/$anno.json [[ -s $dest ]] && { echo "$dest exists. Skipping ..." } || { wget $BASE/annotations/coco/$anno.json -O $dest } done dest=$ROOT/coco/val2017_100.tgz [[ -d $ROOT/coco/val2017 ]] && { echo "$ROOT/coco/val2017 exists. Skipping ..." } || { wget $BASE/annotations/coco/val2017_100.tgz -O $dest tar xzf $dest -C $ROOT/coco/ && rm -f $dest } ================================================ FILE: detectron2/datasets/prepare_panoptic_fpn.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import functools import json import multiprocessing as mp import numpy as np import os import time from fvcore.common.download import download from panopticapi.utils import rgb2id from PIL import Image from detectron2.data.datasets.builtin_meta import COCO_CATEGORIES def _process_panoptic_to_semantic(input_panoptic, output_semantic, segments, id_map): panoptic = np.asarray(Image.open(input_panoptic), dtype=np.uint32) panoptic = rgb2id(panoptic) output = np.zeros_like(panoptic, dtype=np.uint8) + 255 for seg in segments: cat_id = seg["category_id"] new_cat_id = id_map[cat_id] output[panoptic == seg["id"]] = new_cat_id Image.fromarray(output).save(output_semantic) def separate_coco_semantic_from_panoptic(panoptic_json, panoptic_root, sem_seg_root, categories): """ Create semantic segmentation annotations from panoptic segmentation annotations, to be used by PanopticFPN. It maps all thing categories to class 0, and maps all unlabeled pixels to class 255. It maps all stuff categories to contiguous ids starting from 1. Args: panoptic_json (str): path to the panoptic json file, in COCO's format. panoptic_root (str): a directory with panoptic annotation files, in COCO's format. sem_seg_root (str): a directory to output semantic annotation files categories (list[dict]): category metadata. Each dict needs to have: "id": corresponds to the "category_id" in the json annotations "isthing": 0 or 1 """ os.makedirs(sem_seg_root, exist_ok=True) stuff_ids = [k["id"] for k in categories if k["isthing"] == 0] thing_ids = [k["id"] for k in categories if k["isthing"] == 1] id_map = {} # map from category id to id in the output semantic annotation assert len(stuff_ids) <= 254 for i, stuff_id in enumerate(stuff_ids): id_map[stuff_id] = i + 1 for thing_id in thing_ids: id_map[thing_id] = 0 id_map[0] = 255 with open(panoptic_json) as f: obj = json.load(f) pool = mp.Pool(processes=max(mp.cpu_count() // 2, 4)) def iter_annotations(): for anno in obj["annotations"]: file_name = anno["file_name"] segments = anno["segments_info"] input = os.path.join(panoptic_root, file_name) output = os.path.join(sem_seg_root, file_name) yield input, output, segments print("Start writing to {} ...".format(sem_seg_root)) start = time.time() pool.starmap( functools.partial(_process_panoptic_to_semantic, id_map=id_map), iter_annotations(), chunksize=100, ) print("Finished. time: {:.2f}s".format(time.time() - start)) if __name__ == "__main__": dataset_dir = os.path.join(os.getenv("DETECTRON2_DATASETS", "datasets"), "coco") for s in ["val2017", "train2017"]: separate_coco_semantic_from_panoptic( os.path.join(dataset_dir, "annotations/panoptic_{}.json".format(s)), os.path.join(dataset_dir, "panoptic_{}".format(s)), os.path.join(dataset_dir, "panoptic_stuff_{}".format(s)), COCO_CATEGORIES, ) # Prepare val2017_100 for quick testing: dest_dir = os.path.join(dataset_dir, "annotations/") URL_PREFIX = "https://dl.fbaipublicfiles.com/detectron2/" download(URL_PREFIX + "annotations/coco/panoptic_val2017_100.json", dest_dir) with open(os.path.join(dest_dir, "panoptic_val2017_100.json")) as f: obj = json.load(f) def link_val100(dir_full, dir_100): print("Creating " + dir_100 + " ...") os.makedirs(dir_100, exist_ok=True) for img in obj["images"]: basename = os.path.splitext(img["file_name"])[0] src = os.path.join(dir_full, basename + ".png") dst = os.path.join(dir_100, basename + ".png") src = os.path.relpath(src, start=dir_100) os.symlink(src, dst) link_val100( os.path.join(dataset_dir, "panoptic_val2017"), os.path.join(dataset_dir, "panoptic_val2017_100"), ) link_val100( os.path.join(dataset_dir, "panoptic_stuff_val2017"), os.path.join(dataset_dir, "panoptic_stuff_val2017_100"), ) ================================================ FILE: detectron2/demo/README.md ================================================ ## Detectron2 Demo We provide a command line tool to run a simple demo of builtin configs. The usage is explained in [GETTING_STARTED.md](../GETTING_STARTED.md). See our [blog post](https://ai.facebook.com/blog/-detectron2-a-pytorch-based-modular-object-detection-library-) for a high-quality demo generated with this tool. ================================================ FILE: detectron2/demo/demo.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import argparse import glob import multiprocessing as mp import numpy as np import os import tempfile import time import warnings import cv2 import tqdm from detectron2.config import get_cfg from detectron2.data.detection_utils import read_image from detectron2.utils.logger import setup_logger from predictor import VisualizationDemo # constants WINDOW_NAME = "COCO detections" def setup_cfg(args): # load config from file and command-line arguments cfg = get_cfg() # To use demo for Panoptic-DeepLab, please uncomment the following two lines. # from detectron2.projects.panoptic_deeplab import add_panoptic_deeplab_config # noqa # add_panoptic_deeplab_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) # Set score_threshold for builtin models cfg.MODEL.RETINANET.SCORE_THRESH_TEST = args.confidence_threshold cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = args.confidence_threshold cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH = args.confidence_threshold cfg.freeze() return cfg def get_parser(): parser = argparse.ArgumentParser(description="Detectron2 demo for builtin configs") parser.add_argument( "--config-file", default="configs/quick_schedules/mask_rcnn_R_50_FPN_inference_acc_test.yaml", metavar="FILE", help="path to config file", ) parser.add_argument("--webcam", action="store_true", help="Take inputs from webcam.") parser.add_argument("--video-input", help="Path to video file.") parser.add_argument( "--input", nargs="+", help="A list of space separated input images; " "or a single glob pattern such as 'directory/*.jpg'", ) parser.add_argument( "--output", help="A file or directory to save output visualizations. " "If not given, will show output in an OpenCV window.", ) parser.add_argument( "--confidence-threshold", type=float, default=0.5, help="Minimum score for instance predictions to be shown", ) parser.add_argument( "--opts", help="Modify config options using the command-line 'KEY VALUE' pairs", default=[], nargs=argparse.REMAINDER, ) return parser def test_opencv_video_format(codec, file_ext): with tempfile.TemporaryDirectory(prefix="video_format_test") as dir: filename = os.path.join(dir, "test_file" + file_ext) writer = cv2.VideoWriter( filename=filename, fourcc=cv2.VideoWriter_fourcc(*codec), fps=float(30), frameSize=(10, 10), isColor=True, ) [writer.write(np.zeros((10, 10, 3), np.uint8)) for _ in range(30)] writer.release() if os.path.isfile(filename): return True return False if __name__ == "__main__": mp.set_start_method("spawn", force=True) args = get_parser().parse_args() setup_logger(name="fvcore") logger = setup_logger() logger.info("Arguments: " + str(args)) cfg = setup_cfg(args) demo = VisualizationDemo(cfg) if args.input: if len(args.input) == 1: args.input = glob.glob(os.path.expanduser(args.input[0])) assert args.input, "The input path(s) was not found" for path in tqdm.tqdm(args.input, disable=not args.output): # use PIL, to be consistent with evaluation img = read_image(path, format="BGR") start_time = time.time() predictions, visualized_output = demo.run_on_image(img) logger.info( "{}: {} in {:.2f}s".format( path, "detected {} instances".format(len(predictions["instances"])) if "instances" in predictions else "finished", time.time() - start_time, ) ) if args.output: if os.path.isdir(args.output): assert os.path.isdir(args.output), args.output out_filename = os.path.join(args.output, os.path.basename(path)) else: assert len(args.input) == 1, "Please specify a directory with args.output" out_filename = args.output visualized_output.save(out_filename) else: cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL) cv2.imshow(WINDOW_NAME, visualized_output.get_image()[:, :, ::-1]) if cv2.waitKey(0) == 27: break # esc to quit elif args.webcam: assert args.input is None, "Cannot have both --input and --webcam!" assert args.output is None, "output not yet supported with --webcam!" cam = cv2.VideoCapture(0) for vis in tqdm.tqdm(demo.run_on_video(cam)): cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL) cv2.imshow(WINDOW_NAME, vis) if cv2.waitKey(1) == 27: break # esc to quit cam.release() cv2.destroyAllWindows() elif args.video_input: video = cv2.VideoCapture(args.video_input) width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT)) frames_per_second = video.get(cv2.CAP_PROP_FPS) num_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT)) basename = os.path.basename(args.video_input) codec, file_ext = ( ("x264", ".mkv") if test_opencv_video_format("x264", ".mkv") else ("mp4v", ".mp4") ) if codec == ".mp4v": warnings.warn("x264 codec not available, switching to mp4v") if args.output: if os.path.isdir(args.output): output_fname = os.path.join(args.output, basename) output_fname = os.path.splitext(output_fname)[0] + file_ext else: output_fname = args.output assert not os.path.isfile(output_fname), output_fname output_file = cv2.VideoWriter( filename=output_fname, # some installation of opencv may not support x264 (due to its license), # you can try other format (e.g. MPEG) fourcc=cv2.VideoWriter_fourcc(*codec), fps=float(frames_per_second), frameSize=(width, height), isColor=True, ) assert os.path.isfile(args.video_input) for vis_frame in tqdm.tqdm(demo.run_on_video(video), total=num_frames): if args.output: output_file.write(vis_frame) else: cv2.namedWindow(basename, cv2.WINDOW_NORMAL) cv2.imshow(basename, vis_frame) if cv2.waitKey(1) == 27: break # esc to quit video.release() if args.output: output_file.release() else: cv2.destroyAllWindows() ================================================ FILE: detectron2/demo/predictor.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import atexit import bisect import multiprocessing as mp from collections import deque import cv2 import torch from detectron2.data import MetadataCatalog from detectron2.engine.defaults import DefaultPredictor from detectron2.utils.video_visualizer import VideoVisualizer from detectron2.utils.visualizer import ColorMode, Visualizer class VisualizationDemo(object): def __init__(self, cfg, instance_mode=ColorMode.IMAGE, parallel=False): """ Args: cfg (CfgNode): instance_mode (ColorMode): parallel (bool): whether to run the model in different processes from visualization. Useful since the visualization logic can be slow. """ self.metadata = MetadataCatalog.get( cfg.DATASETS.TEST[0] if len(cfg.DATASETS.TEST) else "__unused" ) self.cpu_device = torch.device("cpu") self.instance_mode = instance_mode self.parallel = parallel if parallel: num_gpu = torch.cuda.device_count() self.predictor = AsyncPredictor(cfg, num_gpus=num_gpu) else: self.predictor = DefaultPredictor(cfg) def run_on_image(self, image): """ Args: image (np.ndarray): an image of shape (H, W, C) (in BGR order). This is the format used by OpenCV. Returns: predictions (dict): the output of the model. vis_output (VisImage): the visualized image output. """ vis_output = None predictions = self.predictor(image) # Convert image from OpenCV BGR format to Matplotlib RGB format. image = image[:, :, ::-1] visualizer = Visualizer(image, self.metadata, instance_mode=self.instance_mode) if "panoptic_seg" in predictions: panoptic_seg, segments_info = predictions["panoptic_seg"] vis_output = visualizer.draw_panoptic_seg_predictions( panoptic_seg.to(self.cpu_device), segments_info ) else: if "sem_seg" in predictions: vis_output = visualizer.draw_sem_seg( predictions["sem_seg"].argmax(dim=0).to(self.cpu_device) ) if "instances" in predictions: instances = predictions["instances"].to(self.cpu_device) vis_output = visualizer.draw_instance_predictions(predictions=instances) return predictions, vis_output def _frame_from_video(self, video): while video.isOpened(): success, frame = video.read() if success: yield frame else: break def run_on_video(self, video): """ Visualizes predictions on frames of the input video. Args: video (cv2.VideoCapture): a :class:`VideoCapture` object, whose source can be either a webcam or a video file. Yields: ndarray: BGR visualizations of each video frame. """ video_visualizer = VideoVisualizer(self.metadata, self.instance_mode) def process_predictions(frame, predictions): frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) if "panoptic_seg" in predictions: panoptic_seg, segments_info = predictions["panoptic_seg"] vis_frame = video_visualizer.draw_panoptic_seg_predictions( frame, panoptic_seg.to(self.cpu_device), segments_info ) elif "instances" in predictions: predictions = predictions["instances"].to(self.cpu_device) vis_frame = video_visualizer.draw_instance_predictions(frame, predictions) elif "sem_seg" in predictions: vis_frame = video_visualizer.draw_sem_seg( frame, predictions["sem_seg"].argmax(dim=0).to(self.cpu_device) ) # Converts Matplotlib RGB format to OpenCV BGR format vis_frame = cv2.cvtColor(vis_frame.get_image(), cv2.COLOR_RGB2BGR) return vis_frame frame_gen = self._frame_from_video(video) if self.parallel: buffer_size = self.predictor.default_buffer_size frame_data = deque() for cnt, frame in enumerate(frame_gen): frame_data.append(frame) self.predictor.put(frame) if cnt >= buffer_size: frame = frame_data.popleft() predictions = self.predictor.get() yield process_predictions(frame, predictions) while len(frame_data): frame = frame_data.popleft() predictions = self.predictor.get() yield process_predictions(frame, predictions) else: for frame in frame_gen: yield process_predictions(frame, self.predictor(frame)) class AsyncPredictor: """ A predictor that runs the model asynchronously, possibly on >1 GPUs. Because rendering the visualization takes considerably amount of time, this helps improve throughput a little bit when rendering videos. """ class _StopToken: pass class _PredictWorker(mp.Process): def __init__(self, cfg, task_queue, result_queue): self.cfg = cfg self.task_queue = task_queue self.result_queue = result_queue super().__init__() def run(self): predictor = DefaultPredictor(self.cfg) while True: task = self.task_queue.get() if isinstance(task, AsyncPredictor._StopToken): break idx, data = task result = predictor(data) self.result_queue.put((idx, result)) def __init__(self, cfg, num_gpus: int = 1): """ Args: cfg (CfgNode): num_gpus (int): if 0, will run on CPU """ num_workers = max(num_gpus, 1) self.task_queue = mp.Queue(maxsize=num_workers * 3) self.result_queue = mp.Queue(maxsize=num_workers * 3) self.procs = [] for gpuid in range(max(num_gpus, 1)): cfg = cfg.clone() cfg.defrost() cfg.MODEL.DEVICE = "cuda:{}".format(gpuid) if num_gpus > 0 else "cpu" self.procs.append( AsyncPredictor._PredictWorker(cfg, self.task_queue, self.result_queue) ) self.put_idx = 0 self.get_idx = 0 self.result_rank = [] self.result_data = [] for p in self.procs: p.start() atexit.register(self.shutdown) def put(self, image): self.put_idx += 1 self.task_queue.put((self.put_idx, image)) def get(self): self.get_idx += 1 # the index needed for this request if len(self.result_rank) and self.result_rank[0] == self.get_idx: res = self.result_data[0] del self.result_data[0], self.result_rank[0] return res while True: # make sure the results are returned in the correct order idx, res = self.result_queue.get() if idx == self.get_idx: return res insert = bisect.bisect(self.result_rank, idx) self.result_rank.insert(insert, idx) self.result_data.insert(insert, res) def __len__(self): return self.put_idx - self.get_idx def __call__(self, image): self.put(image) return self.get() def shutdown(self): for _ in self.procs: self.task_queue.put(AsyncPredictor._StopToken()) @property def default_buffer_size(self): return len(self.procs) * 5 ================================================ FILE: detectron2/detectron2/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .utils.env import setup_environment setup_environment() # This line will be programatically read/write by setup.py. # Leave them at the bottom of this file and don't touch them. __version__ = "0.6" ================================================ FILE: detectron2/detectron2/checkpoint/__init__.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. # File: from . import catalog as _UNUSED # register the handler from .detection_checkpoint import DetectionCheckpointer from fvcore.common.checkpoint import Checkpointer, PeriodicCheckpointer __all__ = ["Checkpointer", "PeriodicCheckpointer", "DetectionCheckpointer"] ================================================ FILE: detectron2/detectron2/checkpoint/c2_model_loading.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import logging import re from typing import Dict, List import torch from tabulate import tabulate def convert_basic_c2_names(original_keys): """ Apply some basic name conversion to names in C2 weights. It only deals with typical backbone models. Args: original_keys (list[str]): Returns: list[str]: The same number of strings matching those in original_keys. """ layer_keys = copy.deepcopy(original_keys) layer_keys = [ {"pred_b": "linear_b", "pred_w": "linear_w"}.get(k, k) for k in layer_keys ] # some hard-coded mappings layer_keys = [k.replace("_", ".") for k in layer_keys] layer_keys = [re.sub("\\.b$", ".bias", k) for k in layer_keys] layer_keys = [re.sub("\\.w$", ".weight", k) for k in layer_keys] # Uniform both bn and gn names to "norm" layer_keys = [re.sub("bn\\.s$", "norm.weight", k) for k in layer_keys] layer_keys = [re.sub("bn\\.bias$", "norm.bias", k) for k in layer_keys] layer_keys = [re.sub("bn\\.rm", "norm.running_mean", k) for k in layer_keys] layer_keys = [re.sub("bn\\.running.mean$", "norm.running_mean", k) for k in layer_keys] layer_keys = [re.sub("bn\\.riv$", "norm.running_var", k) for k in layer_keys] layer_keys = [re.sub("bn\\.running.var$", "norm.running_var", k) for k in layer_keys] layer_keys = [re.sub("bn\\.gamma$", "norm.weight", k) for k in layer_keys] layer_keys = [re.sub("bn\\.beta$", "norm.bias", k) for k in layer_keys] layer_keys = [re.sub("gn\\.s$", "norm.weight", k) for k in layer_keys] layer_keys = [re.sub("gn\\.bias$", "norm.bias", k) for k in layer_keys] # stem layer_keys = [re.sub("^res\\.conv1\\.norm\\.", "conv1.norm.", k) for k in layer_keys] # to avoid mis-matching with "conv1" in other components (e.g. detection head) layer_keys = [re.sub("^conv1\\.", "stem.conv1.", k) for k in layer_keys] # layer1-4 is used by torchvision, however we follow the C2 naming strategy (res2-5) # layer_keys = [re.sub("^res2.", "layer1.", k) for k in layer_keys] # layer_keys = [re.sub("^res3.", "layer2.", k) for k in layer_keys] # layer_keys = [re.sub("^res4.", "layer3.", k) for k in layer_keys] # layer_keys = [re.sub("^res5.", "layer4.", k) for k in layer_keys] # blocks layer_keys = [k.replace(".branch1.", ".shortcut.") for k in layer_keys] layer_keys = [k.replace(".branch2a.", ".conv1.") for k in layer_keys] layer_keys = [k.replace(".branch2b.", ".conv2.") for k in layer_keys] layer_keys = [k.replace(".branch2c.", ".conv3.") for k in layer_keys] # DensePose substitutions layer_keys = [re.sub("^body.conv.fcn", "body_conv_fcn", k) for k in layer_keys] layer_keys = [k.replace("AnnIndex.lowres", "ann_index_lowres") for k in layer_keys] layer_keys = [k.replace("Index.UV.lowres", "index_uv_lowres") for k in layer_keys] layer_keys = [k.replace("U.lowres", "u_lowres") for k in layer_keys] layer_keys = [k.replace("V.lowres", "v_lowres") for k in layer_keys] return layer_keys def convert_c2_detectron_names(weights): """ Map Caffe2 Detectron weight names to Detectron2 names. Args: weights (dict): name -> tensor Returns: dict: detectron2 names -> tensor dict: detectron2 names -> C2 names """ logger = logging.getLogger(__name__) logger.info("Renaming Caffe2 weights ......") original_keys = sorted(weights.keys()) layer_keys = copy.deepcopy(original_keys) layer_keys = convert_basic_c2_names(layer_keys) # -------------------------------------------------------------------------- # RPN hidden representation conv # -------------------------------------------------------------------------- # FPN case # In the C2 model, the RPN hidden layer conv is defined for FPN level 2 and then # shared for all other levels, hence the appearance of "fpn2" layer_keys = [ k.replace("conv.rpn.fpn2", "proposal_generator.rpn_head.conv") for k in layer_keys ] # Non-FPN case layer_keys = [k.replace("conv.rpn", "proposal_generator.rpn_head.conv") for k in layer_keys] # -------------------------------------------------------------------------- # RPN box transformation conv # -------------------------------------------------------------------------- # FPN case (see note above about "fpn2") layer_keys = [ k.replace("rpn.bbox.pred.fpn2", "proposal_generator.rpn_head.anchor_deltas") for k in layer_keys ] layer_keys = [ k.replace("rpn.cls.logits.fpn2", "proposal_generator.rpn_head.objectness_logits") for k in layer_keys ] # Non-FPN case layer_keys = [ k.replace("rpn.bbox.pred", "proposal_generator.rpn_head.anchor_deltas") for k in layer_keys ] layer_keys = [ k.replace("rpn.cls.logits", "proposal_generator.rpn_head.objectness_logits") for k in layer_keys ] # -------------------------------------------------------------------------- # Fast R-CNN box head # -------------------------------------------------------------------------- layer_keys = [re.sub("^bbox\\.pred", "bbox_pred", k) for k in layer_keys] layer_keys = [re.sub("^cls\\.score", "cls_score", k) for k in layer_keys] layer_keys = [re.sub("^fc6\\.", "box_head.fc1.", k) for k in layer_keys] layer_keys = [re.sub("^fc7\\.", "box_head.fc2.", k) for k in layer_keys] # 4conv1fc head tensor names: head_conv1_w, head_conv1_gn_s layer_keys = [re.sub("^head\\.conv", "box_head.conv", k) for k in layer_keys] # -------------------------------------------------------------------------- # FPN lateral and output convolutions # -------------------------------------------------------------------------- def fpn_map(name): """ Look for keys with the following patterns: 1) Starts with "fpn.inner." Example: "fpn.inner.res2.2.sum.lateral.weight" Meaning: These are lateral pathway convolutions 2) Starts with "fpn.res" Example: "fpn.res2.2.sum.weight" Meaning: These are FPN output convolutions """ splits = name.split(".") norm = ".norm" if "norm" in splits else "" if name.startswith("fpn.inner."): # splits example: ['fpn', 'inner', 'res2', '2', 'sum', 'lateral', 'weight'] stage = int(splits[2][len("res") :]) return "fpn_lateral{}{}.{}".format(stage, norm, splits[-1]) elif name.startswith("fpn.res"): # splits example: ['fpn', 'res2', '2', 'sum', 'weight'] stage = int(splits[1][len("res") :]) return "fpn_output{}{}.{}".format(stage, norm, splits[-1]) return name layer_keys = [fpn_map(k) for k in layer_keys] # -------------------------------------------------------------------------- # Mask R-CNN mask head # -------------------------------------------------------------------------- # roi_heads.StandardROIHeads case layer_keys = [k.replace(".[mask].fcn", "mask_head.mask_fcn") for k in layer_keys] layer_keys = [re.sub("^\\.mask\\.fcn", "mask_head.mask_fcn", k) for k in layer_keys] layer_keys = [k.replace("mask.fcn.logits", "mask_head.predictor") for k in layer_keys] # roi_heads.Res5ROIHeads case layer_keys = [k.replace("conv5.mask", "mask_head.deconv") for k in layer_keys] # -------------------------------------------------------------------------- # Keypoint R-CNN head # -------------------------------------------------------------------------- # interestingly, the keypoint head convs have blob names that are simply "conv_fcnX" layer_keys = [k.replace("conv.fcn", "roi_heads.keypoint_head.conv_fcn") for k in layer_keys] layer_keys = [ k.replace("kps.score.lowres", "roi_heads.keypoint_head.score_lowres") for k in layer_keys ] layer_keys = [k.replace("kps.score.", "roi_heads.keypoint_head.score.") for k in layer_keys] # -------------------------------------------------------------------------- # Done with replacements # -------------------------------------------------------------------------- assert len(set(layer_keys)) == len(layer_keys) assert len(original_keys) == len(layer_keys) new_weights = {} new_keys_to_original_keys = {} for orig, renamed in zip(original_keys, layer_keys): new_keys_to_original_keys[renamed] = orig if renamed.startswith("bbox_pred.") or renamed.startswith("mask_head.predictor."): # remove the meaningless prediction weight for background class new_start_idx = 4 if renamed.startswith("bbox_pred.") else 1 new_weights[renamed] = weights[orig][new_start_idx:] logger.info( "Remove prediction weight for background class in {}. The shape changes from " "{} to {}.".format( renamed, tuple(weights[orig].shape), tuple(new_weights[renamed].shape) ) ) elif renamed.startswith("cls_score."): # move weights of bg class from original index 0 to last index logger.info( "Move classification weights for background class in {} from index 0 to " "index {}.".format(renamed, weights[orig].shape[0] - 1) ) new_weights[renamed] = torch.cat([weights[orig][1:], weights[orig][:1]]) else: new_weights[renamed] = weights[orig] return new_weights, new_keys_to_original_keys # Note the current matching is not symmetric. # it assumes model_state_dict will have longer names. def align_and_update_state_dicts(model_state_dict, ckpt_state_dict, c2_conversion=True): """ Match names between the two state-dict, and returns a new chkpt_state_dict with names converted to match model_state_dict with heuristics. The returned dict can be later loaded with fvcore checkpointer. If `c2_conversion==True`, `ckpt_state_dict` is assumed to be a Caffe2 model and will be renamed at first. Strategy: suppose that the models that we will create will have prefixes appended to each of its keys, for example due to an extra level of nesting that the original pre-trained weights from ImageNet won't contain. For example, model.state_dict() might return backbone[0].body.res2.conv1.weight, while the pre-trained model contains res2.conv1.weight. We thus want to match both parameters together. For that, we look for each model weight, look among all loaded keys if there is one that is a suffix of the current weight name, and use it if that's the case. If multiple matches exist, take the one with longest size of the corresponding name. For example, for the same model as before, the pretrained weight file can contain both res2.conv1.weight, as well as conv1.weight. In this case, we want to match backbone[0].body.conv1.weight to conv1.weight, and backbone[0].body.res2.conv1.weight to res2.conv1.weight. """ model_keys = sorted(model_state_dict.keys()) if c2_conversion: ckpt_state_dict, original_keys = convert_c2_detectron_names(ckpt_state_dict) # original_keys: the name in the original dict (before renaming) else: original_keys = {x: x for x in ckpt_state_dict.keys()} ckpt_keys = sorted(ckpt_state_dict.keys()) def match(a, b): # Matched ckpt_key should be a complete (starts with '.') suffix. # For example, roi_heads.mesh_head.whatever_conv1 does not match conv1, # but matches whatever_conv1 or mesh_head.whatever_conv1. return a == b or a.endswith("." + b) # get a matrix of string matches, where each (i, j) entry correspond to the size of the # ckpt_key string, if it matches match_matrix = [len(j) if match(i, j) else 0 for i in model_keys for j in ckpt_keys] match_matrix = torch.as_tensor(match_matrix).view(len(model_keys), len(ckpt_keys)) # use the matched one with longest size in case of multiple matches max_match_size, idxs = match_matrix.max(1) # remove indices that correspond to no-match idxs[max_match_size == 0] = -1 logger = logging.getLogger(__name__) # matched_pairs (matched checkpoint key --> matched model key) matched_keys = {} result_state_dict = {} for idx_model, idx_ckpt in enumerate(idxs.tolist()): if idx_ckpt == -1: continue key_model = model_keys[idx_model] key_ckpt = ckpt_keys[idx_ckpt] value_ckpt = ckpt_state_dict[key_ckpt] shape_in_model = model_state_dict[key_model].shape if shape_in_model != value_ckpt.shape: logger.warning( "Shape of {} in checkpoint is {}, while shape of {} in model is {}.".format( key_ckpt, value_ckpt.shape, key_model, shape_in_model ) ) logger.warning( "{} will not be loaded. Please double check and see if this is desired.".format( key_ckpt ) ) continue assert key_model not in result_state_dict result_state_dict[key_model] = value_ckpt if key_ckpt in matched_keys: # already added to matched_keys logger.error( "Ambiguity found for {} in checkpoint!" "It matches at least two keys in the model ({} and {}).".format( key_ckpt, key_model, matched_keys[key_ckpt] ) ) raise ValueError("Cannot match one checkpoint key to multiple keys in the model.") matched_keys[key_ckpt] = key_model # logging: matched_model_keys = sorted(matched_keys.values()) if len(matched_model_keys) == 0: logger.warning("No weights in checkpoint matched with model.") return ckpt_state_dict common_prefix = _longest_common_prefix(matched_model_keys) rev_matched_keys = {v: k for k, v in matched_keys.items()} original_keys = {k: original_keys[rev_matched_keys[k]] for k in matched_model_keys} model_key_groups = _group_keys_by_module(matched_model_keys, original_keys) table = [] memo = set() for key_model in matched_model_keys: if key_model in memo: continue if key_model in model_key_groups: group = model_key_groups[key_model] memo |= set(group) shapes = [tuple(model_state_dict[k].shape) for k in group] table.append( ( _longest_common_prefix([k[len(common_prefix) :] for k in group]) + "*", _group_str([original_keys[k] for k in group]), " ".join([str(x).replace(" ", "") for x in shapes]), ) ) else: key_checkpoint = original_keys[key_model] shape = str(tuple(model_state_dict[key_model].shape)) table.append((key_model[len(common_prefix) :], key_checkpoint, shape)) table_str = tabulate( table, tablefmt="pipe", headers=["Names in Model", "Names in Checkpoint", "Shapes"] ) logger.info( "Following weights matched with " + (f"submodule {common_prefix[:-1]}" if common_prefix else "model") + ":\n" + table_str ) unmatched_ckpt_keys = [k for k in ckpt_keys if k not in set(matched_keys.keys())] for k in unmatched_ckpt_keys: result_state_dict[k] = ckpt_state_dict[k] return result_state_dict def _group_keys_by_module(keys: List[str], original_names: Dict[str, str]): """ Params in the same submodule are grouped together. Args: keys: names of all parameters original_names: mapping from parameter name to their name in the checkpoint Returns: dict[name -> all other names in the same group] """ def _submodule_name(key): pos = key.rfind(".") if pos < 0: return None prefix = key[: pos + 1] return prefix all_submodules = [_submodule_name(k) for k in keys] all_submodules = [x for x in all_submodules if x] all_submodules = sorted(all_submodules, key=len) ret = {} for prefix in all_submodules: group = [k for k in keys if k.startswith(prefix)] if len(group) <= 1: continue original_name_lcp = _longest_common_prefix_str([original_names[k] for k in group]) if len(original_name_lcp) == 0: # don't group weights if original names don't share prefix continue for k in group: if k in ret: continue ret[k] = group return ret def _longest_common_prefix(names: List[str]) -> str: """ ["abc.zfg", "abc.zef"] -> "abc." """ names = [n.split(".") for n in names] m1, m2 = min(names), max(names) ret = [a for a, b in zip(m1, m2) if a == b] ret = ".".join(ret) + "." if len(ret) else "" return ret def _longest_common_prefix_str(names: List[str]) -> str: m1, m2 = min(names), max(names) lcp = [a for a, b in zip(m1, m2) if a == b] lcp = "".join(lcp) return lcp def _group_str(names: List[str]) -> str: """ Turn "common1", "common2", "common3" into "common{1,2,3}" """ lcp = _longest_common_prefix_str(names) rest = [x[len(lcp) :] for x in names] rest = "{" + ",".join(rest) + "}" ret = lcp + rest # add some simplification for BN specifically ret = ret.replace("bn_{beta,running_mean,running_var,gamma}", "bn_*") ret = ret.replace("bn_beta,bn_running_mean,bn_running_var,bn_gamma", "bn_*") return ret ================================================ FILE: detectron2/detectron2/checkpoint/catalog.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging from detectron2.utils.file_io import PathHandler, PathManager class ModelCatalog(object): """ Store mappings from names to third-party models. """ S3_C2_DETECTRON_PREFIX = "https://dl.fbaipublicfiles.com/detectron" # MSRA models have STRIDE_IN_1X1=True. False otherwise. # NOTE: all BN models here have fused BN into an affine layer. # As a result, you should only load them to a model with "FrozenBN". # Loading them to a model with regular BN or SyncBN is wrong. # Even when loaded to FrozenBN, it is still different from affine by an epsilon, # which should be negligible for training. # NOTE: all models here uses PIXEL_STD=[1,1,1] # NOTE: Most of the BN models here are no longer used. We use the # re-converted pre-trained models under detectron2 model zoo instead. C2_IMAGENET_MODELS = { "MSRA/R-50": "ImageNetPretrained/MSRA/R-50.pkl", "MSRA/R-101": "ImageNetPretrained/MSRA/R-101.pkl", "FAIR/R-50-GN": "ImageNetPretrained/47261647/R-50-GN.pkl", "FAIR/R-101-GN": "ImageNetPretrained/47592356/R-101-GN.pkl", "FAIR/X-101-32x8d": "ImageNetPretrained/20171220/X-101-32x8d.pkl", "FAIR/X-101-64x4d": "ImageNetPretrained/FBResNeXt/X-101-64x4d.pkl", "FAIR/X-152-32x8d-IN5k": "ImageNetPretrained/25093814/X-152-32x8d-IN5k.pkl", } C2_DETECTRON_PATH_FORMAT = ( "{prefix}/{url}/output/train/{dataset}/{type}/model_final.pkl" # noqa B950 ) C2_DATASET_COCO = "coco_2014_train%3Acoco_2014_valminusminival" C2_DATASET_COCO_KEYPOINTS = "keypoints_coco_2014_train%3Akeypoints_coco_2014_valminusminival" # format: {model_name} -> part of the url C2_DETECTRON_MODELS = { "35857197/e2e_faster_rcnn_R-50-C4_1x": "35857197/12_2017_baselines/e2e_faster_rcnn_R-50-C4_1x.yaml.01_33_49.iAX0mXvW", # noqa B950 "35857345/e2e_faster_rcnn_R-50-FPN_1x": "35857345/12_2017_baselines/e2e_faster_rcnn_R-50-FPN_1x.yaml.01_36_30.cUF7QR7I", # noqa B950 "35857890/e2e_faster_rcnn_R-101-FPN_1x": "35857890/12_2017_baselines/e2e_faster_rcnn_R-101-FPN_1x.yaml.01_38_50.sNxI7sX7", # noqa B950 "36761737/e2e_faster_rcnn_X-101-32x8d-FPN_1x": "36761737/12_2017_baselines/e2e_faster_rcnn_X-101-32x8d-FPN_1x.yaml.06_31_39.5MIHi1fZ", # noqa B950 "35858791/e2e_mask_rcnn_R-50-C4_1x": "35858791/12_2017_baselines/e2e_mask_rcnn_R-50-C4_1x.yaml.01_45_57.ZgkA7hPB", # noqa B950 "35858933/e2e_mask_rcnn_R-50-FPN_1x": "35858933/12_2017_baselines/e2e_mask_rcnn_R-50-FPN_1x.yaml.01_48_14.DzEQe4wC", # noqa B950 "35861795/e2e_mask_rcnn_R-101-FPN_1x": "35861795/12_2017_baselines/e2e_mask_rcnn_R-101-FPN_1x.yaml.02_31_37.KqyEK4tT", # noqa B950 "36761843/e2e_mask_rcnn_X-101-32x8d-FPN_1x": "36761843/12_2017_baselines/e2e_mask_rcnn_X-101-32x8d-FPN_1x.yaml.06_35_59.RZotkLKI", # noqa B950 "48616381/e2e_mask_rcnn_R-50-FPN_2x_gn": "GN/48616381/04_2018_gn_baselines/e2e_mask_rcnn_R-50-FPN_2x_gn_0416.13_23_38.bTlTI97Q", # noqa B950 "37697547/e2e_keypoint_rcnn_R-50-FPN_1x": "37697547/12_2017_baselines/e2e_keypoint_rcnn_R-50-FPN_1x.yaml.08_42_54.kdzV35ao", # noqa B950 "35998355/rpn_R-50-C4_1x": "35998355/12_2017_baselines/rpn_R-50-C4_1x.yaml.08_00_43.njH5oD9L", # noqa B950 "35998814/rpn_R-50-FPN_1x": "35998814/12_2017_baselines/rpn_R-50-FPN_1x.yaml.08_06_03.Axg0r179", # noqa B950 "36225147/fast_R-50-FPN_1x": "36225147/12_2017_baselines/fast_rcnn_R-50-FPN_1x.yaml.08_39_09.L3obSdQ2", # noqa B950 } @staticmethod def get(name): if name.startswith("Caffe2Detectron/COCO"): return ModelCatalog._get_c2_detectron_baseline(name) if name.startswith("ImageNetPretrained/"): return ModelCatalog._get_c2_imagenet_pretrained(name) raise RuntimeError("model not present in the catalog: {}".format(name)) @staticmethod def _get_c2_imagenet_pretrained(name): prefix = ModelCatalog.S3_C2_DETECTRON_PREFIX name = name[len("ImageNetPretrained/") :] name = ModelCatalog.C2_IMAGENET_MODELS[name] url = "/".join([prefix, name]) return url @staticmethod def _get_c2_detectron_baseline(name): name = name[len("Caffe2Detectron/COCO/") :] url = ModelCatalog.C2_DETECTRON_MODELS[name] if "keypoint_rcnn" in name: dataset = ModelCatalog.C2_DATASET_COCO_KEYPOINTS else: dataset = ModelCatalog.C2_DATASET_COCO if "35998355/rpn_R-50-C4_1x" in name: # this one model is somehow different from others .. type = "rpn" else: type = "generalized_rcnn" # Detectron C2 models are stored in the structure defined in `C2_DETECTRON_PATH_FORMAT`. url = ModelCatalog.C2_DETECTRON_PATH_FORMAT.format( prefix=ModelCatalog.S3_C2_DETECTRON_PREFIX, url=url, type=type, dataset=dataset ) return url class ModelCatalogHandler(PathHandler): """ Resolve URL like catalog://. """ PREFIX = "catalog://" def _get_supported_prefixes(self): return [self.PREFIX] def _get_local_path(self, path, **kwargs): logger = logging.getLogger(__name__) catalog_path = ModelCatalog.get(path[len(self.PREFIX) :]) logger.info("Catalog entry {} points to {}".format(path, catalog_path)) return PathManager.get_local_path(catalog_path, **kwargs) def _open(self, path, mode="r", **kwargs): return PathManager.open(self._get_local_path(path), mode, **kwargs) PathManager.register_handler(ModelCatalogHandler()) ================================================ FILE: detectron2/detectron2/checkpoint/detection_checkpoint.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import os import pickle import torch from fvcore.common.checkpoint import Checkpointer from torch.nn.parallel import DistributedDataParallel import detectron2.utils.comm as comm from detectron2.utils.file_io import PathManager from .c2_model_loading import align_and_update_state_dicts class DetectionCheckpointer(Checkpointer): """ Same as :class:`Checkpointer`, but is able to: 1. handle models in detectron & detectron2 model zoo, and apply conversions for legacy models. 2. correctly load checkpoints that are only available on the master worker """ def __init__(self, model, save_dir="", *, save_to_disk=None, **checkpointables): is_main_process = comm.is_main_process() super().__init__( model, save_dir, save_to_disk=is_main_process if save_to_disk is None else save_to_disk, **checkpointables, ) self.path_manager = PathManager def load(self, path, *args, **kwargs): need_sync = False if path and isinstance(self.model, DistributedDataParallel): logger = logging.getLogger(__name__) path = self.path_manager.get_local_path(path) has_file = os.path.isfile(path) all_has_file = comm.all_gather(has_file) if not all_has_file[0]: raise OSError(f"File {path} not found on main worker.") if not all(all_has_file): logger.warning( f"Not all workers can read checkpoint {path}. " "Training may fail to fully resume." ) # TODO: broadcast the checkpoint file contents from main # worker, and load from it instead. need_sync = True if not has_file: path = None # don't load if not readable ret = super().load(path, *args, **kwargs) if need_sync: logger.info("Broadcasting model states from main worker ...") self.model._sync_params_and_buffers() return ret def _load_file(self, filename): if filename.endswith(".pkl"): with PathManager.open(filename, "rb") as f: data = pickle.load(f, encoding="latin1") if "model" in data and "__author__" in data: # file is in Detectron2 model zoo format self.logger.info("Reading a file from '{}'".format(data["__author__"])) return data else: # assume file is from Caffe2 / Detectron1 model zoo if "blobs" in data: # Detection models have "blobs", but ImageNet models don't data = data["blobs"] data = {k: v for k, v in data.items() if not k.endswith("_momentum")} return {"model": data, "__author__": "Caffe2", "matching_heuristics": True} elif filename.endswith(".pyth"): # assume file is from pycls; no one else seems to use the ".pyth" extension with PathManager.open(filename, "rb") as f: data = torch.load(f) assert ( "model_state" in data ), f"Cannot load .pyth file {filename}; pycls checkpoints must contain 'model_state'." model_state = { k: v for k, v in data["model_state"].items() if not k.endswith("num_batches_tracked") } return {"model": model_state, "__author__": "pycls", "matching_heuristics": True} loaded = super()._load_file(filename) # load native pth checkpoint if "model" not in loaded: loaded = {"model": loaded} loaded["matching_heuristics"] = True return loaded def _load_model(self, checkpoint): if checkpoint.get("matching_heuristics", False): self._convert_ndarray_to_tensor(checkpoint["model"]) # convert weights by name-matching heuristics checkpoint["model"] = align_and_update_state_dicts( self.model.state_dict(), checkpoint["model"], c2_conversion=checkpoint.get("__author__", None) == "Caffe2", ) # for non-caffe2 models, use standard ways to load it incompatible = super()._load_model(checkpoint) model_buffers = dict(self.model.named_buffers(recurse=False)) for k in ["pixel_mean", "pixel_std"]: # Ignore missing key message about pixel_mean/std. # Though they may be missing in old checkpoints, they will be correctly # initialized from config anyway. if k in model_buffers: try: incompatible.missing_keys.remove(k) except ValueError: pass for k in incompatible.unexpected_keys[:]: # Ignore unexpected keys about cell anchors. They exist in old checkpoints # but now they are non-persistent buffers and will not be in new checkpoints. if "anchor_generator.cell_anchors" in k: incompatible.unexpected_keys.remove(k) return incompatible ================================================ FILE: detectron2/detectron2/config/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .compat import downgrade_config, upgrade_config from .config import CfgNode, get_cfg, global_cfg, set_global_cfg, configurable from .instantiate import instantiate from .lazy import LazyCall, LazyConfig __all__ = [ "CfgNode", "get_cfg", "global_cfg", "set_global_cfg", "downgrade_config", "upgrade_config", "configurable", "instantiate", "LazyCall", "LazyConfig", ] from detectron2.utils.env import fixup_module_metadata fixup_module_metadata(__name__, globals(), __all__) del fixup_module_metadata ================================================ FILE: detectron2/detectron2/config/compat.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. """ Backward compatibility of configs. Instructions to bump version: + It's not needed to bump version if new keys are added. It's only needed when backward-incompatible changes happen (i.e., some existing keys disappear, or the meaning of a key changes) + To bump version, do the following: 1. Increment _C.VERSION in defaults.py 2. Add a converter in this file. Each ConverterVX has a function "upgrade" which in-place upgrades config from X-1 to X, and a function "downgrade" which in-place downgrades config from X to X-1 In each function, VERSION is left unchanged. Each converter assumes that its input has the relevant keys (i.e., the input is not a partial config). 3. Run the tests (test_config.py) to make sure the upgrade & downgrade functions are consistent. """ import logging from typing import List, Optional, Tuple from .config import CfgNode as CN from .defaults import _C __all__ = ["upgrade_config", "downgrade_config"] def upgrade_config(cfg: CN, to_version: Optional[int] = None) -> CN: """ Upgrade a config from its current version to a newer version. Args: cfg (CfgNode): to_version (int): defaults to the latest version. """ cfg = cfg.clone() if to_version is None: to_version = _C.VERSION assert cfg.VERSION <= to_version, "Cannot upgrade from v{} to v{}!".format( cfg.VERSION, to_version ) for k in range(cfg.VERSION, to_version): converter = globals()["ConverterV" + str(k + 1)] converter.upgrade(cfg) cfg.VERSION = k + 1 return cfg def downgrade_config(cfg: CN, to_version: int) -> CN: """ Downgrade a config from its current version to an older version. Args: cfg (CfgNode): to_version (int): Note: A general downgrade of arbitrary configs is not always possible due to the different functionalities in different versions. The purpose of downgrade is only to recover the defaults in old versions, allowing it to load an old partial yaml config. Therefore, the implementation only needs to fill in the default values in the old version when a general downgrade is not possible. """ cfg = cfg.clone() assert cfg.VERSION >= to_version, "Cannot downgrade from v{} to v{}!".format( cfg.VERSION, to_version ) for k in range(cfg.VERSION, to_version, -1): converter = globals()["ConverterV" + str(k)] converter.downgrade(cfg) cfg.VERSION = k - 1 return cfg def guess_version(cfg: CN, filename: str) -> int: """ Guess the version of a partial config where the VERSION field is not specified. Returns the version, or the latest if cannot make a guess. This makes it easier for users to migrate. """ logger = logging.getLogger(__name__) def _has(name: str) -> bool: cur = cfg for n in name.split("."): if n not in cur: return False cur = cur[n] return True # Most users' partial configs have "MODEL.WEIGHT", so guess on it ret = None if _has("MODEL.WEIGHT") or _has("TEST.AUG_ON"): ret = 1 if ret is not None: logger.warning("Config '{}' has no VERSION. Assuming it to be v{}.".format(filename, ret)) else: ret = _C.VERSION logger.warning( "Config '{}' has no VERSION. Assuming it to be compatible with latest v{}.".format( filename, ret ) ) return ret def _rename(cfg: CN, old: str, new: str) -> None: old_keys = old.split(".") new_keys = new.split(".") def _set(key_seq: List[str], val: str) -> None: cur = cfg for k in key_seq[:-1]: if k not in cur: cur[k] = CN() cur = cur[k] cur[key_seq[-1]] = val def _get(key_seq: List[str]) -> CN: cur = cfg for k in key_seq: cur = cur[k] return cur def _del(key_seq: List[str]) -> None: cur = cfg for k in key_seq[:-1]: cur = cur[k] del cur[key_seq[-1]] if len(cur) == 0 and len(key_seq) > 1: _del(key_seq[:-1]) _set(new_keys, _get(old_keys)) _del(old_keys) class _RenameConverter: """ A converter that handles simple rename. """ RENAME: List[Tuple[str, str]] = [] # list of tuples of (old name, new name) @classmethod def upgrade(cls, cfg: CN) -> None: for old, new in cls.RENAME: _rename(cfg, old, new) @classmethod def downgrade(cls, cfg: CN) -> None: for old, new in cls.RENAME[::-1]: _rename(cfg, new, old) class ConverterV1(_RenameConverter): RENAME = [("MODEL.RPN_HEAD.NAME", "MODEL.RPN.HEAD_NAME")] class ConverterV2(_RenameConverter): """ A large bulk of rename, before public release. """ RENAME = [ ("MODEL.WEIGHT", "MODEL.WEIGHTS"), ("MODEL.PANOPTIC_FPN.SEMANTIC_LOSS_SCALE", "MODEL.SEM_SEG_HEAD.LOSS_WEIGHT"), ("MODEL.PANOPTIC_FPN.RPN_LOSS_SCALE", "MODEL.RPN.LOSS_WEIGHT"), ("MODEL.PANOPTIC_FPN.INSTANCE_LOSS_SCALE", "MODEL.PANOPTIC_FPN.INSTANCE_LOSS_WEIGHT"), ("MODEL.PANOPTIC_FPN.COMBINE_ON", "MODEL.PANOPTIC_FPN.COMBINE.ENABLED"), ( "MODEL.PANOPTIC_FPN.COMBINE_OVERLAP_THRESHOLD", "MODEL.PANOPTIC_FPN.COMBINE.OVERLAP_THRESH", ), ( "MODEL.PANOPTIC_FPN.COMBINE_STUFF_AREA_LIMIT", "MODEL.PANOPTIC_FPN.COMBINE.STUFF_AREA_LIMIT", ), ( "MODEL.PANOPTIC_FPN.COMBINE_INSTANCES_CONFIDENCE_THRESHOLD", "MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH", ), ("MODEL.ROI_HEADS.SCORE_THRESH", "MODEL.ROI_HEADS.SCORE_THRESH_TEST"), ("MODEL.ROI_HEADS.NMS", "MODEL.ROI_HEADS.NMS_THRESH_TEST"), ("MODEL.RETINANET.INFERENCE_SCORE_THRESHOLD", "MODEL.RETINANET.SCORE_THRESH_TEST"), ("MODEL.RETINANET.INFERENCE_TOPK_CANDIDATES", "MODEL.RETINANET.TOPK_CANDIDATES_TEST"), ("MODEL.RETINANET.INFERENCE_NMS_THRESHOLD", "MODEL.RETINANET.NMS_THRESH_TEST"), ("TEST.DETECTIONS_PER_IMG", "TEST.DETECTIONS_PER_IMAGE"), ("TEST.AUG_ON", "TEST.AUG.ENABLED"), ("TEST.AUG_MIN_SIZES", "TEST.AUG.MIN_SIZES"), ("TEST.AUG_MAX_SIZE", "TEST.AUG.MAX_SIZE"), ("TEST.AUG_FLIP", "TEST.AUG.FLIP"), ] @classmethod def upgrade(cls, cfg: CN) -> None: super().upgrade(cfg) if cfg.MODEL.META_ARCHITECTURE == "RetinaNet": _rename( cfg, "MODEL.RETINANET.ANCHOR_ASPECT_RATIOS", "MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS" ) _rename(cfg, "MODEL.RETINANET.ANCHOR_SIZES", "MODEL.ANCHOR_GENERATOR.SIZES") del cfg["MODEL"]["RPN"]["ANCHOR_SIZES"] del cfg["MODEL"]["RPN"]["ANCHOR_ASPECT_RATIOS"] else: _rename(cfg, "MODEL.RPN.ANCHOR_ASPECT_RATIOS", "MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS") _rename(cfg, "MODEL.RPN.ANCHOR_SIZES", "MODEL.ANCHOR_GENERATOR.SIZES") del cfg["MODEL"]["RETINANET"]["ANCHOR_SIZES"] del cfg["MODEL"]["RETINANET"]["ANCHOR_ASPECT_RATIOS"] del cfg["MODEL"]["RETINANET"]["ANCHOR_STRIDES"] @classmethod def downgrade(cls, cfg: CN) -> None: super().downgrade(cfg) _rename(cfg, "MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS", "MODEL.RPN.ANCHOR_ASPECT_RATIOS") _rename(cfg, "MODEL.ANCHOR_GENERATOR.SIZES", "MODEL.RPN.ANCHOR_SIZES") cfg.MODEL.RETINANET.ANCHOR_ASPECT_RATIOS = cfg.MODEL.RPN.ANCHOR_ASPECT_RATIOS cfg.MODEL.RETINANET.ANCHOR_SIZES = cfg.MODEL.RPN.ANCHOR_SIZES cfg.MODEL.RETINANET.ANCHOR_STRIDES = [] # this is not used anywhere in any version ================================================ FILE: detectron2/detectron2/config/config.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import functools import inspect import logging from fvcore.common.config import CfgNode as _CfgNode from detectron2.utils.file_io import PathManager class CfgNode(_CfgNode): """ The same as `fvcore.common.config.CfgNode`, but different in: 1. Use unsafe yaml loading by default. Note that this may lead to arbitrary code execution: you must not load a config file from untrusted sources before manually inspecting the content of the file. 2. Support config versioning. When attempting to merge an old config, it will convert the old config automatically. .. automethod:: clone .. automethod:: freeze .. automethod:: defrost .. automethod:: is_frozen .. automethod:: load_yaml_with_base .. automethod:: merge_from_list .. automethod:: merge_from_other_cfg """ @classmethod def _open_cfg(cls, filename): return PathManager.open(filename, "r") # Note that the default value of allow_unsafe is changed to True def merge_from_file(self, cfg_filename: str, allow_unsafe: bool = True) -> None: """ Load content from the given config file and merge it into self. Args: cfg_filename: config filename allow_unsafe: allow unsafe yaml syntax """ assert PathManager.isfile(cfg_filename), f"Config file '{cfg_filename}' does not exist!" loaded_cfg = self.load_yaml_with_base(cfg_filename, allow_unsafe=allow_unsafe) loaded_cfg = type(self)(loaded_cfg) # defaults.py needs to import CfgNode from .defaults import _C latest_ver = _C.VERSION assert ( latest_ver == self.VERSION ), "CfgNode.merge_from_file is only allowed on a config object of latest version!" logger = logging.getLogger(__name__) loaded_ver = loaded_cfg.get("VERSION", None) if loaded_ver is None: from .compat import guess_version loaded_ver = guess_version(loaded_cfg, cfg_filename) assert loaded_ver <= self.VERSION, "Cannot merge a v{} config into a v{} config.".format( loaded_ver, self.VERSION ) if loaded_ver == self.VERSION: self.merge_from_other_cfg(loaded_cfg) else: # compat.py needs to import CfgNode from .compat import upgrade_config, downgrade_config logger.warning( "Loading an old v{} config file '{}' by automatically upgrading to v{}. " "See docs/CHANGELOG.md for instructions to update your files.".format( loaded_ver, cfg_filename, self.VERSION ) ) # To convert, first obtain a full config at an old version old_self = downgrade_config(self, to_version=loaded_ver) old_self.merge_from_other_cfg(loaded_cfg) new_config = upgrade_config(old_self) self.clear() self.update(new_config) def dump(self, *args, **kwargs): """ Returns: str: a yaml string representation of the config """ # to make it show up in docs return super().dump(*args, **kwargs) global_cfg = CfgNode() def get_cfg() -> CfgNode: """ Get a copy of the default config. Returns: a detectron2 CfgNode instance. """ from .defaults import _C return _C.clone() def set_global_cfg(cfg: CfgNode) -> None: """ Let the global config point to the given cfg. Assume that the given "cfg" has the key "KEY", after calling `set_global_cfg(cfg)`, the key can be accessed by: :: from detectron2.config import global_cfg print(global_cfg.KEY) By using a hacky global config, you can access these configs anywhere, without having to pass the config object or the values deep into the code. This is a hacky feature introduced for quick prototyping / research exploration. """ global global_cfg global_cfg.clear() global_cfg.update(cfg) def configurable(init_func=None, *, from_config=None): """ Decorate a function or a class's __init__ method so that it can be called with a :class:`CfgNode` object using a :func:`from_config` function that translates :class:`CfgNode` to arguments. Examples: :: # Usage 1: Decorator on __init__: class A: @configurable def __init__(self, a, b=2, c=3): pass @classmethod def from_config(cls, cfg): # 'cfg' must be the first argument # Returns kwargs to be passed to __init__ return {"a": cfg.A, "b": cfg.B} a1 = A(a=1, b=2) # regular construction a2 = A(cfg) # construct with a cfg a3 = A(cfg, b=3, c=4) # construct with extra overwrite # Usage 2: Decorator on any function. Needs an extra from_config argument: @configurable(from_config=lambda cfg: {"a: cfg.A, "b": cfg.B}) def a_func(a, b=2, c=3): pass a1 = a_func(a=1, b=2) # regular call a2 = a_func(cfg) # call with a cfg a3 = a_func(cfg, b=3, c=4) # call with extra overwrite Args: init_func (callable): a class's ``__init__`` method in usage 1. The class must have a ``from_config`` classmethod which takes `cfg` as the first argument. from_config (callable): the from_config function in usage 2. It must take `cfg` as its first argument. """ if init_func is not None: assert ( inspect.isfunction(init_func) and from_config is None and init_func.__name__ == "__init__" ), "Incorrect use of @configurable. Check API documentation for examples." @functools.wraps(init_func) def wrapped(self, *args, **kwargs): try: from_config_func = type(self).from_config except AttributeError as e: raise AttributeError( "Class with @configurable must have a 'from_config' classmethod." ) from e if not inspect.ismethod(from_config_func): raise TypeError("Class with @configurable must have a 'from_config' classmethod.") if _called_with_cfg(*args, **kwargs): explicit_args = _get_args_from_config(from_config_func, *args, **kwargs) init_func(self, **explicit_args) else: init_func(self, *args, **kwargs) return wrapped else: if from_config is None: return configurable # @configurable() is made equivalent to @configurable assert inspect.isfunction( from_config ), "from_config argument of configurable must be a function!" def wrapper(orig_func): @functools.wraps(orig_func) def wrapped(*args, **kwargs): if _called_with_cfg(*args, **kwargs): explicit_args = _get_args_from_config(from_config, *args, **kwargs) return orig_func(**explicit_args) else: return orig_func(*args, **kwargs) wrapped.from_config = from_config return wrapped return wrapper def _get_args_from_config(from_config_func, *args, **kwargs): """ Use `from_config` to obtain explicit arguments. Returns: dict: arguments to be used for cls.__init__ """ signature = inspect.signature(from_config_func) if list(signature.parameters.keys())[0] != "cfg": if inspect.isfunction(from_config_func): name = from_config_func.__name__ else: name = f"{from_config_func.__self__}.from_config" raise TypeError(f"{name} must take 'cfg' as the first argument!") support_var_arg = any( param.kind in [param.VAR_POSITIONAL, param.VAR_KEYWORD] for param in signature.parameters.values() ) if support_var_arg: # forward all arguments to from_config, if from_config accepts them ret = from_config_func(*args, **kwargs) else: # forward supported arguments to from_config supported_arg_names = set(signature.parameters.keys()) extra_kwargs = {} for name in list(kwargs.keys()): if name not in supported_arg_names: extra_kwargs[name] = kwargs.pop(name) ret = from_config_func(*args, **kwargs) # forward the other arguments to __init__ ret.update(extra_kwargs) return ret def _called_with_cfg(*args, **kwargs): """ Returns: bool: whether the arguments contain CfgNode and should be considered forwarded to from_config. """ from omegaconf import DictConfig if len(args) and isinstance(args[0], (_CfgNode, DictConfig)): return True if isinstance(kwargs.pop("cfg", None), (_CfgNode, DictConfig)): return True # `from_config`'s first argument is forced to be "cfg". # So the above check covers all cases. return False ================================================ FILE: detectron2/detectron2/config/defaults.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .config import CfgNode as CN # NOTE: given the new config system # (https://detectron2.readthedocs.io/en/latest/tutorials/lazyconfigs.html), # we will stop adding new functionalities to default CfgNode. # ----------------------------------------------------------------------------- # Convention about Training / Test specific parameters # ----------------------------------------------------------------------------- # Whenever an argument can be either used for training or for testing, the # corresponding name will be post-fixed by a _TRAIN for a training parameter, # or _TEST for a test-specific parameter. # For example, the number of images during training will be # IMAGES_PER_BATCH_TRAIN, while the number of images for testing will be # IMAGES_PER_BATCH_TEST # ----------------------------------------------------------------------------- # Config definition # ----------------------------------------------------------------------------- _C = CN() # The version number, to upgrade from old configs to new ones if any # changes happen. It's recommended to keep a VERSION in your config file. _C.VERSION = 2 _C.MODEL = CN() _C.MODEL.LOAD_PROPOSALS = False _C.MODEL.MASK_ON = False _C.MODEL.KEYPOINT_ON = False _C.MODEL.DEVICE = "cuda" _C.MODEL.META_ARCHITECTURE = "GeneralizedRCNN" # Path (a file path, or URL like detectron2://.., https://..) to a checkpoint file # to be loaded to the model. You can find available models in the model zoo. _C.MODEL.WEIGHTS = "" # Values to be used for image normalization (BGR order, since INPUT.FORMAT defaults to BGR). # To train on images of different number of channels, just set different mean & std. # Default values are the mean pixel value from ImageNet: [103.53, 116.28, 123.675] _C.MODEL.PIXEL_MEAN = [103.530, 116.280, 123.675] # When using pre-trained models in Detectron1 or any MSRA models, # std has been absorbed into its conv1 weights, so the std needs to be set 1. # Otherwise, you can use [57.375, 57.120, 58.395] (ImageNet std) _C.MODEL.PIXEL_STD = [1.0, 1.0, 1.0] # ----------------------------------------------------------------------------- # INPUT # ----------------------------------------------------------------------------- _C.INPUT = CN() # By default, {MIN,MAX}_SIZE options are used in transforms.ResizeShortestEdge. # Please refer to ResizeShortestEdge for detailed definition. # Size of the smallest side of the image during training _C.INPUT.MIN_SIZE_TRAIN = (800,) # Sample size of smallest side by choice or random selection from range give by # INPUT.MIN_SIZE_TRAIN _C.INPUT.MIN_SIZE_TRAIN_SAMPLING = "choice" # Maximum size of the side of the image during training _C.INPUT.MAX_SIZE_TRAIN = 1333 # Size of the smallest side of the image during testing. Set to zero to disable resize in testing. _C.INPUT.MIN_SIZE_TEST = 800 # Maximum size of the side of the image during testing _C.INPUT.MAX_SIZE_TEST = 1333 # Mode for flipping images used in data augmentation during training # choose one of ["horizontal, "vertical", "none"] _C.INPUT.RANDOM_FLIP = "horizontal" # `True` if cropping is used for data augmentation during training _C.INPUT.CROP = CN({"ENABLED": False}) # Cropping type. See documentation of `detectron2.data.transforms.RandomCrop` for explanation. _C.INPUT.CROP.TYPE = "relative_range" # Size of crop in range (0, 1] if CROP.TYPE is "relative" or "relative_range" and in number of # pixels if CROP.TYPE is "absolute" _C.INPUT.CROP.SIZE = [0.9, 0.9] # Whether the model needs RGB, YUV, HSV etc. # Should be one of the modes defined here, as we use PIL to read the image: # https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-modes # with BGR being the one exception. One can set image format to BGR, we will # internally use RGB for conversion and flip the channels over _C.INPUT.FORMAT = "BGR" # The ground truth mask format that the model will use. # Mask R-CNN supports either "polygon" or "bitmask" as ground truth. _C.INPUT.MASK_FORMAT = "polygon" # alternative: "bitmask" # ----------------------------------------------------------------------------- # Dataset # ----------------------------------------------------------------------------- _C.DATASETS = CN() # List of the dataset names for training. Must be registered in DatasetCatalog # Samples from these datasets will be merged and used as one dataset. _C.DATASETS.TRAIN = () # List of the pre-computed proposal files for training, which must be consistent # with datasets listed in DATASETS.TRAIN. _C.DATASETS.PROPOSAL_FILES_TRAIN = () # Number of top scoring precomputed proposals to keep for training _C.DATASETS.PRECOMPUTED_PROPOSAL_TOPK_TRAIN = 2000 # List of the dataset names for testing. Must be registered in DatasetCatalog _C.DATASETS.TEST = () # List of the pre-computed proposal files for test, which must be consistent # with datasets listed in DATASETS.TEST. _C.DATASETS.PROPOSAL_FILES_TEST = () # Number of top scoring precomputed proposals to keep for test _C.DATASETS.PRECOMPUTED_PROPOSAL_TOPK_TEST = 1000 # ----------------------------------------------------------------------------- # DataLoader # ----------------------------------------------------------------------------- _C.DATALOADER = CN() # Number of data loading threads _C.DATALOADER.NUM_WORKERS = 4 # If True, each batch should contain only images for which the aspect ratio # is compatible. This groups portrait images together, and landscape images # are not batched with portrait images. _C.DATALOADER.ASPECT_RATIO_GROUPING = True # Options: TrainingSampler, RepeatFactorTrainingSampler _C.DATALOADER.SAMPLER_TRAIN = "TrainingSampler" # Repeat threshold for RepeatFactorTrainingSampler _C.DATALOADER.REPEAT_THRESHOLD = 0.0 # Tf True, when working on datasets that have instance annotations, the # training dataloader will filter out images without associated annotations _C.DATALOADER.FILTER_EMPTY_ANNOTATIONS = True # ---------------------------------------------------------------------------- # # Backbone options # ---------------------------------------------------------------------------- # _C.MODEL.BACKBONE = CN() _C.MODEL.BACKBONE.NAME = "build_resnet_backbone" # Freeze the first several stages so they are not trained. # There are 5 stages in ResNet. The first is a convolution, and the following # stages are each group of residual blocks. _C.MODEL.BACKBONE.FREEZE_AT = 2 # ---------------------------------------------------------------------------- # # FPN options # ---------------------------------------------------------------------------- # _C.MODEL.FPN = CN() # Names of the input feature maps to be used by FPN # They must have contiguous power of 2 strides # e.g., ["res2", "res3", "res4", "res5"] _C.MODEL.FPN.IN_FEATURES = [] _C.MODEL.FPN.OUT_CHANNELS = 256 # Options: "" (no norm), "GN" _C.MODEL.FPN.NORM = "" # Types for fusing the FPN top-down and lateral features. Can be either "sum" or "avg" _C.MODEL.FPN.FUSE_TYPE = "sum" # ---------------------------------------------------------------------------- # # Proposal generator options # ---------------------------------------------------------------------------- # _C.MODEL.PROPOSAL_GENERATOR = CN() # Current proposal generators include "RPN", "RRPN" and "PrecomputedProposals" _C.MODEL.PROPOSAL_GENERATOR.NAME = "RPN" # Proposal height and width both need to be greater than MIN_SIZE # (a the scale used during training or inference) _C.MODEL.PROPOSAL_GENERATOR.MIN_SIZE = 0 # ---------------------------------------------------------------------------- # # Anchor generator options # ---------------------------------------------------------------------------- # _C.MODEL.ANCHOR_GENERATOR = CN() # The generator can be any name in the ANCHOR_GENERATOR registry _C.MODEL.ANCHOR_GENERATOR.NAME = "DefaultAnchorGenerator" # Anchor sizes (i.e. sqrt of area) in absolute pixels w.r.t. the network input. # Format: list[list[float]]. SIZES[i] specifies the list of sizes to use for # IN_FEATURES[i]; len(SIZES) must be equal to len(IN_FEATURES) or 1. # When len(SIZES) == 1, SIZES[0] is used for all IN_FEATURES. _C.MODEL.ANCHOR_GENERATOR.SIZES = [[32, 64, 128, 256, 512]] # Anchor aspect ratios. For each area given in `SIZES`, anchors with different aspect # ratios are generated by an anchor generator. # Format: list[list[float]]. ASPECT_RATIOS[i] specifies the list of aspect ratios (H/W) # to use for IN_FEATURES[i]; len(ASPECT_RATIOS) == len(IN_FEATURES) must be true, # or len(ASPECT_RATIOS) == 1 is true and aspect ratio list ASPECT_RATIOS[0] is used # for all IN_FEATURES. _C.MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS = [[0.5, 1.0, 2.0]] # Anchor angles. # list[list[float]], the angle in degrees, for each input feature map. # ANGLES[i] specifies the list of angles for IN_FEATURES[i]. _C.MODEL.ANCHOR_GENERATOR.ANGLES = [[-90, 0, 90]] # Relative offset between the center of the first anchor and the top-left corner of the image # Value has to be in [0, 1). Recommend to use 0.5, which means half stride. # The value is not expected to affect model accuracy. _C.MODEL.ANCHOR_GENERATOR.OFFSET = 0.0 # ---------------------------------------------------------------------------- # # RPN options # ---------------------------------------------------------------------------- # _C.MODEL.RPN = CN() _C.MODEL.RPN.HEAD_NAME = "StandardRPNHead" # used by RPN_HEAD_REGISTRY # Names of the input feature maps to be used by RPN # e.g., ["p2", "p3", "p4", "p5", "p6"] for FPN _C.MODEL.RPN.IN_FEATURES = ["res4"] # Remove RPN anchors that go outside the image by BOUNDARY_THRESH pixels # Set to -1 or a large value, e.g. 100000, to disable pruning anchors _C.MODEL.RPN.BOUNDARY_THRESH = -1 # IOU overlap ratios [BG_IOU_THRESHOLD, FG_IOU_THRESHOLD] # Minimum overlap required between an anchor and ground-truth box for the # (anchor, gt box) pair to be a positive example (IoU >= FG_IOU_THRESHOLD # ==> positive RPN example: 1) # Maximum overlap allowed between an anchor and ground-truth box for the # (anchor, gt box) pair to be a negative examples (IoU < BG_IOU_THRESHOLD # ==> negative RPN example: 0) # Anchors with overlap in between (BG_IOU_THRESHOLD <= IoU < FG_IOU_THRESHOLD) # are ignored (-1) _C.MODEL.RPN.IOU_THRESHOLDS = [0.3, 0.7] _C.MODEL.RPN.IOU_LABELS = [0, -1, 1] # Number of regions per image used to train RPN _C.MODEL.RPN.BATCH_SIZE_PER_IMAGE = 256 # Target fraction of foreground (positive) examples per RPN minibatch _C.MODEL.RPN.POSITIVE_FRACTION = 0.5 # Options are: "smooth_l1", "giou", "diou", "ciou" _C.MODEL.RPN.BBOX_REG_LOSS_TYPE = "smooth_l1" _C.MODEL.RPN.BBOX_REG_LOSS_WEIGHT = 1.0 # Weights on (dx, dy, dw, dh) for normalizing RPN anchor regression targets _C.MODEL.RPN.BBOX_REG_WEIGHTS = (1.0, 1.0, 1.0, 1.0) # The transition point from L1 to L2 loss. Set to 0.0 to make the loss simply L1. _C.MODEL.RPN.SMOOTH_L1_BETA = 0.0 _C.MODEL.RPN.LOSS_WEIGHT = 1.0 # Number of top scoring RPN proposals to keep before applying NMS # When FPN is used, this is *per FPN level* (not total) _C.MODEL.RPN.PRE_NMS_TOPK_TRAIN = 12000 _C.MODEL.RPN.PRE_NMS_TOPK_TEST = 6000 # Number of top scoring RPN proposals to keep after applying NMS # When FPN is used, this limit is applied per level and then again to the union # of proposals from all levels # NOTE: When FPN is used, the meaning of this config is different from Detectron1. # It means per-batch topk in Detectron1, but per-image topk here. # See the "find_top_rpn_proposals" function for details. _C.MODEL.RPN.POST_NMS_TOPK_TRAIN = 2000 _C.MODEL.RPN.POST_NMS_TOPK_TEST = 1000 # NMS threshold used on RPN proposals _C.MODEL.RPN.NMS_THRESH = 0.7 # Set this to -1 to use the same number of output channels as input channels. _C.MODEL.RPN.CONV_DIMS = [-1] # ---------------------------------------------------------------------------- # # ROI HEADS options # ---------------------------------------------------------------------------- # _C.MODEL.ROI_HEADS = CN() _C.MODEL.ROI_HEADS.NAME = "Res5ROIHeads" # Number of foreground classes _C.MODEL.ROI_HEADS.NUM_CLASSES = 80 # Names of the input feature maps to be used by ROI heads # Currently all heads (box, mask, ...) use the same input feature map list # e.g., ["p2", "p3", "p4", "p5"] is commonly used for FPN _C.MODEL.ROI_HEADS.IN_FEATURES = ["res4"] # IOU overlap ratios [IOU_THRESHOLD] # Overlap threshold for an RoI to be considered background (if < IOU_THRESHOLD) # Overlap threshold for an RoI to be considered foreground (if >= IOU_THRESHOLD) _C.MODEL.ROI_HEADS.IOU_THRESHOLDS = [0.5] _C.MODEL.ROI_HEADS.IOU_LABELS = [0, 1] # RoI minibatch size *per image* (number of regions of interest [ROIs]) during training # Total number of RoIs per training minibatch = # ROI_HEADS.BATCH_SIZE_PER_IMAGE * SOLVER.IMS_PER_BATCH # E.g., a common configuration is: 512 * 16 = 8192 _C.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE = 512 # Target fraction of RoI minibatch that is labeled foreground (i.e. class > 0) _C.MODEL.ROI_HEADS.POSITIVE_FRACTION = 0.25 # Only used on test mode # Minimum score threshold (assuming scores in a [0, 1] range); a value chosen to # balance obtaining high recall with not having too many low precision # detections that will slow down inference post processing steps (like NMS) # A default threshold of 0.0 increases AP by ~0.2-0.3 but significantly slows down # inference. _C.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.05 # Overlap threshold used for non-maximum suppression (suppress boxes with # IoU >= this threshold) _C.MODEL.ROI_HEADS.NMS_THRESH_TEST = 0.5 # If True, augment proposals with ground-truth boxes before sampling proposals to # train ROI heads. _C.MODEL.ROI_HEADS.PROPOSAL_APPEND_GT = True # ---------------------------------------------------------------------------- # # Box Head # ---------------------------------------------------------------------------- # _C.MODEL.ROI_BOX_HEAD = CN() # C4 don't use head name option # Options for non-C4 models: FastRCNNConvFCHead, _C.MODEL.ROI_BOX_HEAD.NAME = "" # Options are: "smooth_l1", "giou", "diou", "ciou" _C.MODEL.ROI_BOX_HEAD.BBOX_REG_LOSS_TYPE = "smooth_l1" # The final scaling coefficient on the box regression loss, used to balance the magnitude of its # gradients with other losses in the model. See also `MODEL.ROI_KEYPOINT_HEAD.LOSS_WEIGHT`. _C.MODEL.ROI_BOX_HEAD.BBOX_REG_LOSS_WEIGHT = 1.0 # Default weights on (dx, dy, dw, dh) for normalizing bbox regression targets # These are empirically chosen to approximately lead to unit variance targets _C.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS = (10.0, 10.0, 5.0, 5.0) # The transition point from L1 to L2 loss. Set to 0.0 to make the loss simply L1. _C.MODEL.ROI_BOX_HEAD.SMOOTH_L1_BETA = 0.0 _C.MODEL.ROI_BOX_HEAD.POOLER_RESOLUTION = 14 _C.MODEL.ROI_BOX_HEAD.POOLER_SAMPLING_RATIO = 0 # Type of pooling operation applied to the incoming feature map for each RoI _C.MODEL.ROI_BOX_HEAD.POOLER_TYPE = "ROIAlignV2" _C.MODEL.ROI_BOX_HEAD.NUM_FC = 0 # Hidden layer dimension for FC layers in the RoI box head _C.MODEL.ROI_BOX_HEAD.FC_DIM = 1024 _C.MODEL.ROI_BOX_HEAD.NUM_CONV = 0 # Channel dimension for Conv layers in the RoI box head _C.MODEL.ROI_BOX_HEAD.CONV_DIM = 256 # Normalization method for the convolution layers. # Options: "" (no norm), "GN", "SyncBN". _C.MODEL.ROI_BOX_HEAD.NORM = "" # Whether to use class agnostic for bbox regression _C.MODEL.ROI_BOX_HEAD.CLS_AGNOSTIC_BBOX_REG = False # If true, RoI heads use bounding boxes predicted by the box head rather than proposal boxes. _C.MODEL.ROI_BOX_HEAD.TRAIN_ON_PRED_BOXES = False # Federated loss can be used to improve the training of LVIS _C.MODEL.ROI_BOX_HEAD.USE_FED_LOSS = False # Sigmoid cross entrophy is used with federated loss _C.MODEL.ROI_BOX_HEAD.USE_SIGMOID_CE = False # The power value applied to image_count when calcualting frequency weight _C.MODEL.ROI_BOX_HEAD.FED_LOSS_FREQ_WEIGHT_POWER = 0.5 # Number of classes to keep in total _C.MODEL.ROI_BOX_HEAD.FED_LOSS_NUM_CLASSES = 50 # ---------------------------------------------------------------------------- # # Cascaded Box Head # ---------------------------------------------------------------------------- # _C.MODEL.ROI_BOX_CASCADE_HEAD = CN() # The number of cascade stages is implicitly defined by the length of the following two configs. _C.MODEL.ROI_BOX_CASCADE_HEAD.BBOX_REG_WEIGHTS = ( (10.0, 10.0, 5.0, 5.0), (20.0, 20.0, 10.0, 10.0), (30.0, 30.0, 15.0, 15.0), ) _C.MODEL.ROI_BOX_CASCADE_HEAD.IOUS = (0.5, 0.6, 0.7) # ---------------------------------------------------------------------------- # # Mask Head # ---------------------------------------------------------------------------- # _C.MODEL.ROI_MASK_HEAD = CN() _C.MODEL.ROI_MASK_HEAD.NAME = "MaskRCNNConvUpsampleHead" _C.MODEL.ROI_MASK_HEAD.POOLER_RESOLUTION = 14 _C.MODEL.ROI_MASK_HEAD.POOLER_SAMPLING_RATIO = 0 _C.MODEL.ROI_MASK_HEAD.NUM_CONV = 0 # The number of convs in the mask head _C.MODEL.ROI_MASK_HEAD.CONV_DIM = 256 # Normalization method for the convolution layers. # Options: "" (no norm), "GN", "SyncBN". _C.MODEL.ROI_MASK_HEAD.NORM = "" # Whether to use class agnostic for mask prediction _C.MODEL.ROI_MASK_HEAD.CLS_AGNOSTIC_MASK = False # Type of pooling operation applied to the incoming feature map for each RoI _C.MODEL.ROI_MASK_HEAD.POOLER_TYPE = "ROIAlignV2" # ---------------------------------------------------------------------------- # # Keypoint Head # ---------------------------------------------------------------------------- # _C.MODEL.ROI_KEYPOINT_HEAD = CN() _C.MODEL.ROI_KEYPOINT_HEAD.NAME = "KRCNNConvDeconvUpsampleHead" _C.MODEL.ROI_KEYPOINT_HEAD.POOLER_RESOLUTION = 14 _C.MODEL.ROI_KEYPOINT_HEAD.POOLER_SAMPLING_RATIO = 0 _C.MODEL.ROI_KEYPOINT_HEAD.CONV_DIMS = tuple(512 for _ in range(8)) _C.MODEL.ROI_KEYPOINT_HEAD.NUM_KEYPOINTS = 17 # 17 is the number of keypoints in COCO. # Images with too few (or no) keypoints are excluded from training. _C.MODEL.ROI_KEYPOINT_HEAD.MIN_KEYPOINTS_PER_IMAGE = 1 # Normalize by the total number of visible keypoints in the minibatch if True. # Otherwise, normalize by the total number of keypoints that could ever exist # in the minibatch. # The keypoint softmax loss is only calculated on visible keypoints. # Since the number of visible keypoints can vary significantly between # minibatches, this has the effect of up-weighting the importance of # minibatches with few visible keypoints. (Imagine the extreme case of # only one visible keypoint versus N: in the case of N, each one # contributes 1/N to the gradient compared to the single keypoint # determining the gradient direction). Instead, we can normalize the # loss by the total number of keypoints, if it were the case that all # keypoints were visible in a full minibatch. (Returning to the example, # this means that the one visible keypoint contributes as much as each # of the N keypoints.) _C.MODEL.ROI_KEYPOINT_HEAD.NORMALIZE_LOSS_BY_VISIBLE_KEYPOINTS = True # Multi-task loss weight to use for keypoints # Recommended values: # - use 1.0 if NORMALIZE_LOSS_BY_VISIBLE_KEYPOINTS is True # - use 4.0 if NORMALIZE_LOSS_BY_VISIBLE_KEYPOINTS is False _C.MODEL.ROI_KEYPOINT_HEAD.LOSS_WEIGHT = 1.0 # Type of pooling operation applied to the incoming feature map for each RoI _C.MODEL.ROI_KEYPOINT_HEAD.POOLER_TYPE = "ROIAlignV2" # ---------------------------------------------------------------------------- # # Semantic Segmentation Head # ---------------------------------------------------------------------------- # _C.MODEL.SEM_SEG_HEAD = CN() _C.MODEL.SEM_SEG_HEAD.NAME = "SemSegFPNHead" _C.MODEL.SEM_SEG_HEAD.IN_FEATURES = ["p2", "p3", "p4", "p5"] # Label in the semantic segmentation ground truth that is ignored, i.e., no loss is calculated for # the correposnding pixel. _C.MODEL.SEM_SEG_HEAD.IGNORE_VALUE = 255 # Number of classes in the semantic segmentation head _C.MODEL.SEM_SEG_HEAD.NUM_CLASSES = 54 # Number of channels in the 3x3 convs inside semantic-FPN heads. _C.MODEL.SEM_SEG_HEAD.CONVS_DIM = 128 # Outputs from semantic-FPN heads are up-scaled to the COMMON_STRIDE stride. _C.MODEL.SEM_SEG_HEAD.COMMON_STRIDE = 4 # Normalization method for the convolution layers. Options: "" (no norm), "GN". _C.MODEL.SEM_SEG_HEAD.NORM = "GN" _C.MODEL.SEM_SEG_HEAD.LOSS_WEIGHT = 1.0 _C.MODEL.PANOPTIC_FPN = CN() # Scaling of all losses from instance detection / segmentation head. _C.MODEL.PANOPTIC_FPN.INSTANCE_LOSS_WEIGHT = 1.0 # options when combining instance & semantic segmentation outputs _C.MODEL.PANOPTIC_FPN.COMBINE = CN({"ENABLED": True}) # "COMBINE.ENABLED" is deprecated & not used _C.MODEL.PANOPTIC_FPN.COMBINE.OVERLAP_THRESH = 0.5 _C.MODEL.PANOPTIC_FPN.COMBINE.STUFF_AREA_LIMIT = 4096 _C.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH = 0.5 # ---------------------------------------------------------------------------- # # RetinaNet Head # ---------------------------------------------------------------------------- # _C.MODEL.RETINANET = CN() # This is the number of foreground classes. _C.MODEL.RETINANET.NUM_CLASSES = 80 _C.MODEL.RETINANET.IN_FEATURES = ["p3", "p4", "p5", "p6", "p7"] # Convolutions to use in the cls and bbox tower # NOTE: this doesn't include the last conv for logits _C.MODEL.RETINANET.NUM_CONVS = 4 # IoU overlap ratio [bg, fg] for labeling anchors. # Anchors with < bg are labeled negative (0) # Anchors with >= bg and < fg are ignored (-1) # Anchors with >= fg are labeled positive (1) _C.MODEL.RETINANET.IOU_THRESHOLDS = [0.4, 0.5] _C.MODEL.RETINANET.IOU_LABELS = [0, -1, 1] # Prior prob for rare case (i.e. foreground) at the beginning of training. # This is used to set the bias for the logits layer of the classifier subnet. # This improves training stability in the case of heavy class imbalance. _C.MODEL.RETINANET.PRIOR_PROB = 0.01 # Inference cls score threshold, only anchors with score > INFERENCE_TH are # considered for inference (to improve speed) _C.MODEL.RETINANET.SCORE_THRESH_TEST = 0.05 # Select topk candidates before NMS _C.MODEL.RETINANET.TOPK_CANDIDATES_TEST = 1000 _C.MODEL.RETINANET.NMS_THRESH_TEST = 0.5 # Weights on (dx, dy, dw, dh) for normalizing Retinanet anchor regression targets _C.MODEL.RETINANET.BBOX_REG_WEIGHTS = (1.0, 1.0, 1.0, 1.0) # Loss parameters _C.MODEL.RETINANET.FOCAL_LOSS_GAMMA = 2.0 _C.MODEL.RETINANET.FOCAL_LOSS_ALPHA = 0.25 _C.MODEL.RETINANET.SMOOTH_L1_LOSS_BETA = 0.1 # Options are: "smooth_l1", "giou", "diou", "ciou" _C.MODEL.RETINANET.BBOX_REG_LOSS_TYPE = "smooth_l1" # One of BN, SyncBN, FrozenBN, GN # Only supports GN until unshared norm is implemented _C.MODEL.RETINANET.NORM = "" # ---------------------------------------------------------------------------- # # ResNe[X]t options (ResNets = {ResNet, ResNeXt} # Note that parts of a resnet may be used for both the backbone and the head # These options apply to both # ---------------------------------------------------------------------------- # _C.MODEL.RESNETS = CN() _C.MODEL.RESNETS.DEPTH = 50 _C.MODEL.RESNETS.OUT_FEATURES = ["res4"] # res4 for C4 backbone, res2..5 for FPN backbone # Number of groups to use; 1 ==> ResNet; > 1 ==> ResNeXt _C.MODEL.RESNETS.NUM_GROUPS = 1 # Options: FrozenBN, GN, "SyncBN", "BN" _C.MODEL.RESNETS.NORM = "FrozenBN" # Baseline width of each group. # Scaling this parameters will scale the width of all bottleneck layers. _C.MODEL.RESNETS.WIDTH_PER_GROUP = 64 # Place the stride 2 conv on the 1x1 filter # Use True only for the original MSRA ResNet; use False for C2 and Torch models _C.MODEL.RESNETS.STRIDE_IN_1X1 = True # Apply dilation in stage "res5" _C.MODEL.RESNETS.RES5_DILATION = 1 # Output width of res2. Scaling this parameters will scale the width of all 1x1 convs in ResNet # For R18 and R34, this needs to be set to 64 _C.MODEL.RESNETS.RES2_OUT_CHANNELS = 256 _C.MODEL.RESNETS.STEM_OUT_CHANNELS = 64 # Apply Deformable Convolution in stages # Specify if apply deform_conv on Res2, Res3, Res4, Res5 _C.MODEL.RESNETS.DEFORM_ON_PER_STAGE = [False, False, False, False] # Use True to use modulated deform_conv (DeformableV2, https://arxiv.org/abs/1811.11168); # Use False for DeformableV1. _C.MODEL.RESNETS.DEFORM_MODULATED = False # Number of groups in deformable conv. _C.MODEL.RESNETS.DEFORM_NUM_GROUPS = 1 # ---------------------------------------------------------------------------- # # Solver # ---------------------------------------------------------------------------- # _C.SOLVER = CN() # Options: WarmupMultiStepLR, WarmupCosineLR. # See detectron2/solver/build.py for definition. _C.SOLVER.LR_SCHEDULER_NAME = "WarmupMultiStepLR" _C.SOLVER.MAX_ITER = 40000 _C.SOLVER.BASE_LR = 0.001 # The end lr, only used by WarmupCosineLR _C.SOLVER.BASE_LR_END = 0.0 _C.SOLVER.MOMENTUM = 0.9 _C.SOLVER.NESTEROV = False _C.SOLVER.WEIGHT_DECAY = 0.0001 # The weight decay that's applied to parameters of normalization layers # (typically the affine transformation) _C.SOLVER.WEIGHT_DECAY_NORM = 0.0 _C.SOLVER.GAMMA = 0.1 # The iteration number to decrease learning rate by GAMMA. _C.SOLVER.STEPS = (30000,) _C.SOLVER.WARMUP_FACTOR = 1.0 / 1000 _C.SOLVER.WARMUP_ITERS = 1000 _C.SOLVER.WARMUP_METHOD = "linear" # Save a checkpoint after every this number of iterations _C.SOLVER.CHECKPOINT_PERIOD = 5000 # Number of images per batch across all machines. This is also the number # of training images per step (i.e. per iteration). If we use 16 GPUs # and IMS_PER_BATCH = 32, each GPU will see 2 images per batch. # May be adjusted automatically if REFERENCE_WORLD_SIZE is set. _C.SOLVER.IMS_PER_BATCH = 16 # The reference number of workers (GPUs) this config is meant to train with. # It takes no effect when set to 0. # With a non-zero value, it will be used by DefaultTrainer to compute a desired # per-worker batch size, and then scale the other related configs (total batch size, # learning rate, etc) to match the per-worker batch size. # See documentation of `DefaultTrainer.auto_scale_workers` for details: _C.SOLVER.REFERENCE_WORLD_SIZE = 0 # Detectron v1 (and previous detection code) used a 2x higher LR and 0 WD for # biases. This is not useful (at least for recent models). You should avoid # changing these and they exist only to reproduce Detectron v1 training if # desired. _C.SOLVER.BIAS_LR_FACTOR = 1.0 _C.SOLVER.WEIGHT_DECAY_BIAS = None # None means following WEIGHT_DECAY # Gradient clipping _C.SOLVER.CLIP_GRADIENTS = CN({"ENABLED": False}) # Type of gradient clipping, currently 2 values are supported: # - "value": the absolute values of elements of each gradients are clipped # - "norm": the norm of the gradient for each parameter is clipped thus # affecting all elements in the parameter _C.SOLVER.CLIP_GRADIENTS.CLIP_TYPE = "value" # Maximum absolute value used for clipping gradients _C.SOLVER.CLIP_GRADIENTS.CLIP_VALUE = 1.0 # Floating point number p for L-p norm to be used with the "norm" # gradient clipping type; for L-inf, please specify .inf _C.SOLVER.CLIP_GRADIENTS.NORM_TYPE = 2.0 # Enable automatic mixed precision for training # Note that this does not change model's inference behavior. # To use AMP in inference, run inference under autocast() _C.SOLVER.AMP = CN({"ENABLED": False}) # ---------------------------------------------------------------------------- # # Specific test options # ---------------------------------------------------------------------------- # _C.TEST = CN() # For end-to-end tests to verify the expected accuracy. # Each item is [task, metric, value, tolerance] # e.g.: [['bbox', 'AP', 38.5, 0.2]] _C.TEST.EXPECTED_RESULTS = [] # The period (in terms of steps) to evaluate the model during training. # Set to 0 to disable. _C.TEST.EVAL_PERIOD = 0 # The sigmas used to calculate keypoint OKS. See http://cocodataset.org/#keypoints-eval # When empty, it will use the defaults in COCO. # Otherwise it should be a list[float] with the same length as ROI_KEYPOINT_HEAD.NUM_KEYPOINTS. _C.TEST.KEYPOINT_OKS_SIGMAS = [] # Maximum number of detections to return per image during inference (100 is # based on the limit established for the COCO dataset). _C.TEST.DETECTIONS_PER_IMAGE = 100 _C.TEST.AUG = CN({"ENABLED": False}) _C.TEST.AUG.MIN_SIZES = (400, 500, 600, 700, 800, 900, 1000, 1100, 1200) _C.TEST.AUG.MAX_SIZE = 4000 _C.TEST.AUG.FLIP = True _C.TEST.PRECISE_BN = CN({"ENABLED": False}) _C.TEST.PRECISE_BN.NUM_ITER = 200 # ---------------------------------------------------------------------------- # # Misc options # ---------------------------------------------------------------------------- # # Directory where output files are written _C.OUTPUT_DIR = "./output" # Set seed to negative to fully randomize everything. # Set seed to positive to use a fixed seed. Note that a fixed seed increases # reproducibility but does not guarantee fully deterministic behavior. # Disabling all parallelism further increases reproducibility. _C.SEED = -1 # Benchmark different cudnn algorithms. # If input images have very different sizes, this option will have large overhead # for about 10k iterations. It usually hurts total time, but can benefit for certain models. # If input images have the same or similar sizes, benchmark is often helpful. _C.CUDNN_BENCHMARK = False # The period (in terms of steps) for minibatch visualization at train time. # Set to 0 to disable. _C.VIS_PERIOD = 0 # global config is for quick hack purposes. # You can set them in command line or config files, # and access it with: # # from detectron2.config import global_cfg # print(global_cfg.HACK) # # Do not commit any configs into it. _C.GLOBAL = CN() _C.GLOBAL.HACK = 1.0 ================================================ FILE: detectron2/detectron2/config/instantiate.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import collections.abc as abc import dataclasses import logging from typing import Any from detectron2.utils.registry import _convert_target_to_string, locate __all__ = ["dump_dataclass", "instantiate"] def dump_dataclass(obj: Any): """ Dump a dataclass recursively into a dict that can be later instantiated. Args: obj: a dataclass object Returns: dict """ assert dataclasses.is_dataclass(obj) and not isinstance( obj, type ), "dump_dataclass() requires an instance of a dataclass." ret = {"_target_": _convert_target_to_string(type(obj))} for f in dataclasses.fields(obj): v = getattr(obj, f.name) if dataclasses.is_dataclass(v): v = dump_dataclass(v) if isinstance(v, (list, tuple)): v = [dump_dataclass(x) if dataclasses.is_dataclass(x) else x for x in v] ret[f.name] = v return ret def instantiate(cfg): """ Recursively instantiate objects defined in dictionaries by "_target_" and arguments. Args: cfg: a dict-like object with "_target_" that defines the caller, and other keys that define the arguments Returns: object instantiated by cfg """ from omegaconf import ListConfig, DictConfig, OmegaConf if isinstance(cfg, ListConfig): lst = [instantiate(x) for x in cfg] return ListConfig(lst, flags={"allow_objects": True}) if isinstance(cfg, list): # Specialize for list, because many classes take # list[objects] as arguments, such as ResNet, DatasetMapper return [instantiate(x) for x in cfg] # If input is a DictConfig backed by dataclasses (i.e. omegaconf's structured config), # instantiate it to the actual dataclass. if isinstance(cfg, DictConfig) and dataclasses.is_dataclass(cfg._metadata.object_type): return OmegaConf.to_object(cfg) if isinstance(cfg, abc.Mapping) and "_target_" in cfg: # conceptually equivalent to hydra.utils.instantiate(cfg) with _convert_=all, # but faster: https://github.com/facebookresearch/hydra/issues/1200 cfg = {k: instantiate(v) for k, v in cfg.items()} cls = cfg.pop("_target_") cls = instantiate(cls) if isinstance(cls, str): cls_name = cls cls = locate(cls_name) assert cls is not None, cls_name else: try: cls_name = cls.__module__ + "." + cls.__qualname__ except Exception: # target could be anything, so the above could fail cls_name = str(cls) assert callable(cls), f"_target_ {cls} does not define a callable object" try: return cls(**cfg) except TypeError: logger = logging.getLogger(__name__) logger.error(f"Error when instantiating {cls_name}!") raise return cfg # return as-is if don't know what to do ================================================ FILE: detectron2/detectron2/config/lazy.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import ast import builtins import collections.abc as abc import importlib import inspect import logging import os import uuid from contextlib import contextmanager from copy import deepcopy from dataclasses import is_dataclass from typing import List, Tuple, Union import cloudpickle import yaml from omegaconf import DictConfig, ListConfig, OmegaConf, SCMode from detectron2.utils.file_io import PathManager from detectron2.utils.registry import _convert_target_to_string __all__ = ["LazyCall", "LazyConfig"] class LazyCall: """ Wrap a callable so that when it's called, the call will not be executed, but returns a dict that describes the call. LazyCall object has to be called with only keyword arguments. Positional arguments are not yet supported. Examples: :: from detectron2.config import instantiate, LazyCall layer_cfg = LazyCall(nn.Conv2d)(in_channels=32, out_channels=32) layer_cfg.out_channels = 64 # can edit it afterwards layer = instantiate(layer_cfg) """ def __init__(self, target): if not (callable(target) or isinstance(target, (str, abc.Mapping))): raise TypeError( f"target of LazyCall must be a callable or defines a callable! Got {target}" ) self._target = target def __call__(self, **kwargs): if is_dataclass(self._target): # omegaconf object cannot hold dataclass type # https://github.com/omry/omegaconf/issues/784 target = _convert_target_to_string(self._target) else: target = self._target kwargs["_target_"] = target return DictConfig(content=kwargs, flags={"allow_objects": True}) def _visit_dict_config(cfg, func): """ Apply func recursively to all DictConfig in cfg. """ if isinstance(cfg, DictConfig): func(cfg) for v in cfg.values(): _visit_dict_config(v, func) elif isinstance(cfg, ListConfig): for v in cfg: _visit_dict_config(v, func) def _validate_py_syntax(filename): # see also https://github.com/open-mmlab/mmcv/blob/master/mmcv/utils/config.py with PathManager.open(filename, "r") as f: content = f.read() try: ast.parse(content) except SyntaxError as e: raise SyntaxError(f"Config file {filename} has syntax error!") from e def _cast_to_config(obj): # if given a dict, return DictConfig instead if isinstance(obj, dict): return DictConfig(obj, flags={"allow_objects": True}) return obj _CFG_PACKAGE_NAME = "detectron2._cfg_loader" """ A namespace to put all imported config into. """ def _random_package_name(filename): # generate a random package name when loading config files return _CFG_PACKAGE_NAME + str(uuid.uuid4())[:4] + "." + os.path.basename(filename) @contextmanager def _patch_import(): """ Enhance relative import statements in config files, so that they: 1. locate files purely based on relative location, regardless of packages. e.g. you can import file without having __init__ 2. do not cache modules globally; modifications of module states has no side effect 3. support other storage system through PathManager 4. imported dict are turned into omegaconf.DictConfig automatically """ old_import = builtins.__import__ def find_relative_file(original_file, relative_import_path, level): cur_file = os.path.dirname(original_file) for _ in range(level - 1): cur_file = os.path.dirname(cur_file) cur_name = relative_import_path.lstrip(".") for part in cur_name.split("."): cur_file = os.path.join(cur_file, part) # NOTE: directory import is not handled. Because then it's unclear # if such import should produce python module or DictConfig. This can # be discussed further if needed. if not cur_file.endswith(".py"): cur_file += ".py" if not PathManager.isfile(cur_file): raise ImportError( f"Cannot import name {relative_import_path} from " f"{original_file}: {cur_file} has to exist." ) return cur_file def new_import(name, globals=None, locals=None, fromlist=(), level=0): if ( # Only deal with relative imports inside config files level != 0 and globals is not None and (globals.get("__package__", "") or "").startswith(_CFG_PACKAGE_NAME) ): cur_file = find_relative_file(globals["__file__"], name, level) _validate_py_syntax(cur_file) spec = importlib.machinery.ModuleSpec( _random_package_name(cur_file), None, origin=cur_file ) module = importlib.util.module_from_spec(spec) module.__file__ = cur_file with PathManager.open(cur_file) as f: content = f.read() exec(compile(content, cur_file, "exec"), module.__dict__) for name in fromlist: # turn imported dict into DictConfig automatically val = _cast_to_config(module.__dict__[name]) module.__dict__[name] = val return module return old_import(name, globals, locals, fromlist=fromlist, level=level) builtins.__import__ = new_import yield new_import builtins.__import__ = old_import class LazyConfig: """ Provide methods to save, load, and overrides an omegaconf config object which may contain definition of lazily-constructed objects. """ @staticmethod def load_rel(filename: str, keys: Union[None, str, Tuple[str, ...]] = None): """ Similar to :meth:`load()`, but load path relative to the caller's source file. This has the same functionality as a relative import, except that this method accepts filename as a string, so more characters are allowed in the filename. """ caller_frame = inspect.stack()[1] caller_fname = caller_frame[0].f_code.co_filename assert caller_fname != "", "load_rel Unable to find caller" caller_dir = os.path.dirname(caller_fname) filename = os.path.join(caller_dir, filename) return LazyConfig.load(filename, keys) @staticmethod def load(filename: str, keys: Union[None, str, Tuple[str, ...]] = None): """ Load a config file. Args: filename: absolute path or relative path w.r.t. the current working directory keys: keys to load and return. If not given, return all keys (whose values are config objects) in a dict. """ has_keys = keys is not None filename = filename.replace("/./", "/") # redundant if os.path.splitext(filename)[1] not in [".py", ".yaml", ".yml"]: raise ValueError(f"Config file {filename} has to be a python or yaml file.") if filename.endswith(".py"): _validate_py_syntax(filename) with _patch_import(): # Record the filename module_namespace = { "__file__": filename, "__package__": _random_package_name(filename), } with PathManager.open(filename) as f: content = f.read() # Compile first with filename to: # 1. make filename appears in stacktrace # 2. make load_rel able to find its parent's (possibly remote) location exec(compile(content, filename, "exec"), module_namespace) ret = module_namespace else: with PathManager.open(filename) as f: obj = yaml.unsafe_load(f) ret = OmegaConf.create(obj, flags={"allow_objects": True}) if has_keys: if isinstance(keys, str): return _cast_to_config(ret[keys]) else: return tuple(_cast_to_config(ret[a]) for a in keys) else: if filename.endswith(".py"): # when not specified, only load those that are config objects ret = DictConfig( { name: _cast_to_config(value) for name, value in ret.items() if isinstance(value, (DictConfig, ListConfig, dict)) and not name.startswith("_") }, flags={"allow_objects": True}, ) return ret @staticmethod def save(cfg, filename: str): """ Save a config object to a yaml file. Note that when the config dictionary contains complex objects (e.g. lambda), it can't be saved to yaml. In that case we will print an error and attempt to save to a pkl file instead. Args: cfg: an omegaconf config object filename: yaml file name to save the config file """ logger = logging.getLogger(__name__) try: cfg = deepcopy(cfg) except Exception: pass else: # if it's deep-copyable, then... def _replace_type_by_name(x): if "_target_" in x and callable(x._target_): try: x._target_ = _convert_target_to_string(x._target_) except AttributeError: pass # not necessary, but makes yaml looks nicer _visit_dict_config(cfg, _replace_type_by_name) save_pkl = False try: dict = OmegaConf.to_container( cfg, # Do not resolve interpolation when saving, i.e. do not turn ${a} into # actual values when saving. resolve=False, # Save structures (dataclasses) in a format that can be instantiated later. # Without this option, the type information of the dataclass will be erased. structured_config_mode=SCMode.INSTANTIATE, ) dumped = yaml.dump(dict, default_flow_style=None, allow_unicode=True, width=9999) with PathManager.open(filename, "w") as f: f.write(dumped) try: _ = yaml.unsafe_load(dumped) # test that it is loadable except Exception: logger.warning( "The config contains objects that cannot serialize to a valid yaml. " f"{filename} is human-readable but cannot be loaded." ) save_pkl = True except Exception: logger.exception("Unable to serialize the config to yaml. Error:") save_pkl = True if save_pkl: new_filename = filename + ".pkl" try: # retry by pickle with PathManager.open(new_filename, "wb") as f: cloudpickle.dump(cfg, f) logger.warning(f"Config is saved using cloudpickle at {new_filename}.") except Exception: pass @staticmethod def apply_overrides(cfg, overrides: List[str]): """ In-place override contents of cfg. Args: cfg: an omegaconf config object overrides: list of strings in the format of "a=b" to override configs. See https://hydra.cc/docs/next/advanced/override_grammar/basic/ for syntax. Returns: the cfg object """ def safe_update(cfg, key, value): parts = key.split(".") for idx in range(1, len(parts)): prefix = ".".join(parts[:idx]) v = OmegaConf.select(cfg, prefix, default=None) if v is None: break if not OmegaConf.is_config(v): raise KeyError( f"Trying to update key {key}, but {prefix} " f"is not a config, but has type {type(v)}." ) OmegaConf.update(cfg, key, value, merge=True) from hydra.core.override_parser.overrides_parser import OverridesParser parser = OverridesParser.create() overrides = parser.parse_overrides(overrides) for o in overrides: key = o.key_or_group value = o.value() if o.is_delete(): # TODO support this raise NotImplementedError("deletion is not yet a supported override") safe_update(cfg, key, value) return cfg @staticmethod def to_py(cfg, prefix: str = "cfg."): """ Try to convert a config object into Python-like psuedo code. Note that perfect conversion is not always possible. So the returned results are mainly meant to be human-readable, and not meant to be executed. Args: cfg: an omegaconf config object prefix: root name for the resulting code (default: "cfg.") Returns: str of formatted Python code """ import black cfg = OmegaConf.to_container(cfg, resolve=True) def _to_str(obj, prefix=None, inside_call=False): if prefix is None: prefix = [] if isinstance(obj, abc.Mapping) and "_target_" in obj: # Dict representing a function call target = _convert_target_to_string(obj.pop("_target_")) args = [] for k, v in sorted(obj.items()): args.append(f"{k}={_to_str(v, inside_call=True)}") args = ", ".join(args) call = f"{target}({args})" return "".join(prefix) + call elif isinstance(obj, abc.Mapping) and not inside_call: # Dict that is not inside a call is a list of top-level config objects that we # render as one object per line with dot separated prefixes key_list = [] for k, v in sorted(obj.items()): if isinstance(v, abc.Mapping) and "_target_" not in v: key_list.append(_to_str(v, prefix=prefix + [k + "."])) else: key = "".join(prefix) + k key_list.append(f"{key}={_to_str(v)}") return "\n".join(key_list) elif isinstance(obj, abc.Mapping): # Dict that is inside a call is rendered as a regular dict return ( "{" + ",".join( f"{repr(k)}: {_to_str(v, inside_call=inside_call)}" for k, v in sorted(obj.items()) ) + "}" ) elif isinstance(obj, list): return "[" + ",".join(_to_str(x, inside_call=inside_call) for x in obj) + "]" else: return repr(obj) py_str = _to_str(cfg, prefix=[prefix]) try: return black.format_str(py_str, mode=black.Mode()) except black.InvalidInput: return py_str ================================================ FILE: detectron2/detectron2/data/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from . import transforms # isort:skip from .build import ( build_batch_data_loader, build_detection_test_loader, build_detection_train_loader, get_detection_dataset_dicts, load_proposals_into_dataset, print_instances_class_histogram, ) from .catalog import DatasetCatalog, MetadataCatalog, Metadata from .common import DatasetFromList, MapDataset, ToIterableDataset from .dataset_mapper import DatasetMapper # ensure the builtin datasets are registered from . import datasets, samplers # isort:skip __all__ = [k for k in globals().keys() if not k.startswith("_")] ================================================ FILE: detectron2/detectron2/data/benchmark.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np from itertools import count from typing import List, Tuple import torch import tqdm from fvcore.common.timer import Timer from detectron2.utils import comm from .build import build_batch_data_loader from .common import DatasetFromList, MapDataset from .samplers import TrainingSampler logger = logging.getLogger(__name__) class _EmptyMapDataset(torch.utils.data.Dataset): """ Map anything to emptiness. """ def __init__(self, dataset): self.ds = dataset def __len__(self): return len(self.ds) def __getitem__(self, idx): _ = self.ds[idx] return [0] def iter_benchmark( iterator, num_iter: int, warmup: int = 5, max_time_seconds: float = 60 ) -> Tuple[float, List[float]]: """ Benchmark an iterator/iterable for `num_iter` iterations with an extra `warmup` iterations of warmup. End early if `max_time_seconds` time is spent on iterations. Returns: float: average time (seconds) per iteration list[float]: time spent on each iteration. Sometimes useful for further analysis. """ num_iter, warmup = int(num_iter), int(warmup) iterator = iter(iterator) for _ in range(warmup): next(iterator) timer = Timer() all_times = [] for curr_iter in tqdm.trange(num_iter): start = timer.seconds() if start > max_time_seconds: num_iter = curr_iter break next(iterator) all_times.append(timer.seconds() - start) avg = timer.seconds() / num_iter return avg, all_times class DataLoaderBenchmark: """ Some common benchmarks that help understand perf bottleneck of a standard dataloader made of dataset, mapper and sampler. """ def __init__( self, dataset, *, mapper, sampler=None, total_batch_size, num_workers=0, max_time_seconds: int = 90, ): """ Args: max_time_seconds (int): maximum time to spent for each benchmark other args: same as in `build.py:build_detection_train_loader` """ if isinstance(dataset, list): dataset = DatasetFromList(dataset, copy=False, serialize=True) if sampler is None: sampler = TrainingSampler(len(dataset)) self.dataset = dataset self.mapper = mapper self.sampler = sampler self.total_batch_size = total_batch_size self.num_workers = num_workers self.per_gpu_batch_size = self.total_batch_size // comm.get_world_size() self.max_time_seconds = max_time_seconds def _benchmark(self, iterator, num_iter, warmup, msg=None): avg, all_times = iter_benchmark(iterator, num_iter, warmup, self.max_time_seconds) if msg is not None: self._log_time(msg, avg, all_times) return avg, all_times def _log_time(self, msg, avg, all_times, distributed=False): percentiles = [np.percentile(all_times, k, interpolation="nearest") for k in [1, 5, 95, 99]] if not distributed: logger.info( f"{msg}: avg={1.0/avg:.1f} it/s, " f"p1={percentiles[0]:.2g}s, p5={percentiles[1]:.2g}s, " f"p95={percentiles[2]:.2g}s, p99={percentiles[3]:.2g}s." ) return avg_per_gpu = comm.all_gather(avg) percentiles_per_gpu = comm.all_gather(percentiles) if comm.get_rank() > 0: return for idx, avg, percentiles in zip(count(), avg_per_gpu, percentiles_per_gpu): logger.info( f"GPU{idx} {msg}: avg={1.0/avg:.1f} it/s, " f"p1={percentiles[0]:.2g}s, p5={percentiles[1]:.2g}s, " f"p95={percentiles[2]:.2g}s, p99={percentiles[3]:.2g}s." ) def benchmark_dataset(self, num_iter, warmup=5): """ Benchmark the speed of taking raw samples from the dataset. """ def loader(): while True: for k in self.sampler: yield self.dataset[k] self._benchmark(loader(), num_iter, warmup, "Dataset Alone") def benchmark_mapper(self, num_iter, warmup=5): """ Benchmark the speed of taking raw samples from the dataset and map them in a single process. """ def loader(): while True: for k in self.sampler: yield self.mapper(self.dataset[k]) self._benchmark(loader(), num_iter, warmup, "Single Process Mapper (sec/sample)") def benchmark_workers(self, num_iter, warmup=10): """ Benchmark the dataloader by tuning num_workers to [0, 1, self.num_workers]. """ candidates = [0, 1] if self.num_workers not in candidates: candidates.append(self.num_workers) dataset = MapDataset(self.dataset, self.mapper) for n in candidates: loader = build_batch_data_loader( dataset, self.sampler, self.total_batch_size, num_workers=n, ) self._benchmark( iter(loader), num_iter * max(n, 1), warmup * max(n, 1), f"DataLoader ({n} workers, bs={self.per_gpu_batch_size})", ) del loader def benchmark_IPC(self, num_iter, warmup=10): """ Benchmark the dataloader where each worker outputs nothing. This eliminates the IPC overhead compared to the regular dataloader. PyTorch multiprocessing's IPC only optimizes for torch tensors. Large numpy arrays or other data structure may incur large IPC overhead. """ n = self.num_workers dataset = _EmptyMapDataset(MapDataset(self.dataset, self.mapper)) loader = build_batch_data_loader( dataset, self.sampler, self.total_batch_size, num_workers=n ) self._benchmark( iter(loader), num_iter * max(n, 1), warmup * max(n, 1), f"DataLoader ({n} workers, bs={self.per_gpu_batch_size}) w/o comm", ) def benchmark_distributed(self, num_iter, warmup=10): """ Benchmark the dataloader in each distributed worker, and log results of all workers. This helps understand the final performance as well as the variances among workers. It also prints startup time (first iter) of the dataloader. """ gpu = comm.get_world_size() dataset = MapDataset(self.dataset, self.mapper) n = self.num_workers loader = build_batch_data_loader( dataset, self.sampler, self.total_batch_size, num_workers=n ) timer = Timer() loader = iter(loader) next(loader) startup_time = timer.seconds() logger.info("Dataloader startup time: {:.2f} seconds".format(startup_time)) comm.synchronize() avg, all_times = self._benchmark(loader, num_iter * max(n, 1), warmup * max(n, 1)) del loader self._log_time( f"DataLoader ({gpu} GPUs x {n} workers, total bs={self.total_batch_size})", avg, all_times, True, ) ================================================ FILE: detectron2/detectron2/data/build.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import itertools import logging import numpy as np import operator import pickle from typing import Any, Callable, Dict, List, Optional, Union import torch import torch.utils.data as torchdata from tabulate import tabulate from termcolor import colored from detectron2.config import configurable from detectron2.structures import BoxMode from detectron2.utils.comm import get_world_size from detectron2.utils.env import seed_all_rng from detectron2.utils.file_io import PathManager from detectron2.utils.logger import _log_api_usage, log_first_n from .catalog import DatasetCatalog, MetadataCatalog from .common import AspectRatioGroupedDataset, DatasetFromList, MapDataset, ToIterableDataset from .dataset_mapper import DatasetMapper from .detection_utils import check_metadata_consistency from .samplers import ( InferenceSampler, RandomSubsetTrainingSampler, RepeatFactorTrainingSampler, TrainingSampler, ) """ This file contains the default logic to build a dataloader for training or testing. """ __all__ = [ "build_batch_data_loader", "build_detection_train_loader", "build_detection_test_loader", "get_detection_dataset_dicts", "load_proposals_into_dataset", "print_instances_class_histogram", ] def filter_images_with_only_crowd_annotations(dataset_dicts): """ Filter out images with none annotations or only crowd annotations (i.e., images without non-crowd annotations). A common training-time preprocessing on COCO dataset. Args: dataset_dicts (list[dict]): annotations in Detectron2 Dataset format. Returns: list[dict]: the same format, but filtered. """ num_before = len(dataset_dicts) def valid(anns): for ann in anns: if ann.get("iscrowd", 0) == 0: return True return False dataset_dicts = [x for x in dataset_dicts if valid(x["annotations"])] num_after = len(dataset_dicts) logger = logging.getLogger(__name__) logger.info( "Removed {} images with no usable annotations. {} images left.".format( num_before - num_after, num_after ) ) return dataset_dicts def filter_images_with_few_keypoints(dataset_dicts, min_keypoints_per_image): """ Filter out images with too few number of keypoints. Args: dataset_dicts (list[dict]): annotations in Detectron2 Dataset format. Returns: list[dict]: the same format as dataset_dicts, but filtered. """ num_before = len(dataset_dicts) def visible_keypoints_in_image(dic): # Each keypoints field has the format [x1, y1, v1, ...], where v is visibility annotations = dic["annotations"] return sum( (np.array(ann["keypoints"][2::3]) > 0).sum() for ann in annotations if "keypoints" in ann ) dataset_dicts = [ x for x in dataset_dicts if visible_keypoints_in_image(x) >= min_keypoints_per_image ] num_after = len(dataset_dicts) logger = logging.getLogger(__name__) logger.info( "Removed {} images with fewer than {} keypoints.".format( num_before - num_after, min_keypoints_per_image ) ) return dataset_dicts def load_proposals_into_dataset(dataset_dicts, proposal_file): """ Load precomputed object proposals into the dataset. The proposal file should be a pickled dict with the following keys: - "ids": list[int] or list[str], the image ids - "boxes": list[np.ndarray], each is an Nx4 array of boxes corresponding to the image id - "objectness_logits": list[np.ndarray], each is an N sized array of objectness scores corresponding to the boxes. - "bbox_mode": the BoxMode of the boxes array. Defaults to ``BoxMode.XYXY_ABS``. Args: dataset_dicts (list[dict]): annotations in Detectron2 Dataset format. proposal_file (str): file path of pre-computed proposals, in pkl format. Returns: list[dict]: the same format as dataset_dicts, but added proposal field. """ logger = logging.getLogger(__name__) logger.info("Loading proposals from: {}".format(proposal_file)) with PathManager.open(proposal_file, "rb") as f: proposals = pickle.load(f, encoding="latin1") # Rename the key names in D1 proposal files rename_keys = {"indexes": "ids", "scores": "objectness_logits"} for key in rename_keys: if key in proposals: proposals[rename_keys[key]] = proposals.pop(key) # Fetch the indexes of all proposals that are in the dataset # Convert image_id to str since they could be int. img_ids = set({str(record["image_id"]) for record in dataset_dicts}) id_to_index = {str(id): i for i, id in enumerate(proposals["ids"]) if str(id) in img_ids} # Assuming default bbox_mode of precomputed proposals are 'XYXY_ABS' bbox_mode = BoxMode(proposals["bbox_mode"]) if "bbox_mode" in proposals else BoxMode.XYXY_ABS for record in dataset_dicts: # Get the index of the proposal i = id_to_index[str(record["image_id"])] boxes = proposals["boxes"][i] objectness_logits = proposals["objectness_logits"][i] # Sort the proposals in descending order of the scores inds = objectness_logits.argsort()[::-1] record["proposal_boxes"] = boxes[inds] record["proposal_objectness_logits"] = objectness_logits[inds] record["proposal_bbox_mode"] = bbox_mode return dataset_dicts def print_instances_class_histogram(dataset_dicts, class_names): """ Args: dataset_dicts (list[dict]): list of dataset dicts. class_names (list[str]): list of class names (zero-indexed). """ num_classes = len(class_names) hist_bins = np.arange(num_classes + 1) histogram = np.zeros((num_classes,), dtype=np.int) for entry in dataset_dicts: annos = entry["annotations"] classes = np.asarray( [x["category_id"] for x in annos if not x.get("iscrowd", 0)], dtype=np.int ) if len(classes): assert classes.min() >= 0, f"Got an invalid category_id={classes.min()}" assert ( classes.max() < num_classes ), f"Got an invalid category_id={classes.max()} for a dataset of {num_classes} classes" histogram += np.histogram(classes, bins=hist_bins)[0] N_COLS = min(6, len(class_names) * 2) def short_name(x): # make long class names shorter. useful for lvis if len(x) > 13: return x[:11] + ".." return x data = list( itertools.chain(*[[short_name(class_names[i]), int(v)] for i, v in enumerate(histogram)]) ) total_num_instances = sum(data[1::2]) data.extend([None] * (N_COLS - (len(data) % N_COLS))) if num_classes > 1: data.extend(["total", total_num_instances]) data = itertools.zip_longest(*[data[i::N_COLS] for i in range(N_COLS)]) table = tabulate( data, headers=["category", "#instances"] * (N_COLS // 2), tablefmt="pipe", numalign="left", stralign="center", ) log_first_n( logging.INFO, "Distribution of instances among all {} categories:\n".format(num_classes) + colored(table, "cyan"), key="message", ) def get_detection_dataset_dicts( names, filter_empty=True, min_keypoints=0, proposal_files=None, check_consistency=True, ): """ Load and prepare dataset dicts for instance detection/segmentation and semantic segmentation. Args: names (str or list[str]): a dataset name or a list of dataset names filter_empty (bool): whether to filter out images without instance annotations min_keypoints (int): filter out images with fewer keypoints than `min_keypoints`. Set to 0 to do nothing. proposal_files (list[str]): if given, a list of object proposal files that match each dataset in `names`. check_consistency (bool): whether to check if datasets have consistent metadata. Returns: list[dict]: a list of dicts following the standard dataset dict format. """ if isinstance(names, str): names = [names] assert len(names), names dataset_dicts = [DatasetCatalog.get(dataset_name) for dataset_name in names] if isinstance(dataset_dicts[0], torchdata.Dataset): if len(dataset_dicts) > 1: # ConcatDataset does not work for iterable style dataset. # We could support concat for iterable as well, but it's often # not a good idea to concat iterables anyway. return torchdata.ConcatDataset(dataset_dicts) return dataset_dicts[0] for dataset_name, dicts in zip(names, dataset_dicts): assert len(dicts), "Dataset '{}' is empty!".format(dataset_name) if proposal_files is not None: assert len(names) == len(proposal_files) # load precomputed proposals from proposal files dataset_dicts = [ load_proposals_into_dataset(dataset_i_dicts, proposal_file) for dataset_i_dicts, proposal_file in zip(dataset_dicts, proposal_files) ] dataset_dicts = list(itertools.chain.from_iterable(dataset_dicts)) has_instances = "annotations" in dataset_dicts[0] if filter_empty and has_instances: dataset_dicts = filter_images_with_only_crowd_annotations(dataset_dicts) if min_keypoints > 0 and has_instances: dataset_dicts = filter_images_with_few_keypoints(dataset_dicts, min_keypoints) if check_consistency and has_instances: try: class_names = MetadataCatalog.get(names[0]).thing_classes check_metadata_consistency("thing_classes", names) print_instances_class_histogram(dataset_dicts, class_names) except AttributeError: # class names are not available for this dataset pass assert len(dataset_dicts), "No valid data found in {}.".format(",".join(names)) return dataset_dicts def build_batch_data_loader( dataset, sampler, total_batch_size, *, aspect_ratio_grouping=False, num_workers=0, collate_fn=None, ): """ Build a batched dataloader. The main differences from `torch.utils.data.DataLoader` are: 1. support aspect ratio grouping options 2. use no "batch collation", because this is common for detection training Args: dataset (torch.utils.data.Dataset): a pytorch map-style or iterable dataset. sampler (torch.utils.data.sampler.Sampler or None): a sampler that produces indices. Must be provided iff. ``dataset`` is a map-style dataset. total_batch_size, aspect_ratio_grouping, num_workers, collate_fn: see :func:`build_detection_train_loader`. Returns: iterable[list]. Length of each list is the batch size of the current GPU. Each element in the list comes from the dataset. """ world_size = get_world_size() assert ( total_batch_size > 0 and total_batch_size % world_size == 0 ), "Total batch size ({}) must be divisible by the number of gpus ({}).".format( total_batch_size, world_size ) batch_size = total_batch_size // world_size if isinstance(dataset, torchdata.IterableDataset): assert sampler is None, "sampler must be None if dataset is IterableDataset" else: dataset = ToIterableDataset(dataset, sampler) if aspect_ratio_grouping: data_loader = torchdata.DataLoader( dataset, num_workers=num_workers, collate_fn=operator.itemgetter(0), # don't batch, but yield individual elements worker_init_fn=worker_init_reset_seed, ) # yield individual mapped dict data_loader = AspectRatioGroupedDataset(data_loader, batch_size) if collate_fn is None: return data_loader return MapDataset(data_loader, collate_fn) else: return torchdata.DataLoader( dataset, batch_size=batch_size, drop_last=True, num_workers=num_workers, collate_fn=trivial_batch_collator if collate_fn is None else collate_fn, worker_init_fn=worker_init_reset_seed, ) def _train_loader_from_config(cfg, mapper=None, *, dataset=None, sampler=None): if dataset is None: dataset = get_detection_dataset_dicts( cfg.DATASETS.TRAIN, filter_empty=cfg.DATALOADER.FILTER_EMPTY_ANNOTATIONS, min_keypoints=cfg.MODEL.ROI_KEYPOINT_HEAD.MIN_KEYPOINTS_PER_IMAGE if cfg.MODEL.KEYPOINT_ON else 0, proposal_files=cfg.DATASETS.PROPOSAL_FILES_TRAIN if cfg.MODEL.LOAD_PROPOSALS else None, ) _log_api_usage("dataset." + cfg.DATASETS.TRAIN[0]) if mapper is None: mapper = DatasetMapper(cfg, True) if sampler is None: sampler_name = cfg.DATALOADER.SAMPLER_TRAIN logger = logging.getLogger(__name__) if isinstance(dataset, torchdata.IterableDataset): logger.info("Not using any sampler since the dataset is IterableDataset.") sampler = None else: logger.info("Using training sampler {}".format(sampler_name)) if sampler_name == "TrainingSampler": sampler = TrainingSampler(len(dataset)) elif sampler_name == "RepeatFactorTrainingSampler": repeat_factors = RepeatFactorTrainingSampler.repeat_factors_from_category_frequency( dataset, cfg.DATALOADER.REPEAT_THRESHOLD ) sampler = RepeatFactorTrainingSampler(repeat_factors) elif sampler_name == "RandomSubsetTrainingSampler": sampler = RandomSubsetTrainingSampler( len(dataset), cfg.DATALOADER.RANDOM_SUBSET_RATIO ) else: raise ValueError("Unknown training sampler: {}".format(sampler_name)) return { "dataset": dataset, "sampler": sampler, "mapper": mapper, "total_batch_size": cfg.SOLVER.IMS_PER_BATCH, "aspect_ratio_grouping": cfg.DATALOADER.ASPECT_RATIO_GROUPING, "num_workers": cfg.DATALOADER.NUM_WORKERS, } @configurable(from_config=_train_loader_from_config) def build_detection_train_loader( dataset, *, mapper, sampler=None, total_batch_size, aspect_ratio_grouping=True, num_workers=0, collate_fn=None, ): """ Build a dataloader for object detection with some default features. Args: dataset (list or torch.utils.data.Dataset): a list of dataset dicts, or a pytorch dataset (either map-style or iterable). It can be obtained by using :func:`DatasetCatalog.get` or :func:`get_detection_dataset_dicts`. mapper (callable): a callable which takes a sample (dict) from dataset and returns the format to be consumed by the model. When using cfg, the default choice is ``DatasetMapper(cfg, is_train=True)``. sampler (torch.utils.data.sampler.Sampler or None): a sampler that produces indices to be applied on ``dataset``. If ``dataset`` is map-style, the default sampler is a :class:`TrainingSampler`, which coordinates an infinite random shuffle sequence across all workers. Sampler must be None if ``dataset`` is iterable. total_batch_size (int): total batch size across all workers. aspect_ratio_grouping (bool): whether to group images with similar aspect ratio for efficiency. When enabled, it requires each element in dataset be a dict with keys "width" and "height". num_workers (int): number of parallel data loading workers collate_fn: a function that determines how to do batching, same as the argument of `torch.utils.data.DataLoader`. Defaults to do no collation and return a list of data. No collation is OK for small batch size and simple data structures. If your batch size is large and each sample contains too many small tensors, it's more efficient to collate them in data loader. Returns: torch.utils.data.DataLoader: a dataloader. Each output from it is a ``list[mapped_element]`` of length ``total_batch_size / num_workers``, where ``mapped_element`` is produced by the ``mapper``. """ if isinstance(dataset, list): dataset = DatasetFromList(dataset, copy=False) if mapper is not None: dataset = MapDataset(dataset, mapper) if isinstance(dataset, torchdata.IterableDataset): assert sampler is None, "sampler must be None if dataset is IterableDataset" else: if sampler is None: sampler = TrainingSampler(len(dataset)) assert isinstance(sampler, torchdata.Sampler), f"Expect a Sampler but got {type(sampler)}" return build_batch_data_loader( dataset, sampler, total_batch_size, aspect_ratio_grouping=aspect_ratio_grouping, num_workers=num_workers, collate_fn=collate_fn, ) def _test_loader_from_config(cfg, dataset_name, mapper=None): """ Uses the given `dataset_name` argument (instead of the names in cfg), because the standard practice is to evaluate each test set individually (not combining them). """ if isinstance(dataset_name, str): dataset_name = [dataset_name] dataset = get_detection_dataset_dicts( dataset_name, filter_empty=False, proposal_files=[ cfg.DATASETS.PROPOSAL_FILES_TEST[list(cfg.DATASETS.TEST).index(x)] for x in dataset_name ] if cfg.MODEL.LOAD_PROPOSALS else None, ) if mapper is None: mapper = DatasetMapper(cfg, False) return { "dataset": dataset, "mapper": mapper, "num_workers": cfg.DATALOADER.NUM_WORKERS, "sampler": InferenceSampler(len(dataset)) if not isinstance(dataset, torchdata.IterableDataset) else None, } @configurable(from_config=_test_loader_from_config) def build_detection_test_loader( dataset: Union[List[Any], torchdata.Dataset], *, mapper: Callable[[Dict[str, Any]], Any], sampler: Optional[torchdata.Sampler] = None, batch_size: int = 1, num_workers: int = 0, collate_fn: Optional[Callable[[List[Any]], Any]] = None, ) -> torchdata.DataLoader: """ Similar to `build_detection_train_loader`, with default batch size = 1, and sampler = :class:`InferenceSampler`. This sampler coordinates all workers to produce the exact set of all samples. Args: dataset: a list of dataset dicts, or a pytorch dataset (either map-style or iterable). They can be obtained by using :func:`DatasetCatalog.get` or :func:`get_detection_dataset_dicts`. mapper: a callable which takes a sample (dict) from dataset and returns the format to be consumed by the model. When using cfg, the default choice is ``DatasetMapper(cfg, is_train=False)``. sampler: a sampler that produces indices to be applied on ``dataset``. Default to :class:`InferenceSampler`, which splits the dataset across all workers. Sampler must be None if `dataset` is iterable. batch_size: the batch size of the data loader to be created. Default to 1 image per worker since this is the standard when reporting inference time in papers. num_workers: number of parallel data loading workers collate_fn: same as the argument of `torch.utils.data.DataLoader`. Defaults to do no collation and return a list of data. Returns: DataLoader: a torch DataLoader, that loads the given detection dataset, with test-time transformation and batching. Examples: :: data_loader = build_detection_test_loader( DatasetRegistry.get("my_test"), mapper=DatasetMapper(...)) # or, instantiate with a CfgNode: data_loader = build_detection_test_loader(cfg, "my_test") """ if isinstance(dataset, list): dataset = DatasetFromList(dataset, copy=False) if mapper is not None: dataset = MapDataset(dataset, mapper) if isinstance(dataset, torchdata.IterableDataset): assert sampler is None, "sampler must be None if dataset is IterableDataset" else: if sampler is None: sampler = InferenceSampler(len(dataset)) return torchdata.DataLoader( dataset, batch_size=batch_size, sampler=sampler, drop_last=False, num_workers=num_workers, collate_fn=trivial_batch_collator if collate_fn is None else collate_fn, ) def trivial_batch_collator(batch): """ A batch collator that does nothing. """ return batch def worker_init_reset_seed(worker_id): initial_seed = torch.initial_seed() % 2**31 seed_all_rng(initial_seed + worker_id) ================================================ FILE: detectron2/detectron2/data/catalog.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import logging import types from collections import UserDict from typing import List from detectron2.utils.logger import log_first_n __all__ = ["DatasetCatalog", "MetadataCatalog", "Metadata"] class _DatasetCatalog(UserDict): """ A global dictionary that stores information about the datasets and how to obtain them. It contains a mapping from strings (which are names that identify a dataset, e.g. "coco_2014_train") to a function which parses the dataset and returns the samples in the format of `list[dict]`. The returned dicts should be in Detectron2 Dataset format (See DATASETS.md for details) if used with the data loader functionalities in `data/build.py,data/detection_transform.py`. The purpose of having this catalog is to make it easy to choose different datasets, by just using the strings in the config. """ def register(self, name, func): """ Args: name (str): the name that identifies a dataset, e.g. "coco_2014_train". func (callable): a callable which takes no arguments and returns a list of dicts. It must return the same results if called multiple times. """ assert callable(func), "You must register a function with `DatasetCatalog.register`!" assert name not in self, "Dataset '{}' is already registered!".format(name) self[name] = func def get(self, name): """ Call the registered function and return its results. Args: name (str): the name that identifies a dataset, e.g. "coco_2014_train". Returns: list[dict]: dataset annotations. """ try: f = self[name] except KeyError as e: raise KeyError( "Dataset '{}' is not registered! Available datasets are: {}".format( name, ", ".join(list(self.keys())) ) ) from e return f() def list(self) -> List[str]: """ List all registered datasets. Returns: list[str] """ return list(self.keys()) def remove(self, name): """ Alias of ``pop``. """ self.pop(name) def __str__(self): return "DatasetCatalog(registered datasets: {})".format(", ".join(self.keys())) __repr__ = __str__ DatasetCatalog = _DatasetCatalog() DatasetCatalog.__doc__ = ( _DatasetCatalog.__doc__ + """ .. automethod:: detectron2.data.catalog.DatasetCatalog.register .. automethod:: detectron2.data.catalog.DatasetCatalog.get """ ) class Metadata(types.SimpleNamespace): """ A class that supports simple attribute setter/getter. It is intended for storing metadata of a dataset and make it accessible globally. Examples: :: # somewhere when you load the data: MetadataCatalog.get("mydataset").thing_classes = ["person", "dog"] # somewhere when you print statistics or visualize: classes = MetadataCatalog.get("mydataset").thing_classes """ # the name of the dataset # set default to N/A so that `self.name` in the errors will not trigger getattr again name: str = "N/A" _RENAMED = { "class_names": "thing_classes", "dataset_id_to_contiguous_id": "thing_dataset_id_to_contiguous_id", "stuff_class_names": "stuff_classes", } def __getattr__(self, key): if key in self._RENAMED: log_first_n( logging.WARNING, "Metadata '{}' was renamed to '{}'!".format(key, self._RENAMED[key]), n=10, ) return getattr(self, self._RENAMED[key]) # "name" exists in every metadata if len(self.__dict__) > 1: raise AttributeError( "Attribute '{}' does not exist in the metadata of dataset '{}'. Available " "keys are {}.".format(key, self.name, str(self.__dict__.keys())) ) else: raise AttributeError( f"Attribute '{key}' does not exist in the metadata of dataset '{self.name}': " "metadata is empty." ) def __setattr__(self, key, val): if key in self._RENAMED: log_first_n( logging.WARNING, "Metadata '{}' was renamed to '{}'!".format(key, self._RENAMED[key]), n=10, ) setattr(self, self._RENAMED[key], val) # Ensure that metadata of the same name stays consistent try: oldval = getattr(self, key) assert oldval == val, ( "Attribute '{}' in the metadata of '{}' cannot be set " "to a different value!\n{} != {}".format(key, self.name, oldval, val) ) except AttributeError: super().__setattr__(key, val) def as_dict(self): """ Returns all the metadata as a dict. Note that modifications to the returned dict will not reflect on the Metadata object. """ return copy.copy(self.__dict__) def set(self, **kwargs): """ Set multiple metadata with kwargs. """ for k, v in kwargs.items(): setattr(self, k, v) return self def get(self, key, default=None): """ Access an attribute and return its value if exists. Otherwise return default. """ try: return getattr(self, key) except AttributeError: return default class _MetadataCatalog(UserDict): """ MetadataCatalog is a global dictionary that provides access to :class:`Metadata` of a given dataset. The metadata associated with a certain name is a singleton: once created, the metadata will stay alive and will be returned by future calls to ``get(name)``. It's like global variables, so don't abuse it. It's meant for storing knowledge that's constant and shared across the execution of the program, e.g.: the class names in COCO. """ def get(self, name): """ Args: name (str): name of a dataset (e.g. coco_2014_train). Returns: Metadata: The :class:`Metadata` instance associated with this name, or create an empty one if none is available. """ assert len(name) r = super().get(name, None) if r is None: r = self[name] = Metadata(name=name) return r def list(self): """ List all registered metadata. Returns: list[str]: keys (names of datasets) of all registered metadata """ return list(self.keys()) def remove(self, name): """ Alias of ``pop``. """ self.pop(name) def __str__(self): return "MetadataCatalog(registered metadata: {})".format(", ".join(self.keys())) __repr__ = __str__ MetadataCatalog = _MetadataCatalog() MetadataCatalog.__doc__ = ( _MetadataCatalog.__doc__ + """ .. automethod:: detectron2.data.catalog.MetadataCatalog.get """ ) ================================================ FILE: detectron2/detectron2/data/common.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import itertools import logging import numpy as np import pickle import random import torch.utils.data as data from torch.utils.data.sampler import Sampler from detectron2.utils.serialize import PicklableWrapper __all__ = ["MapDataset", "DatasetFromList", "AspectRatioGroupedDataset", "ToIterableDataset"] def _shard_iterator_dataloader_worker(iterable): # Shard the iterable if we're currently inside pytorch dataloader worker. worker_info = data.get_worker_info() if worker_info is None or worker_info.num_workers == 1: # do nothing yield from iterable else: yield from itertools.islice(iterable, worker_info.id, None, worker_info.num_workers) class _MapIterableDataset(data.IterableDataset): """ Map a function over elements in an IterableDataset. Similar to pytorch's MapIterDataPipe, but support filtering when map_func returns None. This class is not public-facing. Will be called by `MapDataset`. """ def __init__(self, dataset, map_func): self._dataset = dataset self._map_func = PicklableWrapper(map_func) # wrap so that a lambda will work def __len__(self): return len(self._dataset) def __iter__(self): for x in map(self._map_func, self._dataset): if x is not None: yield x class MapDataset(data.Dataset): """ Map a function over the elements in a dataset. """ def __init__(self, dataset, map_func): """ Args: dataset: a dataset where map function is applied. Can be either map-style or iterable dataset. When given an iterable dataset, the returned object will also be an iterable dataset. map_func: a callable which maps the element in dataset. map_func can return None to skip the data (e.g. in case of errors). How None is handled depends on the style of `dataset`. If `dataset` is map-style, it randomly tries other elements. If `dataset` is iterable, it skips the data and tries the next. """ self._dataset = dataset self._map_func = PicklableWrapper(map_func) # wrap so that a lambda will work self._rng = random.Random(42) self._fallback_candidates = set(range(len(dataset))) def __new__(cls, dataset, map_func): is_iterable = isinstance(dataset, data.IterableDataset) if is_iterable: return _MapIterableDataset(dataset, map_func) else: return super().__new__(cls) def __getnewargs__(self): return self._dataset, self._map_func def __len__(self): return len(self._dataset) def __getitem__(self, idx): retry_count = 0 cur_idx = int(idx) while True: data = self._map_func(self._dataset[cur_idx]) if data is not None: self._fallback_candidates.add(cur_idx) return data # _map_func fails for this idx, use a random new index from the pool retry_count += 1 self._fallback_candidates.discard(cur_idx) cur_idx = self._rng.sample(self._fallback_candidates, k=1)[0] if retry_count >= 3: logger = logging.getLogger(__name__) logger.warning( "Failed to apply `_map_func` for idx: {}, retry count: {}".format( idx, retry_count ) ) class DatasetFromList(data.Dataset): """ Wrap a list to a torch Dataset. It produces elements of the list as data. """ def __init__(self, lst: list, copy: bool = True, serialize: bool = True): """ Args: lst (list): a list which contains elements to produce. copy (bool): whether to deepcopy the element when producing it, so that the result can be modified in place without affecting the source in the list. serialize (bool): whether to hold memory using serialized objects, when enabled, data loader workers can use shared RAM from master process instead of making a copy. """ self._lst = lst self._copy = copy self._serialize = serialize def _serialize(data): buffer = pickle.dumps(data, protocol=-1) return np.frombuffer(buffer, dtype=np.uint8) if self._serialize: logger = logging.getLogger(__name__) logger.info( "Serializing {} elements to byte tensors and concatenating them all ...".format( len(self._lst) ) ) self._lst = [_serialize(x) for x in self._lst] self._addr = np.asarray([len(x) for x in self._lst], dtype=np.int64) self._addr = np.cumsum(self._addr) self._lst = np.concatenate(self._lst) logger.info("Serialized dataset takes {:.2f} MiB".format(len(self._lst) / 1024**2)) def __len__(self): if self._serialize: return len(self._addr) else: return len(self._lst) def __getitem__(self, idx): if self._serialize: start_addr = 0 if idx == 0 else self._addr[idx - 1].item() end_addr = self._addr[idx].item() bytes = memoryview(self._lst[start_addr:end_addr]) return pickle.loads(bytes) elif self._copy: return copy.deepcopy(self._lst[idx]) else: return self._lst[idx] class ToIterableDataset(data.IterableDataset): """ Convert an old indices-based (also called map-style) dataset to an iterable-style dataset. """ def __init__(self, dataset: data.Dataset, sampler: Sampler, shard_sampler: bool = True): """ Args: dataset: an old-style dataset with ``__getitem__`` sampler: a cheap iterable that produces indices to be applied on ``dataset``. shard_sampler: whether to shard the sampler based on the current pytorch data loader worker id. When an IterableDataset is forked by pytorch's DataLoader into multiple workers, it is responsible for sharding its data based on worker id so that workers don't produce identical data. Most samplers (like our TrainingSampler) do not shard based on dataloader worker id and this argument should be set to True. But certain samplers may be already sharded, in that case this argument should be set to False. """ assert not isinstance(dataset, data.IterableDataset), dataset assert isinstance(sampler, Sampler), sampler self.dataset = dataset self.sampler = sampler self.shard_sampler = shard_sampler def __iter__(self): if not self.shard_sampler: sampler = self.sampler else: # With map-style dataset, `DataLoader(dataset, sampler)` runs the # sampler in main process only. But `DataLoader(ToIterableDataset(dataset, sampler))` # will run sampler in every of the N worker. So we should only keep 1/N of the ids on # each worker. The assumption is that sampler is cheap to iterate so it's fine to # discard ids in workers. sampler = _shard_iterator_dataloader_worker(self.sampler) for idx in sampler: yield self.dataset[idx] def __len__(self): return len(self.sampler) class AspectRatioGroupedDataset(data.IterableDataset): """ Batch data that have similar aspect ratio together. In this implementation, images whose aspect ratio < (or >) 1 will be batched together. This improves training speed because the images then need less padding to form a batch. It assumes the underlying dataset produces dicts with "width" and "height" keys. It will then produce a list of original dicts with length = batch_size, all with similar aspect ratios. """ def __init__(self, dataset, batch_size): """ Args: dataset: an iterable. Each element must be a dict with keys "width" and "height", which will be used to batch data. batch_size (int): """ self.dataset = dataset self.batch_size = batch_size self._buckets = [[] for _ in range(2)] # Hard-coded two aspect ratio groups: w > h and w < h. # Can add support for more aspect ratio groups, but doesn't seem useful def __iter__(self): for d in self.dataset: w, h = d["width"], d["height"] bucket_id = 0 if w > h else 1 bucket = self._buckets[bucket_id] bucket.append(d) if len(bucket) == self.batch_size: data = bucket[:] # Clear bucket first, because code after yield is not # guaranteed to execute del bucket[:] yield data ================================================ FILE: detectron2/detectron2/data/dataset_mapper.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import logging import numpy as np from typing import List, Optional, Union import torch from detectron2.config import configurable from . import detection_utils as utils from . import transforms as T """ This file contains the default mapping that's applied to "dataset dicts". """ __all__ = ["DatasetMapper"] class DatasetMapper: """ A callable which takes a dataset dict in Detectron2 Dataset format, and map it into a format used by the model. This is the default callable to be used to map your dataset dict into training data. You may need to follow it to implement your own one for customized logic, such as a different way to read or transform images. See :doc:`/tutorials/data_loading` for details. The callable currently does the following: 1. Read the image from "file_name" 2. Applies cropping/geometric transforms to the image and annotations 3. Prepare data and annotations to Tensor and :class:`Instances` """ @configurable def __init__( self, is_train: bool, *, augmentations: List[Union[T.Augmentation, T.Transform]], image_format: str, use_instance_mask: bool = False, use_keypoint: bool = False, instance_mask_format: str = "polygon", keypoint_hflip_indices: Optional[np.ndarray] = None, precomputed_proposal_topk: Optional[int] = None, recompute_boxes: bool = False, ): """ NOTE: this interface is experimental. Args: is_train: whether it's used in training or inference augmentations: a list of augmentations or deterministic transforms to apply image_format: an image format supported by :func:`detection_utils.read_image`. use_instance_mask: whether to process instance segmentation annotations, if available use_keypoint: whether to process keypoint annotations if available instance_mask_format: one of "polygon" or "bitmask". Process instance segmentation masks into this format. keypoint_hflip_indices: see :func:`detection_utils.create_keypoint_hflip_indices` precomputed_proposal_topk: if given, will load pre-computed proposals from dataset_dict and keep the top k proposals for each image. recompute_boxes: whether to overwrite bounding box annotations by computing tight bounding boxes from instance mask annotations. """ if recompute_boxes: assert use_instance_mask, "recompute_boxes requires instance masks" # fmt: off self.is_train = is_train self.augmentations = T.AugmentationList(augmentations) self.image_format = image_format self.use_instance_mask = use_instance_mask self.instance_mask_format = instance_mask_format self.use_keypoint = use_keypoint self.keypoint_hflip_indices = keypoint_hflip_indices self.proposal_topk = precomputed_proposal_topk self.recompute_boxes = recompute_boxes # fmt: on logger = logging.getLogger(__name__) mode = "training" if is_train else "inference" logger.info(f"[DatasetMapper] Augmentations used in {mode}: {augmentations}") @classmethod def from_config(cls, cfg, is_train: bool = True): augs = utils.build_augmentation(cfg, is_train) if cfg.INPUT.CROP.ENABLED and is_train: augs.insert(0, T.RandomCrop(cfg.INPUT.CROP.TYPE, cfg.INPUT.CROP.SIZE)) recompute_boxes = cfg.MODEL.MASK_ON else: recompute_boxes = False ret = { "is_train": is_train, "augmentations": augs, "image_format": cfg.INPUT.FORMAT, "use_instance_mask": cfg.MODEL.MASK_ON, "instance_mask_format": cfg.INPUT.MASK_FORMAT, "use_keypoint": cfg.MODEL.KEYPOINT_ON, "recompute_boxes": recompute_boxes, } if cfg.MODEL.KEYPOINT_ON: ret["keypoint_hflip_indices"] = utils.create_keypoint_hflip_indices(cfg.DATASETS.TRAIN) if cfg.MODEL.LOAD_PROPOSALS: ret["precomputed_proposal_topk"] = ( cfg.DATASETS.PRECOMPUTED_PROPOSAL_TOPK_TRAIN if is_train else cfg.DATASETS.PRECOMPUTED_PROPOSAL_TOPK_TEST ) return ret def _transform_annotations(self, dataset_dict, transforms, image_shape): # USER: Modify this if you want to keep them for some reason. for anno in dataset_dict["annotations"]: if not self.use_instance_mask: anno.pop("segmentation", None) if not self.use_keypoint: anno.pop("keypoints", None) # USER: Implement additional transformations if you have other types of data annos = [ utils.transform_instance_annotations( obj, transforms, image_shape, keypoint_hflip_indices=self.keypoint_hflip_indices ) for obj in dataset_dict.pop("annotations") if obj.get("iscrowd", 0) == 0 ] instances = utils.annotations_to_instances( annos, image_shape, mask_format=self.instance_mask_format ) # After transforms such as cropping are applied, the bounding box may no longer # tightly bound the object. As an example, imagine a triangle object # [(0,0), (2,0), (0,2)] cropped by a box [(1,0),(2,2)] (XYXY format). The tight # bounding box of the cropped triangle should be [(1,0),(2,1)], which is not equal to # the intersection of original bounding box and the cropping box. if self.recompute_boxes: instances.gt_boxes = instances.gt_masks.get_bounding_boxes() dataset_dict["instances"] = utils.filter_empty_instances(instances) def __call__(self, dataset_dict): """ Args: dataset_dict (dict): Metadata of one image, in Detectron2 Dataset format. Returns: dict: a format that builtin models in detectron2 accept """ dataset_dict = copy.deepcopy(dataset_dict) # it will be modified by code below # USER: Write your own image loading if it's not from a file image = utils.read_image(dataset_dict["file_name"], format=self.image_format) utils.check_image_size(dataset_dict, image) # USER: Remove if you don't do semantic/panoptic segmentation. if "sem_seg_file_name" in dataset_dict: sem_seg_gt = utils.read_image(dataset_dict.pop("sem_seg_file_name"), "L").squeeze(2) else: sem_seg_gt = None aug_input = T.AugInput(image, sem_seg=sem_seg_gt) transforms = self.augmentations(aug_input) image, sem_seg_gt = aug_input.image, aug_input.sem_seg image_shape = image.shape[:2] # h, w # Pytorch's dataloader is efficient on torch.Tensor due to shared-memory, # but not efficient on large generic data structures due to the use of pickle & mp.Queue. # Therefore it's important to use torch.Tensor. dataset_dict["image"] = torch.as_tensor(np.ascontiguousarray(image.transpose(2, 0, 1))) if sem_seg_gt is not None: dataset_dict["sem_seg"] = torch.as_tensor(sem_seg_gt.astype("long")) # USER: Remove if you don't use pre-computed proposals. # Most users would not need this feature. if self.proposal_topk is not None: utils.transform_proposals( dataset_dict, image_shape, transforms, proposal_topk=self.proposal_topk ) if not self.is_train: # USER: Modify this if you want to keep them for some reason. dataset_dict.pop("annotations", None) dataset_dict.pop("sem_seg_file_name", None) return dataset_dict if "annotations" in dataset_dict: self._transform_annotations(dataset_dict, transforms, image_shape) return dataset_dict ================================================ FILE: detectron2/detectron2/data/datasets/README.md ================================================ ### Common Datasets The dataset implemented here do not need to load the data into the final format. It should provide the minimal data structure needed to use the dataset, so it can be very efficient. For example, for an image dataset, just provide the file names and labels, but don't read the images. Let the downstream decide how to read. ================================================ FILE: detectron2/detectron2/data/datasets/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .coco import load_coco_json, load_sem_seg, register_coco_instances, convert_to_coco_json from .coco_panoptic import register_coco_panoptic, register_coco_panoptic_separated from .lvis import load_lvis_json, register_lvis_instances, get_lvis_instances_meta from .pascal_voc import load_voc_instances, register_pascal_voc from . import builtin as _builtin # ensure the builtin datasets are registered __all__ = [k for k in globals().keys() if not k.startswith("_")] ================================================ FILE: detectron2/detectron2/data/datasets/builtin.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. """ This file registers pre-defined datasets at hard-coded paths, and their metadata. We hard-code metadata for common datasets. This will enable: 1. Consistency check when loading the datasets 2. Use models on these standard datasets directly and run demos, without having to download the dataset annotations We hard-code some paths to the dataset that's assumed to exist in "./datasets/". Users SHOULD NOT use this file to create new dataset / metadata for new dataset. To add new dataset, refer to the tutorial "docs/DATASETS.md". """ import os from detectron2.data import DatasetCatalog, MetadataCatalog from .builtin_meta import ADE20K_SEM_SEG_CATEGORIES, _get_builtin_metadata from .cityscapes import load_cityscapes_instances, load_cityscapes_semantic from .cityscapes_panoptic import register_all_cityscapes_panoptic from .coco import load_sem_seg, register_coco_instances from .coco_panoptic import register_coco_panoptic, register_coco_panoptic_separated from .lvis import get_lvis_instances_meta, register_lvis_instances from .pascal_voc import register_pascal_voc # ==== Predefined datasets and splits for COCO ========== _PREDEFINED_SPLITS_COCO = {} _PREDEFINED_SPLITS_COCO["coco"] = { "coco_2014_train": ("coco/train2014", "coco/annotations/instances_train2014.json"), "coco_2014_val": ("coco/val2014", "coco/annotations/instances_val2014.json"), "coco_2014_minival": ("coco/val2014", "coco/annotations/instances_minival2014.json"), "coco_2014_valminusminival": ( "coco/val2014", "coco/annotations/instances_valminusminival2014.json", ), "coco_2017_train": ("coco/train2017", "coco/annotations/instances_train2017.json"), "coco_2017_val": ("coco/val2017", "coco/annotations/instances_val2017.json"), "coco_2017_test": ("coco/test2017", "coco/annotations/image_info_test2017.json"), "coco_2017_test-dev": ("coco/test2017", "coco/annotations/image_info_test-dev2017.json"), "coco_2017_val_100": ("coco/val2017", "coco/annotations/instances_val2017_100.json"), } _PREDEFINED_SPLITS_COCO["coco_person"] = { "keypoints_coco_2014_train": ( "coco/train2014", "coco/annotations/person_keypoints_train2014.json", ), "keypoints_coco_2014_val": ("coco/val2014", "coco/annotations/person_keypoints_val2014.json"), "keypoints_coco_2014_minival": ( "coco/val2014", "coco/annotations/person_keypoints_minival2014.json", ), "keypoints_coco_2014_valminusminival": ( "coco/val2014", "coco/annotations/person_keypoints_valminusminival2014.json", ), "keypoints_coco_2017_train": ( "coco/train2017", "coco/annotations/person_keypoints_train2017.json", ), "keypoints_coco_2017_val": ("coco/val2017", "coco/annotations/person_keypoints_val2017.json"), "keypoints_coco_2017_val_100": ( "coco/val2017", "coco/annotations/person_keypoints_val2017_100.json", ), } _PREDEFINED_SPLITS_COCO_PANOPTIC = { "coco_2017_train_panoptic": ( # This is the original panoptic annotation directory "coco/panoptic_train2017", "coco/annotations/panoptic_train2017.json", # This directory contains semantic annotations that are # converted from panoptic annotations. # It is used by PanopticFPN. # You can use the script at detectron2/datasets/prepare_panoptic_fpn.py # to create these directories. "coco/panoptic_stuff_train2017", ), "coco_2017_val_panoptic": ( "coco/panoptic_val2017", "coco/annotations/panoptic_val2017.json", "coco/panoptic_stuff_val2017", ), "coco_2017_val_100_panoptic": ( "coco/panoptic_val2017_100", "coco/annotations/panoptic_val2017_100.json", "coco/panoptic_stuff_val2017_100", ), } def register_all_coco(root): for dataset_name, splits_per_dataset in _PREDEFINED_SPLITS_COCO.items(): for key, (image_root, json_file) in splits_per_dataset.items(): # Assume pre-defined datasets live in `./datasets`. register_coco_instances( key, _get_builtin_metadata(dataset_name), os.path.join(root, json_file) if "://" not in json_file else json_file, os.path.join(root, image_root), ) for ( prefix, (panoptic_root, panoptic_json, semantic_root), ) in _PREDEFINED_SPLITS_COCO_PANOPTIC.items(): prefix_instances = prefix[: -len("_panoptic")] instances_meta = MetadataCatalog.get(prefix_instances) image_root, instances_json = instances_meta.image_root, instances_meta.json_file # The "separated" version of COCO panoptic segmentation dataset, # e.g. used by Panoptic FPN register_coco_panoptic_separated( prefix, _get_builtin_metadata("coco_panoptic_separated"), image_root, os.path.join(root, panoptic_root), os.path.join(root, panoptic_json), os.path.join(root, semantic_root), instances_json, ) # The "standard" version of COCO panoptic segmentation dataset, # e.g. used by Panoptic-DeepLab register_coco_panoptic( prefix, _get_builtin_metadata("coco_panoptic_standard"), image_root, os.path.join(root, panoptic_root), os.path.join(root, panoptic_json), instances_json, ) # ==== Predefined datasets and splits for LVIS ========== _PREDEFINED_SPLITS_LVIS = { "lvis_v1": { "lvis_v1_train": ("coco/", "lvis/lvis_v1_train.json"), "lvis_v1_val": ("coco/", "lvis/lvis_v1_val.json"), "lvis_v1_test_dev": ("coco/", "lvis/lvis_v1_image_info_test_dev.json"), "lvis_v1_test_challenge": ("coco/", "lvis/lvis_v1_image_info_test_challenge.json"), }, "lvis_v0.5": { "lvis_v0.5_train": ("coco/", "lvis/lvis_v0.5_train.json"), "lvis_v0.5_val": ("coco/", "lvis/lvis_v0.5_val.json"), "lvis_v0.5_val_rand_100": ("coco/", "lvis/lvis_v0.5_val_rand_100.json"), "lvis_v0.5_test": ("coco/", "lvis/lvis_v0.5_image_info_test.json"), }, "lvis_v0.5_cocofied": { "lvis_v0.5_train_cocofied": ("coco/", "lvis/lvis_v0.5_train_cocofied.json"), "lvis_v0.5_val_cocofied": ("coco/", "lvis/lvis_v0.5_val_cocofied.json"), }, } def register_all_lvis(root): for dataset_name, splits_per_dataset in _PREDEFINED_SPLITS_LVIS.items(): for key, (image_root, json_file) in splits_per_dataset.items(): register_lvis_instances( key, get_lvis_instances_meta(dataset_name), os.path.join(root, json_file) if "://" not in json_file else json_file, os.path.join(root, image_root), ) # ==== Predefined splits for raw cityscapes images =========== _RAW_CITYSCAPES_SPLITS = { "cityscapes_fine_{task}_train": ("cityscapes/leftImg8bit/train/", "cityscapes/gtFine/train/"), "cityscapes_fine_{task}_val": ("cityscapes/leftImg8bit/val/", "cityscapes/gtFine/val/"), "cityscapes_fine_{task}_test": ("cityscapes/leftImg8bit/test/", "cityscapes/gtFine/test/"), } def register_all_cityscapes(root): for key, (image_dir, gt_dir) in _RAW_CITYSCAPES_SPLITS.items(): meta = _get_builtin_metadata("cityscapes") image_dir = os.path.join(root, image_dir) gt_dir = os.path.join(root, gt_dir) inst_key = key.format(task="instance_seg") DatasetCatalog.register( inst_key, lambda x=image_dir, y=gt_dir: load_cityscapes_instances( x, y, from_json=True, to_polygons=True ), ) MetadataCatalog.get(inst_key).set( image_dir=image_dir, gt_dir=gt_dir, evaluator_type="cityscapes_instance", **meta ) sem_key = key.format(task="sem_seg") DatasetCatalog.register( sem_key, lambda x=image_dir, y=gt_dir: load_cityscapes_semantic(x, y) ) MetadataCatalog.get(sem_key).set( image_dir=image_dir, gt_dir=gt_dir, evaluator_type="cityscapes_sem_seg", ignore_label=255, **meta, ) # ==== Predefined splits for PASCAL VOC =========== def register_all_pascal_voc(root): SPLITS = [ ("voc_2007_trainval", "VOC2007", "trainval"), ("voc_2007_train", "VOC2007", "train"), ("voc_2007_val", "VOC2007", "val"), ("voc_2007_test", "VOC2007", "test"), ("voc_2012_trainval", "VOC2012", "trainval"), ("voc_2012_train", "VOC2012", "train"), ("voc_2012_val", "VOC2012", "val"), ] for name, dirname, split in SPLITS: year = 2007 if "2007" in name else 2012 register_pascal_voc(name, os.path.join(root, dirname), split, year) MetadataCatalog.get(name).evaluator_type = "pascal_voc" def register_all_ade20k(root): root = os.path.join(root, "ADEChallengeData2016") for name, dirname in [("train", "training"), ("val", "validation")]: image_dir = os.path.join(root, "images", dirname) gt_dir = os.path.join(root, "annotations_detectron2", dirname) name = f"ade20k_sem_seg_{name}" DatasetCatalog.register( name, lambda x=image_dir, y=gt_dir: load_sem_seg(y, x, gt_ext="png", image_ext="jpg") ) MetadataCatalog.get(name).set( stuff_classes=ADE20K_SEM_SEG_CATEGORIES[:], image_root=image_dir, sem_seg_root=gt_dir, evaluator_type="sem_seg", ignore_label=255, ) # True for open source; # Internally at fb, we register them elsewhere if __name__.endswith(".builtin"): # Assume pre-defined datasets live in `./datasets`. _root = os.path.expanduser(os.getenv("DETECTRON2_DATASETS", "datasets")) register_all_coco(_root) register_all_lvis(_root) register_all_cityscapes(_root) register_all_cityscapes_panoptic(_root) register_all_pascal_voc(_root) register_all_ade20k(_root) ================================================ FILE: detectron2/detectron2/data/datasets/builtin_meta.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. """ Note: For your custom dataset, there is no need to hard-code metadata anywhere in the code. For example, for COCO-format dataset, metadata will be obtained automatically when calling `load_coco_json`. For other dataset, metadata may also be obtained in other ways during loading. However, we hard-coded metadata for a few common dataset here. The only goal is to allow users who don't have these dataset to use pre-trained models. Users don't have to download a COCO json (which contains metadata), in order to visualize a COCO model (with correct class names and colors). """ # All coco categories, together with their nice-looking visualization colors # It's from https://github.com/cocodataset/panopticapi/blob/master/panoptic_coco_categories.json COCO_CATEGORIES = [ {"color": [220, 20, 60], "isthing": 1, "id": 1, "name": "person"}, {"color": [119, 11, 32], "isthing": 1, "id": 2, "name": "bicycle"}, {"color": [0, 0, 142], "isthing": 1, "id": 3, "name": "car"}, {"color": [0, 0, 230], "isthing": 1, "id": 4, "name": "motorcycle"}, {"color": [106, 0, 228], "isthing": 1, "id": 5, "name": "airplane"}, {"color": [0, 60, 100], "isthing": 1, "id": 6, "name": "bus"}, {"color": [0, 80, 100], "isthing": 1, "id": 7, "name": "train"}, {"color": [0, 0, 70], "isthing": 1, "id": 8, "name": "truck"}, {"color": [0, 0, 192], "isthing": 1, "id": 9, "name": "boat"}, {"color": [250, 170, 30], "isthing": 1, "id": 10, "name": "traffic light"}, {"color": [100, 170, 30], "isthing": 1, "id": 11, "name": "fire hydrant"}, {"color": [220, 220, 0], "isthing": 1, "id": 13, "name": "stop sign"}, {"color": [175, 116, 175], "isthing": 1, "id": 14, "name": "parking meter"}, {"color": [250, 0, 30], "isthing": 1, "id": 15, "name": "bench"}, {"color": [165, 42, 42], "isthing": 1, "id": 16, "name": "bird"}, {"color": [255, 77, 255], "isthing": 1, "id": 17, "name": "cat"}, {"color": [0, 226, 252], "isthing": 1, "id": 18, "name": "dog"}, {"color": [182, 182, 255], "isthing": 1, "id": 19, "name": "horse"}, {"color": [0, 82, 0], "isthing": 1, "id": 20, "name": "sheep"}, {"color": [120, 166, 157], "isthing": 1, "id": 21, "name": "cow"}, {"color": [110, 76, 0], "isthing": 1, "id": 22, "name": "elephant"}, {"color": [174, 57, 255], "isthing": 1, "id": 23, "name": "bear"}, {"color": [199, 100, 0], "isthing": 1, "id": 24, "name": "zebra"}, {"color": [72, 0, 118], "isthing": 1, "id": 25, "name": "giraffe"}, {"color": [255, 179, 240], "isthing": 1, "id": 27, "name": "backpack"}, {"color": [0, 125, 92], "isthing": 1, "id": 28, "name": "umbrella"}, {"color": [209, 0, 151], "isthing": 1, "id": 31, "name": "handbag"}, {"color": [188, 208, 182], "isthing": 1, "id": 32, "name": "tie"}, {"color": [0, 220, 176], "isthing": 1, "id": 33, "name": "suitcase"}, {"color": [255, 99, 164], "isthing": 1, "id": 34, "name": "frisbee"}, {"color": [92, 0, 73], "isthing": 1, "id": 35, "name": "skis"}, {"color": [133, 129, 255], "isthing": 1, "id": 36, "name": "snowboard"}, {"color": [78, 180, 255], "isthing": 1, "id": 37, "name": "sports ball"}, {"color": [0, 228, 0], "isthing": 1, "id": 38, "name": "kite"}, {"color": [174, 255, 243], "isthing": 1, "id": 39, "name": "baseball bat"}, {"color": [45, 89, 255], "isthing": 1, "id": 40, "name": "baseball glove"}, {"color": [134, 134, 103], "isthing": 1, "id": 41, "name": "skateboard"}, {"color": [145, 148, 174], "isthing": 1, "id": 42, "name": "surfboard"}, {"color": [255, 208, 186], "isthing": 1, "id": 43, "name": "tennis racket"}, {"color": [197, 226, 255], "isthing": 1, "id": 44, "name": "bottle"}, {"color": [171, 134, 1], "isthing": 1, "id": 46, "name": "wine glass"}, {"color": [109, 63, 54], "isthing": 1, "id": 47, "name": "cup"}, {"color": [207, 138, 255], "isthing": 1, "id": 48, "name": "fork"}, {"color": [151, 0, 95], "isthing": 1, "id": 49, "name": "knife"}, {"color": [9, 80, 61], "isthing": 1, "id": 50, "name": "spoon"}, {"color": [84, 105, 51], "isthing": 1, "id": 51, "name": "bowl"}, {"color": [74, 65, 105], "isthing": 1, "id": 52, "name": "banana"}, {"color": [166, 196, 102], "isthing": 1, "id": 53, "name": "apple"}, {"color": [208, 195, 210], "isthing": 1, "id": 54, "name": "sandwich"}, {"color": [255, 109, 65], "isthing": 1, "id": 55, "name": "orange"}, {"color": [0, 143, 149], "isthing": 1, "id": 56, "name": "broccoli"}, {"color": [179, 0, 194], "isthing": 1, "id": 57, "name": "carrot"}, {"color": [209, 99, 106], "isthing": 1, "id": 58, "name": "hot dog"}, {"color": [5, 121, 0], "isthing": 1, "id": 59, "name": "pizza"}, {"color": [227, 255, 205], "isthing": 1, "id": 60, "name": "donut"}, {"color": [147, 186, 208], "isthing": 1, "id": 61, "name": "cake"}, {"color": [153, 69, 1], "isthing": 1, "id": 62, "name": "chair"}, {"color": [3, 95, 161], "isthing": 1, "id": 63, "name": "couch"}, {"color": [163, 255, 0], "isthing": 1, "id": 64, "name": "potted plant"}, {"color": [119, 0, 170], "isthing": 1, "id": 65, "name": "bed"}, {"color": [0, 182, 199], "isthing": 1, "id": 67, "name": "dining table"}, {"color": [0, 165, 120], "isthing": 1, "id": 70, "name": "toilet"}, {"color": [183, 130, 88], "isthing": 1, "id": 72, "name": "tv"}, {"color": [95, 32, 0], "isthing": 1, "id": 73, "name": "laptop"}, {"color": [130, 114, 135], "isthing": 1, "id": 74, "name": "mouse"}, {"color": [110, 129, 133], "isthing": 1, "id": 75, "name": "remote"}, {"color": [166, 74, 118], "isthing": 1, "id": 76, "name": "keyboard"}, {"color": [219, 142, 185], "isthing": 1, "id": 77, "name": "cell phone"}, {"color": [79, 210, 114], "isthing": 1, "id": 78, "name": "microwave"}, {"color": [178, 90, 62], "isthing": 1, "id": 79, "name": "oven"}, {"color": [65, 70, 15], "isthing": 1, "id": 80, "name": "toaster"}, {"color": [127, 167, 115], "isthing": 1, "id": 81, "name": "sink"}, {"color": [59, 105, 106], "isthing": 1, "id": 82, "name": "refrigerator"}, {"color": [142, 108, 45], "isthing": 1, "id": 84, "name": "book"}, {"color": [196, 172, 0], "isthing": 1, "id": 85, "name": "clock"}, {"color": [95, 54, 80], "isthing": 1, "id": 86, "name": "vase"}, {"color": [128, 76, 255], "isthing": 1, "id": 87, "name": "scissors"}, {"color": [201, 57, 1], "isthing": 1, "id": 88, "name": "teddy bear"}, {"color": [246, 0, 122], "isthing": 1, "id": 89, "name": "hair drier"}, {"color": [191, 162, 208], "isthing": 1, "id": 90, "name": "toothbrush"}, {"color": [255, 255, 128], "isthing": 0, "id": 92, "name": "banner"}, {"color": [147, 211, 203], "isthing": 0, "id": 93, "name": "blanket"}, {"color": [150, 100, 100], "isthing": 0, "id": 95, "name": "bridge"}, {"color": [168, 171, 172], "isthing": 0, "id": 100, "name": "cardboard"}, {"color": [146, 112, 198], "isthing": 0, "id": 107, "name": "counter"}, {"color": [210, 170, 100], "isthing": 0, "id": 109, "name": "curtain"}, {"color": [92, 136, 89], "isthing": 0, "id": 112, "name": "door-stuff"}, {"color": [218, 88, 184], "isthing": 0, "id": 118, "name": "floor-wood"}, {"color": [241, 129, 0], "isthing": 0, "id": 119, "name": "flower"}, {"color": [217, 17, 255], "isthing": 0, "id": 122, "name": "fruit"}, {"color": [124, 74, 181], "isthing": 0, "id": 125, "name": "gravel"}, {"color": [70, 70, 70], "isthing": 0, "id": 128, "name": "house"}, {"color": [255, 228, 255], "isthing": 0, "id": 130, "name": "light"}, {"color": [154, 208, 0], "isthing": 0, "id": 133, "name": "mirror-stuff"}, {"color": [193, 0, 92], "isthing": 0, "id": 138, "name": "net"}, {"color": [76, 91, 113], "isthing": 0, "id": 141, "name": "pillow"}, {"color": [255, 180, 195], "isthing": 0, "id": 144, "name": "platform"}, {"color": [106, 154, 176], "isthing": 0, "id": 145, "name": "playingfield"}, {"color": [230, 150, 140], "isthing": 0, "id": 147, "name": "railroad"}, {"color": [60, 143, 255], "isthing": 0, "id": 148, "name": "river"}, {"color": [128, 64, 128], "isthing": 0, "id": 149, "name": "road"}, {"color": [92, 82, 55], "isthing": 0, "id": 151, "name": "roof"}, {"color": [254, 212, 124], "isthing": 0, "id": 154, "name": "sand"}, {"color": [73, 77, 174], "isthing": 0, "id": 155, "name": "sea"}, {"color": [255, 160, 98], "isthing": 0, "id": 156, "name": "shelf"}, {"color": [255, 255, 255], "isthing": 0, "id": 159, "name": "snow"}, {"color": [104, 84, 109], "isthing": 0, "id": 161, "name": "stairs"}, {"color": [169, 164, 131], "isthing": 0, "id": 166, "name": "tent"}, {"color": [225, 199, 255], "isthing": 0, "id": 168, "name": "towel"}, {"color": [137, 54, 74], "isthing": 0, "id": 171, "name": "wall-brick"}, {"color": [135, 158, 223], "isthing": 0, "id": 175, "name": "wall-stone"}, {"color": [7, 246, 231], "isthing": 0, "id": 176, "name": "wall-tile"}, {"color": [107, 255, 200], "isthing": 0, "id": 177, "name": "wall-wood"}, {"color": [58, 41, 149], "isthing": 0, "id": 178, "name": "water-other"}, {"color": [183, 121, 142], "isthing": 0, "id": 180, "name": "window-blind"}, {"color": [255, 73, 97], "isthing": 0, "id": 181, "name": "window-other"}, {"color": [107, 142, 35], "isthing": 0, "id": 184, "name": "tree-merged"}, {"color": [190, 153, 153], "isthing": 0, "id": 185, "name": "fence-merged"}, {"color": [146, 139, 141], "isthing": 0, "id": 186, "name": "ceiling-merged"}, {"color": [70, 130, 180], "isthing": 0, "id": 187, "name": "sky-other-merged"}, {"color": [134, 199, 156], "isthing": 0, "id": 188, "name": "cabinet-merged"}, {"color": [209, 226, 140], "isthing": 0, "id": 189, "name": "table-merged"}, {"color": [96, 36, 108], "isthing": 0, "id": 190, "name": "floor-other-merged"}, {"color": [96, 96, 96], "isthing": 0, "id": 191, "name": "pavement-merged"}, {"color": [64, 170, 64], "isthing": 0, "id": 192, "name": "mountain-merged"}, {"color": [152, 251, 152], "isthing": 0, "id": 193, "name": "grass-merged"}, {"color": [208, 229, 228], "isthing": 0, "id": 194, "name": "dirt-merged"}, {"color": [206, 186, 171], "isthing": 0, "id": 195, "name": "paper-merged"}, {"color": [152, 161, 64], "isthing": 0, "id": 196, "name": "food-other-merged"}, {"color": [116, 112, 0], "isthing": 0, "id": 197, "name": "building-other-merged"}, {"color": [0, 114, 143], "isthing": 0, "id": 198, "name": "rock-merged"}, {"color": [102, 102, 156], "isthing": 0, "id": 199, "name": "wall-other-merged"}, {"color": [250, 141, 255], "isthing": 0, "id": 200, "name": "rug-merged"}, ] # fmt: off COCO_PERSON_KEYPOINT_NAMES = ( "nose", "left_eye", "right_eye", "left_ear", "right_ear", "left_shoulder", "right_shoulder", "left_elbow", "right_elbow", "left_wrist", "right_wrist", "left_hip", "right_hip", "left_knee", "right_knee", "left_ankle", "right_ankle", ) # fmt: on # Pairs of keypoints that should be exchanged under horizontal flipping COCO_PERSON_KEYPOINT_FLIP_MAP = ( ("left_eye", "right_eye"), ("left_ear", "right_ear"), ("left_shoulder", "right_shoulder"), ("left_elbow", "right_elbow"), ("left_wrist", "right_wrist"), ("left_hip", "right_hip"), ("left_knee", "right_knee"), ("left_ankle", "right_ankle"), ) # rules for pairs of keypoints to draw a line between, and the line color to use. KEYPOINT_CONNECTION_RULES = [ # face ("left_ear", "left_eye", (102, 204, 255)), ("right_ear", "right_eye", (51, 153, 255)), ("left_eye", "nose", (102, 0, 204)), ("nose", "right_eye", (51, 102, 255)), # upper-body ("left_shoulder", "right_shoulder", (255, 128, 0)), ("left_shoulder", "left_elbow", (153, 255, 204)), ("right_shoulder", "right_elbow", (128, 229, 255)), ("left_elbow", "left_wrist", (153, 255, 153)), ("right_elbow", "right_wrist", (102, 255, 224)), # lower-body ("left_hip", "right_hip", (255, 102, 0)), ("left_hip", "left_knee", (255, 255, 77)), ("right_hip", "right_knee", (153, 255, 204)), ("left_knee", "left_ankle", (191, 255, 128)), ("right_knee", "right_ankle", (255, 195, 77)), ] # All Cityscapes categories, together with their nice-looking visualization colors # It's from https://github.com/mcordts/cityscapesScripts/blob/master/cityscapesscripts/helpers/labels.py # noqa CITYSCAPES_CATEGORIES = [ {"color": (128, 64, 128), "isthing": 0, "id": 7, "trainId": 0, "name": "road"}, {"color": (244, 35, 232), "isthing": 0, "id": 8, "trainId": 1, "name": "sidewalk"}, {"color": (70, 70, 70), "isthing": 0, "id": 11, "trainId": 2, "name": "building"}, {"color": (102, 102, 156), "isthing": 0, "id": 12, "trainId": 3, "name": "wall"}, {"color": (190, 153, 153), "isthing": 0, "id": 13, "trainId": 4, "name": "fence"}, {"color": (153, 153, 153), "isthing": 0, "id": 17, "trainId": 5, "name": "pole"}, {"color": (250, 170, 30), "isthing": 0, "id": 19, "trainId": 6, "name": "traffic light"}, {"color": (220, 220, 0), "isthing": 0, "id": 20, "trainId": 7, "name": "traffic sign"}, {"color": (107, 142, 35), "isthing": 0, "id": 21, "trainId": 8, "name": "vegetation"}, {"color": (152, 251, 152), "isthing": 0, "id": 22, "trainId": 9, "name": "terrain"}, {"color": (70, 130, 180), "isthing": 0, "id": 23, "trainId": 10, "name": "sky"}, {"color": (220, 20, 60), "isthing": 1, "id": 24, "trainId": 11, "name": "person"}, {"color": (255, 0, 0), "isthing": 1, "id": 25, "trainId": 12, "name": "rider"}, {"color": (0, 0, 142), "isthing": 1, "id": 26, "trainId": 13, "name": "car"}, {"color": (0, 0, 70), "isthing": 1, "id": 27, "trainId": 14, "name": "truck"}, {"color": (0, 60, 100), "isthing": 1, "id": 28, "trainId": 15, "name": "bus"}, {"color": (0, 80, 100), "isthing": 1, "id": 31, "trainId": 16, "name": "train"}, {"color": (0, 0, 230), "isthing": 1, "id": 32, "trainId": 17, "name": "motorcycle"}, {"color": (119, 11, 32), "isthing": 1, "id": 33, "trainId": 18, "name": "bicycle"}, ] # fmt: off ADE20K_SEM_SEG_CATEGORIES = [ "wall", "building", "sky", "floor", "tree", "ceiling", "road, route", "bed", "window ", "grass", "cabinet", "sidewalk, pavement", "person", "earth, ground", "door", "table", "mountain, mount", "plant", "curtain", "chair", "car", "water", "painting, picture", "sofa", "shelf", "house", "sea", "mirror", "rug", "field", "armchair", "seat", "fence", "desk", "rock, stone", "wardrobe, closet, press", "lamp", "tub", "rail", "cushion", "base, pedestal, stand", "box", "column, pillar", "signboard, sign", "chest of drawers, chest, bureau, dresser", "counter", "sand", "sink", "skyscraper", "fireplace", "refrigerator, icebox", "grandstand, covered stand", "path", "stairs", "runway", "case, display case, showcase, vitrine", "pool table, billiard table, snooker table", "pillow", "screen door, screen", "stairway, staircase", "river", "bridge, span", "bookcase", "blind, screen", "coffee table", "toilet, can, commode, crapper, pot, potty, stool, throne", "flower", "book", "hill", "bench", "countertop", "stove", "palm, palm tree", "kitchen island", "computer", "swivel chair", "boat", "bar", "arcade machine", "hovel, hut, hutch, shack, shanty", "bus", "towel", "light", "truck", "tower", "chandelier", "awning, sunshade, sunblind", "street lamp", "booth", "tv", "plane", "dirt track", "clothes", "pole", "land, ground, soil", "bannister, banister, balustrade, balusters, handrail", "escalator, moving staircase, moving stairway", "ottoman, pouf, pouffe, puff, hassock", "bottle", "buffet, counter, sideboard", "poster, posting, placard, notice, bill, card", "stage", "van", "ship", "fountain", "conveyer belt, conveyor belt, conveyer, conveyor, transporter", "canopy", "washer, automatic washer, washing machine", "plaything, toy", "pool", "stool", "barrel, cask", "basket, handbasket", "falls", "tent", "bag", "minibike, motorbike", "cradle", "oven", "ball", "food, solid food", "step, stair", "tank, storage tank", "trade name", "microwave", "pot", "animal", "bicycle", "lake", "dishwasher", "screen", "blanket, cover", "sculpture", "hood, exhaust hood", "sconce", "vase", "traffic light", "tray", "trash can", "fan", "pier", "crt screen", "plate", "monitor", "bulletin board", "shower", "radiator", "glass, drinking glass", "clock", "flag", # noqa ] # After processed by `prepare_ade20k_sem_seg.py`, id 255 means ignore # fmt: on def _get_coco_instances_meta(): thing_ids = [k["id"] for k in COCO_CATEGORIES if k["isthing"] == 1] thing_colors = [k["color"] for k in COCO_CATEGORIES if k["isthing"] == 1] assert len(thing_ids) == 80, len(thing_ids) # Mapping from the incontiguous COCO category id to an id in [0, 79] thing_dataset_id_to_contiguous_id = {k: i for i, k in enumerate(thing_ids)} thing_classes = [k["name"] for k in COCO_CATEGORIES if k["isthing"] == 1] ret = { "thing_dataset_id_to_contiguous_id": thing_dataset_id_to_contiguous_id, "thing_classes": thing_classes, "thing_colors": thing_colors, } return ret def _get_coco_panoptic_separated_meta(): """ Returns metadata for "separated" version of the panoptic segmentation dataset. """ stuff_ids = [k["id"] for k in COCO_CATEGORIES if k["isthing"] == 0] assert len(stuff_ids) == 53, len(stuff_ids) # For semantic segmentation, this mapping maps from contiguous stuff id # (in [0, 53], used in models) to ids in the dataset (used for processing results) # The id 0 is mapped to an extra category "thing". stuff_dataset_id_to_contiguous_id = {k: i + 1 for i, k in enumerate(stuff_ids)} # When converting COCO panoptic annotations to semantic annotations # We label the "thing" category to 0 stuff_dataset_id_to_contiguous_id[0] = 0 # 54 names for COCO stuff categories (including "things") stuff_classes = ["things"] + [ k["name"].replace("-other", "").replace("-merged", "") for k in COCO_CATEGORIES if k["isthing"] == 0 ] # NOTE: I randomly picked a color for things stuff_colors = [[82, 18, 128]] + [k["color"] for k in COCO_CATEGORIES if k["isthing"] == 0] ret = { "stuff_dataset_id_to_contiguous_id": stuff_dataset_id_to_contiguous_id, "stuff_classes": stuff_classes, "stuff_colors": stuff_colors, } ret.update(_get_coco_instances_meta()) return ret def _get_builtin_metadata(dataset_name): if dataset_name == "coco": return _get_coco_instances_meta() if dataset_name == "coco_panoptic_separated": return _get_coco_panoptic_separated_meta() elif dataset_name == "coco_panoptic_standard": meta = {} # The following metadata maps contiguous id from [0, #thing categories + # #stuff categories) to their names and colors. We have to replica of the # same name and color under "thing_*" and "stuff_*" because the current # visualization function in D2 handles thing and class classes differently # due to some heuristic used in Panoptic FPN. We keep the same naming to # enable reusing existing visualization functions. thing_classes = [k["name"] for k in COCO_CATEGORIES] thing_colors = [k["color"] for k in COCO_CATEGORIES] stuff_classes = [k["name"] for k in COCO_CATEGORIES] stuff_colors = [k["color"] for k in COCO_CATEGORIES] meta["thing_classes"] = thing_classes meta["thing_colors"] = thing_colors meta["stuff_classes"] = stuff_classes meta["stuff_colors"] = stuff_colors # Convert category id for training: # category id: like semantic segmentation, it is the class id for each # pixel. Since there are some classes not used in evaluation, the category # id is not always contiguous and thus we have two set of category ids: # - original category id: category id in the original dataset, mainly # used for evaluation. # - contiguous category id: [0, #classes), in order to train the linear # softmax classifier. thing_dataset_id_to_contiguous_id = {} stuff_dataset_id_to_contiguous_id = {} for i, cat in enumerate(COCO_CATEGORIES): if cat["isthing"]: thing_dataset_id_to_contiguous_id[cat["id"]] = i else: stuff_dataset_id_to_contiguous_id[cat["id"]] = i meta["thing_dataset_id_to_contiguous_id"] = thing_dataset_id_to_contiguous_id meta["stuff_dataset_id_to_contiguous_id"] = stuff_dataset_id_to_contiguous_id return meta elif dataset_name == "coco_person": return { "thing_classes": ["person"], "keypoint_names": COCO_PERSON_KEYPOINT_NAMES, "keypoint_flip_map": COCO_PERSON_KEYPOINT_FLIP_MAP, "keypoint_connection_rules": KEYPOINT_CONNECTION_RULES, } elif dataset_name == "cityscapes": # fmt: off CITYSCAPES_THING_CLASSES = [ "person", "rider", "car", "truck", "bus", "train", "motorcycle", "bicycle", ] CITYSCAPES_STUFF_CLASSES = [ "road", "sidewalk", "building", "wall", "fence", "pole", "traffic light", "traffic sign", "vegetation", "terrain", "sky", "person", "rider", "car", "truck", "bus", "train", "motorcycle", "bicycle", ] # fmt: on return { "thing_classes": CITYSCAPES_THING_CLASSES, "stuff_classes": CITYSCAPES_STUFF_CLASSES, } raise KeyError("No built-in metadata for dataset {}".format(dataset_name)) ================================================ FILE: detectron2/detectron2/data/datasets/cityscapes.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import functools import json import logging import multiprocessing as mp import numpy as np import os from itertools import chain import pycocotools.mask as mask_util from PIL import Image from detectron2.structures import BoxMode from detectron2.utils.comm import get_world_size from detectron2.utils.file_io import PathManager from detectron2.utils.logger import setup_logger try: import cv2 # noqa except ImportError: # OpenCV is an optional dependency at the moment pass logger = logging.getLogger(__name__) def _get_cityscapes_files(image_dir, gt_dir): files = [] # scan through the directory cities = PathManager.ls(image_dir) logger.info(f"{len(cities)} cities found in '{image_dir}'.") for city in cities: city_img_dir = os.path.join(image_dir, city) city_gt_dir = os.path.join(gt_dir, city) for basename in PathManager.ls(city_img_dir): image_file = os.path.join(city_img_dir, basename) suffix = "leftImg8bit.png" assert basename.endswith(suffix), basename basename = basename[: -len(suffix)] instance_file = os.path.join(city_gt_dir, basename + "gtFine_instanceIds.png") label_file = os.path.join(city_gt_dir, basename + "gtFine_labelIds.png") json_file = os.path.join(city_gt_dir, basename + "gtFine_polygons.json") files.append((image_file, instance_file, label_file, json_file)) assert len(files), "No images found in {}".format(image_dir) for f in files[0]: assert PathManager.isfile(f), f return files def load_cityscapes_instances(image_dir, gt_dir, from_json=True, to_polygons=True): """ Args: image_dir (str): path to the raw dataset. e.g., "~/cityscapes/leftImg8bit/train". gt_dir (str): path to the raw annotations. e.g., "~/cityscapes/gtFine/train". from_json (bool): whether to read annotations from the raw json file or the png files. to_polygons (bool): whether to represent the segmentation as polygons (COCO's format) instead of masks (cityscapes's format). Returns: list[dict]: a list of dicts in Detectron2 standard format. (See `Using Custom Datasets `_ ) """ if from_json: assert to_polygons, ( "Cityscapes's json annotations are in polygon format. " "Converting to mask format is not supported now." ) files = _get_cityscapes_files(image_dir, gt_dir) logger.info("Preprocessing cityscapes annotations ...") # This is still not fast: all workers will execute duplicate works and will # take up to 10m on a 8GPU server. pool = mp.Pool(processes=max(mp.cpu_count() // get_world_size() // 2, 4)) ret = pool.map( functools.partial(_cityscapes_files_to_dict, from_json=from_json, to_polygons=to_polygons), files, ) logger.info("Loaded {} images from {}".format(len(ret), image_dir)) # Map cityscape ids to contiguous ids from cityscapesscripts.helpers.labels import labels labels = [l for l in labels if l.hasInstances and not l.ignoreInEval] dataset_id_to_contiguous_id = {l.id: idx for idx, l in enumerate(labels)} for dict_per_image in ret: for anno in dict_per_image["annotations"]: anno["category_id"] = dataset_id_to_contiguous_id[anno["category_id"]] return ret def load_cityscapes_semantic(image_dir, gt_dir): """ Args: image_dir (str): path to the raw dataset. e.g., "~/cityscapes/leftImg8bit/train". gt_dir (str): path to the raw annotations. e.g., "~/cityscapes/gtFine/train". Returns: list[dict]: a list of dict, each has "file_name" and "sem_seg_file_name". """ ret = [] # gt_dir is small and contain many small files. make sense to fetch to local first gt_dir = PathManager.get_local_path(gt_dir) for image_file, _, label_file, json_file in _get_cityscapes_files(image_dir, gt_dir): label_file = label_file.replace("labelIds", "labelTrainIds") with PathManager.open(json_file, "r") as f: jsonobj = json.load(f) ret.append( { "file_name": image_file, "sem_seg_file_name": label_file, "height": jsonobj["imgHeight"], "width": jsonobj["imgWidth"], } ) assert len(ret), f"No images found in {image_dir}!" assert PathManager.isfile( ret[0]["sem_seg_file_name"] ), "Please generate labelTrainIds.png with cityscapesscripts/preparation/createTrainIdLabelImgs.py" # noqa return ret def _cityscapes_files_to_dict(files, from_json, to_polygons): """ Parse cityscapes annotation files to a instance segmentation dataset dict. Args: files (tuple): consists of (image_file, instance_id_file, label_id_file, json_file) from_json (bool): whether to read annotations from the raw json file or the png files. to_polygons (bool): whether to represent the segmentation as polygons (COCO's format) instead of masks (cityscapes's format). Returns: A dict in Detectron2 Dataset format. """ from cityscapesscripts.helpers.labels import id2label, name2label image_file, instance_id_file, _, json_file = files annos = [] if from_json: from shapely.geometry import MultiPolygon, Polygon with PathManager.open(json_file, "r") as f: jsonobj = json.load(f) ret = { "file_name": image_file, "image_id": os.path.basename(image_file), "height": jsonobj["imgHeight"], "width": jsonobj["imgWidth"], } # `polygons_union` contains the union of all valid polygons. polygons_union = Polygon() # CityscapesScripts draw the polygons in sequential order # and each polygon *overwrites* existing ones. See # (https://github.com/mcordts/cityscapesScripts/blob/master/cityscapesscripts/preparation/json2instanceImg.py) # noqa # We use reverse order, and each polygon *avoids* early ones. # This will resolve the ploygon overlaps in the same way as CityscapesScripts. for obj in jsonobj["objects"][::-1]: if "deleted" in obj: # cityscapes data format specific continue label_name = obj["label"] try: label = name2label[label_name] except KeyError: if label_name.endswith("group"): # crowd area label = name2label[label_name[: -len("group")]] else: raise if label.id < 0: # cityscapes data format continue # Cityscapes's raw annotations uses integer coordinates # Therefore +0.5 here poly_coord = np.asarray(obj["polygon"], dtype="f4") + 0.5 # CityscapesScript uses PIL.ImageDraw.polygon to rasterize # polygons for evaluation. This function operates in integer space # and draws each pixel whose center falls into the polygon. # Therefore it draws a polygon which is 0.5 "fatter" in expectation. # We therefore dilate the input polygon by 0.5 as our input. poly = Polygon(poly_coord).buffer(0.5, resolution=4) if not label.hasInstances or label.ignoreInEval: # even if we won't store the polygon it still contributes to overlaps resolution polygons_union = polygons_union.union(poly) continue # Take non-overlapping part of the polygon poly_wo_overlaps = poly.difference(polygons_union) if poly_wo_overlaps.is_empty: continue polygons_union = polygons_union.union(poly) anno = {} anno["iscrowd"] = label_name.endswith("group") anno["category_id"] = label.id if isinstance(poly_wo_overlaps, Polygon): poly_list = [poly_wo_overlaps] elif isinstance(poly_wo_overlaps, MultiPolygon): poly_list = poly_wo_overlaps.geoms else: raise NotImplementedError("Unknown geometric structure {}".format(poly_wo_overlaps)) poly_coord = [] for poly_el in poly_list: # COCO API can work only with exterior boundaries now, hence we store only them. # TODO: store both exterior and interior boundaries once other parts of the # codebase support holes in polygons. poly_coord.append(list(chain(*poly_el.exterior.coords))) anno["segmentation"] = poly_coord (xmin, ymin, xmax, ymax) = poly_wo_overlaps.bounds anno["bbox"] = (xmin, ymin, xmax, ymax) anno["bbox_mode"] = BoxMode.XYXY_ABS annos.append(anno) else: # See also the official annotation parsing scripts at # https://github.com/mcordts/cityscapesScripts/blob/master/cityscapesscripts/evaluation/instances2dict.py # noqa with PathManager.open(instance_id_file, "rb") as f: inst_image = np.asarray(Image.open(f), order="F") # ids < 24 are stuff labels (filtering them first is about 5% faster) flattened_ids = np.unique(inst_image[inst_image >= 24]) ret = { "file_name": image_file, "image_id": os.path.basename(image_file), "height": inst_image.shape[0], "width": inst_image.shape[1], } for instance_id in flattened_ids: # For non-crowd annotations, instance_id // 1000 is the label_id # Crowd annotations have <1000 instance ids label_id = instance_id // 1000 if instance_id >= 1000 else instance_id label = id2label[label_id] if not label.hasInstances or label.ignoreInEval: continue anno = {} anno["iscrowd"] = instance_id < 1000 anno["category_id"] = label.id mask = np.asarray(inst_image == instance_id, dtype=np.uint8, order="F") inds = np.nonzero(mask) ymin, ymax = inds[0].min(), inds[0].max() xmin, xmax = inds[1].min(), inds[1].max() anno["bbox"] = (xmin, ymin, xmax, ymax) if xmax <= xmin or ymax <= ymin: continue anno["bbox_mode"] = BoxMode.XYXY_ABS if to_polygons: # This conversion comes from D4809743 and D5171122, # when Mask-RCNN was first developed. contours = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[ -2 ] polygons = [c.reshape(-1).tolist() for c in contours if len(c) >= 3] # opencv's can produce invalid polygons if len(polygons) == 0: continue anno["segmentation"] = polygons else: anno["segmentation"] = mask_util.encode(mask[:, :, None])[0] annos.append(anno) ret["annotations"] = annos return ret if __name__ == "__main__": """ Test the cityscapes dataset loader. Usage: python -m detectron2.data.datasets.cityscapes \ cityscapes/leftImg8bit/train cityscapes/gtFine/train """ import argparse parser = argparse.ArgumentParser() parser.add_argument("image_dir") parser.add_argument("gt_dir") parser.add_argument("--type", choices=["instance", "semantic"], default="instance") args = parser.parse_args() from detectron2.data.catalog import Metadata from detectron2.utils.visualizer import Visualizer from cityscapesscripts.helpers.labels import labels logger = setup_logger(name=__name__) dirname = "cityscapes-data-vis" os.makedirs(dirname, exist_ok=True) if args.type == "instance": dicts = load_cityscapes_instances( args.image_dir, args.gt_dir, from_json=True, to_polygons=True ) logger.info("Done loading {} samples.".format(len(dicts))) thing_classes = [k.name for k in labels if k.hasInstances and not k.ignoreInEval] meta = Metadata().set(thing_classes=thing_classes) else: dicts = load_cityscapes_semantic(args.image_dir, args.gt_dir) logger.info("Done loading {} samples.".format(len(dicts))) stuff_classes = [k.name for k in labels if k.trainId != 255] stuff_colors = [k.color for k in labels if k.trainId != 255] meta = Metadata().set(stuff_classes=stuff_classes, stuff_colors=stuff_colors) for d in dicts: img = np.array(Image.open(PathManager.open(d["file_name"], "rb"))) visualizer = Visualizer(img, metadata=meta) vis = visualizer.draw_dataset_dict(d) # cv2.imshow("a", vis.get_image()[:, :, ::-1]) # cv2.waitKey() fpath = os.path.join(dirname, os.path.basename(d["file_name"])) vis.save(fpath) ================================================ FILE: detectron2/detectron2/data/datasets/cityscapes_panoptic.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import json import logging import os from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.data.datasets.builtin_meta import CITYSCAPES_CATEGORIES from detectron2.utils.file_io import PathManager """ This file contains functions to register the Cityscapes panoptic dataset to the DatasetCatalog. """ logger = logging.getLogger(__name__) def get_cityscapes_panoptic_files(image_dir, gt_dir, json_info): files = [] # scan through the directory cities = PathManager.ls(image_dir) logger.info(f"{len(cities)} cities found in '{image_dir}'.") image_dict = {} for city in cities: city_img_dir = os.path.join(image_dir, city) for basename in PathManager.ls(city_img_dir): image_file = os.path.join(city_img_dir, basename) suffix = "_leftImg8bit.png" assert basename.endswith(suffix), basename basename = os.path.basename(basename)[: -len(suffix)] image_dict[basename] = image_file for ann in json_info["annotations"]: image_file = image_dict.get(ann["image_id"], None) assert image_file is not None, "No image {} found for annotation {}".format( ann["image_id"], ann["file_name"] ) label_file = os.path.join(gt_dir, ann["file_name"]) segments_info = ann["segments_info"] files.append((image_file, label_file, segments_info)) assert len(files), "No images found in {}".format(image_dir) assert PathManager.isfile(files[0][0]), files[0][0] assert PathManager.isfile(files[0][1]), files[0][1] return files def load_cityscapes_panoptic(image_dir, gt_dir, gt_json, meta): """ Args: image_dir (str): path to the raw dataset. e.g., "~/cityscapes/leftImg8bit/train". gt_dir (str): path to the raw annotations. e.g., "~/cityscapes/gtFine/cityscapes_panoptic_train". gt_json (str): path to the json file. e.g., "~/cityscapes/gtFine/cityscapes_panoptic_train.json". meta (dict): dictionary containing "thing_dataset_id_to_contiguous_id" and "stuff_dataset_id_to_contiguous_id" to map category ids to contiguous ids for training. Returns: list[dict]: a list of dicts in Detectron2 standard format. (See `Using Custom Datasets `_ ) """ def _convert_category_id(segment_info, meta): if segment_info["category_id"] in meta["thing_dataset_id_to_contiguous_id"]: segment_info["category_id"] = meta["thing_dataset_id_to_contiguous_id"][ segment_info["category_id"] ] else: segment_info["category_id"] = meta["stuff_dataset_id_to_contiguous_id"][ segment_info["category_id"] ] return segment_info assert os.path.exists( gt_json ), "Please run `python cityscapesscripts/preparation/createPanopticImgs.py` to generate label files." # noqa with open(gt_json) as f: json_info = json.load(f) files = get_cityscapes_panoptic_files(image_dir, gt_dir, json_info) ret = [] for image_file, label_file, segments_info in files: sem_label_file = ( image_file.replace("leftImg8bit", "gtFine").split(".")[0] + "_labelTrainIds.png" ) segments_info = [_convert_category_id(x, meta) for x in segments_info] ret.append( { "file_name": image_file, "image_id": "_".join( os.path.splitext(os.path.basename(image_file))[0].split("_")[:3] ), "sem_seg_file_name": sem_label_file, "pan_seg_file_name": label_file, "segments_info": segments_info, } ) assert len(ret), f"No images found in {image_dir}!" assert PathManager.isfile( ret[0]["sem_seg_file_name"] ), "Please generate labelTrainIds.png with cityscapesscripts/preparation/createTrainIdLabelImgs.py" # noqa assert PathManager.isfile( ret[0]["pan_seg_file_name"] ), "Please generate panoptic annotation with python cityscapesscripts/preparation/createPanopticImgs.py" # noqa return ret _RAW_CITYSCAPES_PANOPTIC_SPLITS = { "cityscapes_fine_panoptic_train": ( "cityscapes/leftImg8bit/train", "cityscapes/gtFine/cityscapes_panoptic_train", "cityscapes/gtFine/cityscapes_panoptic_train.json", ), "cityscapes_fine_panoptic_val": ( "cityscapes/leftImg8bit/val", "cityscapes/gtFine/cityscapes_panoptic_val", "cityscapes/gtFine/cityscapes_panoptic_val.json", ), # "cityscapes_fine_panoptic_test": not supported yet } def register_all_cityscapes_panoptic(root): meta = {} # The following metadata maps contiguous id from [0, #thing categories + # #stuff categories) to their names and colors. We have to replica of the # same name and color under "thing_*" and "stuff_*" because the current # visualization function in D2 handles thing and class classes differently # due to some heuristic used in Panoptic FPN. We keep the same naming to # enable reusing existing visualization functions. thing_classes = [k["name"] for k in CITYSCAPES_CATEGORIES] thing_colors = [k["color"] for k in CITYSCAPES_CATEGORIES] stuff_classes = [k["name"] for k in CITYSCAPES_CATEGORIES] stuff_colors = [k["color"] for k in CITYSCAPES_CATEGORIES] meta["thing_classes"] = thing_classes meta["thing_colors"] = thing_colors meta["stuff_classes"] = stuff_classes meta["stuff_colors"] = stuff_colors # There are three types of ids in cityscapes panoptic segmentation: # (1) category id: like semantic segmentation, it is the class id for each # pixel. Since there are some classes not used in evaluation, the category # id is not always contiguous and thus we have two set of category ids: # - original category id: category id in the original dataset, mainly # used for evaluation. # - contiguous category id: [0, #classes), in order to train the classifier # (2) instance id: this id is used to differentiate different instances from # the same category. For "stuff" classes, the instance id is always 0; for # "thing" classes, the instance id starts from 1 and 0 is reserved for # ignored instances (e.g. crowd annotation). # (3) panoptic id: this is the compact id that encode both category and # instance id by: category_id * 1000 + instance_id. thing_dataset_id_to_contiguous_id = {} stuff_dataset_id_to_contiguous_id = {} for k in CITYSCAPES_CATEGORIES: if k["isthing"] == 1: thing_dataset_id_to_contiguous_id[k["id"]] = k["trainId"] else: stuff_dataset_id_to_contiguous_id[k["id"]] = k["trainId"] meta["thing_dataset_id_to_contiguous_id"] = thing_dataset_id_to_contiguous_id meta["stuff_dataset_id_to_contiguous_id"] = stuff_dataset_id_to_contiguous_id for key, (image_dir, gt_dir, gt_json) in _RAW_CITYSCAPES_PANOPTIC_SPLITS.items(): image_dir = os.path.join(root, image_dir) gt_dir = os.path.join(root, gt_dir) gt_json = os.path.join(root, gt_json) DatasetCatalog.register( key, lambda x=image_dir, y=gt_dir, z=gt_json: load_cityscapes_panoptic(x, y, z, meta) ) MetadataCatalog.get(key).set( panoptic_root=gt_dir, image_root=image_dir, panoptic_json=gt_json, gt_dir=gt_dir.replace("cityscapes_panoptic_", ""), evaluator_type="cityscapes_panoptic_seg", ignore_label=255, label_divisor=1000, **meta, ) ================================================ FILE: detectron2/detectron2/data/datasets/coco.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import contextlib import datetime import io import json import logging import numpy as np import os import shutil import pycocotools.mask as mask_util from fvcore.common.timer import Timer from iopath.common.file_io import file_lock from PIL import Image from detectron2.structures import Boxes, BoxMode, PolygonMasks, RotatedBoxes from detectron2.utils.file_io import PathManager from .. import DatasetCatalog, MetadataCatalog """ This file contains functions to parse COCO-format annotations into dicts in "Detectron2 format". """ logger = logging.getLogger(__name__) __all__ = ["load_coco_json", "load_sem_seg", "convert_to_coco_json", "register_coco_instances"] def load_coco_json(json_file, image_root, dataset_name=None, extra_annotation_keys=None): """ Load a json file with COCO's instances annotation format. Currently supports instance detection, instance segmentation, and person keypoints annotations. Args: json_file (str): full path to the json file in COCO instances annotation format. image_root (str or path-like): the directory where the images in this json file exists. dataset_name (str or None): the name of the dataset (e.g., coco_2017_train). When provided, this function will also do the following: * Put "thing_classes" into the metadata associated with this dataset. * Map the category ids into a contiguous range (needed by standard dataset format), and add "thing_dataset_id_to_contiguous_id" to the metadata associated with this dataset. This option should usually be provided, unless users need to load the original json content and apply more processing manually. extra_annotation_keys (list[str]): list of per-annotation keys that should also be loaded into the dataset dict (besides "iscrowd", "bbox", "keypoints", "category_id", "segmentation"). The values for these keys will be returned as-is. For example, the densepose annotations are loaded in this way. Returns: list[dict]: a list of dicts in Detectron2 standard dataset dicts format (See `Using Custom Datasets `_ ) when `dataset_name` is not None. If `dataset_name` is None, the returned `category_ids` may be incontiguous and may not conform to the Detectron2 standard format. Notes: 1. This function does not read the image files. The results do not have the "image" field. """ from pycocotools.coco import COCO timer = Timer() json_file = PathManager.get_local_path(json_file) with contextlib.redirect_stdout(io.StringIO()): coco_api = COCO(json_file) if timer.seconds() > 1: logger.info("Loading {} takes {:.2f} seconds.".format(json_file, timer.seconds())) id_map = None if dataset_name is not None: meta = MetadataCatalog.get(dataset_name) cat_ids = sorted(coco_api.getCatIds()) cats = coco_api.loadCats(cat_ids) # The categories in a custom json file may not be sorted. thing_classes = [c["name"] for c in sorted(cats, key=lambda x: x["id"])] meta.thing_classes = thing_classes # In COCO, certain category ids are artificially removed, # and by convention they are always ignored. # We deal with COCO's id issue and translate # the category ids to contiguous ids in [0, 80). # It works by looking at the "categories" field in the json, therefore # if users' own json also have incontiguous ids, we'll # apply this mapping as well but print a warning. if not (min(cat_ids) == 1 and max(cat_ids) == len(cat_ids)): if "coco" not in dataset_name: logger.warning( """ Category ids in annotations are not in [1, #categories]! We'll apply a mapping for you. """ ) id_map = {v: i for i, v in enumerate(cat_ids)} meta.thing_dataset_id_to_contiguous_id = id_map # sort indices for reproducible results img_ids = sorted(coco_api.imgs.keys()) # imgs is a list of dicts, each looks something like: # {'license': 4, # 'url': 'http://farm6.staticflickr.com/5454/9413846304_881d5e5c3b_z.jpg', # 'file_name': 'COCO_val2014_000000001268.jpg', # 'height': 427, # 'width': 640, # 'date_captured': '2013-11-17 05:57:24', # 'id': 1268} imgs = coco_api.loadImgs(img_ids) # anns is a list[list[dict]], where each dict is an annotation # record for an object. The inner list enumerates the objects in an image # and the outer list enumerates over images. Example of anns[0]: # [{'segmentation': [[192.81, # 247.09, # ... # 219.03, # 249.06]], # 'area': 1035.749, # 'iscrowd': 0, # 'image_id': 1268, # 'bbox': [192.81, 224.8, 74.73, 33.43], # 'category_id': 16, # 'id': 42986}, # ...] anns = [coco_api.imgToAnns[img_id] for img_id in img_ids] total_num_valid_anns = sum([len(x) for x in anns]) total_num_anns = len(coco_api.anns) if total_num_valid_anns < total_num_anns: logger.warning( f"{json_file} contains {total_num_anns} annotations, but only " f"{total_num_valid_anns} of them match to images in the file." ) if "minival" not in json_file: # The popular valminusminival & minival annotations for COCO2014 contain this bug. # However the ratio of buggy annotations there is tiny and does not affect accuracy. # Therefore we explicitly white-list them. ann_ids = [ann["id"] for anns_per_image in anns for ann in anns_per_image] assert len(set(ann_ids)) == len(ann_ids), "Annotation ids in '{}' are not unique!".format( json_file ) imgs_anns = list(zip(imgs, anns)) logger.info("Loaded {} images in COCO format from {}".format(len(imgs_anns), json_file)) dataset_dicts = [] ann_keys = ["iscrowd", "bbox", "keypoints", "category_id"] + (extra_annotation_keys or []) num_instances_without_valid_segmentation = 0 for (img_dict, anno_dict_list) in imgs_anns: record = {} record["file_name"] = os.path.join(image_root, img_dict["file_name"]) record["height"] = img_dict["height"] record["width"] = img_dict["width"] image_id = record["image_id"] = img_dict["id"] objs = [] for anno in anno_dict_list: # Check that the image_id in this annotation is the same as # the image_id we're looking at. # This fails only when the data parsing logic or the annotation file is buggy. # The original COCO valminusminival2014 & minival2014 annotation files # actually contains bugs that, together with certain ways of using COCO API, # can trigger this assertion. assert anno["image_id"] == image_id assert anno.get("ignore", 0) == 0, '"ignore" in COCO json file is not supported.' obj = {key: anno[key] for key in ann_keys if key in anno} if "bbox" in obj and len(obj["bbox"]) == 0: raise ValueError( f"One annotation of image {image_id} contains empty 'bbox' value! " "This json does not have valid COCO format." ) segm = anno.get("segmentation", None) if segm: # either list[list[float]] or dict(RLE) if isinstance(segm, dict): if isinstance(segm["counts"], list): # convert to compressed RLE segm = mask_util.frPyObjects(segm, *segm["size"]) else: # filter out invalid polygons (< 3 points) segm = [poly for poly in segm if len(poly) % 2 == 0 and len(poly) >= 6] if len(segm) == 0: num_instances_without_valid_segmentation += 1 continue # ignore this instance obj["segmentation"] = segm keypts = anno.get("keypoints", None) if keypts: # list[int] for idx, v in enumerate(keypts): if idx % 3 != 2: # COCO's segmentation coordinates are floating points in [0, H or W], # but keypoint coordinates are integers in [0, H-1 or W-1] # Therefore we assume the coordinates are "pixel indices" and # add 0.5 to convert to floating point coordinates. keypts[idx] = v + 0.5 obj["keypoints"] = keypts obj["bbox_mode"] = BoxMode.XYWH_ABS if id_map: annotation_category_id = obj["category_id"] try: obj["category_id"] = id_map[annotation_category_id] except KeyError as e: raise KeyError( f"Encountered category_id={annotation_category_id} " "but this id does not exist in 'categories' of the json file." ) from e objs.append(obj) record["annotations"] = objs dataset_dicts.append(record) if num_instances_without_valid_segmentation > 0: logger.warning( "Filtered out {} instances without valid segmentation. ".format( num_instances_without_valid_segmentation ) + "There might be issues in your dataset generation process. Please " "check https://detectron2.readthedocs.io/en/latest/tutorials/datasets.html carefully" ) return dataset_dicts def load_sem_seg(gt_root, image_root, gt_ext="png", image_ext="jpg"): """ Load semantic segmentation datasets. All files under "gt_root" with "gt_ext" extension are treated as ground truth annotations and all files under "image_root" with "image_ext" extension as input images. Ground truth and input images are matched using file paths relative to "gt_root" and "image_root" respectively without taking into account file extensions. This works for COCO as well as some other datasets. Args: gt_root (str): full path to ground truth semantic segmentation files. Semantic segmentation annotations are stored as images with integer values in pixels that represent corresponding semantic labels. image_root (str): the directory where the input images are. gt_ext (str): file extension for ground truth annotations. image_ext (str): file extension for input images. Returns: list[dict]: a list of dicts in detectron2 standard format without instance-level annotation. Notes: 1. This function does not read the image and ground truth files. The results do not have the "image" and "sem_seg" fields. """ # We match input images with ground truth based on their relative filepaths (without file # extensions) starting from 'image_root' and 'gt_root' respectively. def file2id(folder_path, file_path): # extract relative path starting from `folder_path` image_id = os.path.normpath(os.path.relpath(file_path, start=folder_path)) # remove file extension image_id = os.path.splitext(image_id)[0] return image_id input_files = sorted( (os.path.join(image_root, f) for f in PathManager.ls(image_root) if f.endswith(image_ext)), key=lambda file_path: file2id(image_root, file_path), ) gt_files = sorted( (os.path.join(gt_root, f) for f in PathManager.ls(gt_root) if f.endswith(gt_ext)), key=lambda file_path: file2id(gt_root, file_path), ) assert len(gt_files) > 0, "No annotations found in {}.".format(gt_root) # Use the intersection, so that val2017_100 annotations can run smoothly with val2017 images if len(input_files) != len(gt_files): logger.warn( "Directory {} and {} has {} and {} files, respectively.".format( image_root, gt_root, len(input_files), len(gt_files) ) ) input_basenames = [os.path.basename(f)[: -len(image_ext)] for f in input_files] gt_basenames = [os.path.basename(f)[: -len(gt_ext)] for f in gt_files] intersect = list(set(input_basenames) & set(gt_basenames)) # sort, otherwise each worker may obtain a list[dict] in different order intersect = sorted(intersect) logger.warn("Will use their intersection of {} files.".format(len(intersect))) input_files = [os.path.join(image_root, f + image_ext) for f in intersect] gt_files = [os.path.join(gt_root, f + gt_ext) for f in intersect] logger.info( "Loaded {} images with semantic segmentation from {}".format(len(input_files), image_root) ) dataset_dicts = [] for (img_path, gt_path) in zip(input_files, gt_files): record = {} record["file_name"] = img_path record["sem_seg_file_name"] = gt_path dataset_dicts.append(record) return dataset_dicts def convert_to_coco_dict(dataset_name): """ Convert an instance detection/segmentation or keypoint detection dataset in detectron2's standard format into COCO json format. Generic dataset description can be found here: https://detectron2.readthedocs.io/tutorials/datasets.html#register-a-dataset COCO data format description can be found here: http://cocodataset.org/#format-data Args: dataset_name (str): name of the source dataset Must be registered in DatastCatalog and in detectron2's standard format. Must have corresponding metadata "thing_classes" Returns: coco_dict: serializable dict in COCO json format """ dataset_dicts = DatasetCatalog.get(dataset_name) metadata = MetadataCatalog.get(dataset_name) # unmap the category mapping ids for COCO if hasattr(metadata, "thing_dataset_id_to_contiguous_id"): reverse_id_mapping = {v: k for k, v in metadata.thing_dataset_id_to_contiguous_id.items()} reverse_id_mapper = lambda contiguous_id: reverse_id_mapping[contiguous_id] # noqa else: reverse_id_mapper = lambda contiguous_id: contiguous_id # noqa categories = [ {"id": reverse_id_mapper(id), "name": name} for id, name in enumerate(metadata.thing_classes) ] logger.info("Converting dataset dicts into COCO format") coco_images = [] coco_annotations = [] for image_id, image_dict in enumerate(dataset_dicts): coco_image = { "id": image_dict.get("image_id", image_id), "width": int(image_dict["width"]), "height": int(image_dict["height"]), "file_name": str(image_dict["file_name"]), } coco_images.append(coco_image) anns_per_image = image_dict.get("annotations", []) for annotation in anns_per_image: # create a new dict with only COCO fields coco_annotation = {} # COCO requirement: XYWH box format for axis-align and XYWHA for rotated bbox = annotation["bbox"] if isinstance(bbox, np.ndarray): if bbox.ndim != 1: raise ValueError(f"bbox has to be 1-dimensional. Got shape={bbox.shape}.") bbox = bbox.tolist() if len(bbox) not in [4, 5]: raise ValueError(f"bbox has to has length 4 or 5. Got {bbox}.") from_bbox_mode = annotation["bbox_mode"] to_bbox_mode = BoxMode.XYWH_ABS if len(bbox) == 4 else BoxMode.XYWHA_ABS bbox = BoxMode.convert(bbox, from_bbox_mode, to_bbox_mode) # COCO requirement: instance area if "segmentation" in annotation: # Computing areas for instances by counting the pixels segmentation = annotation["segmentation"] # TODO: check segmentation type: RLE, BinaryMask or Polygon if isinstance(segmentation, list): polygons = PolygonMasks([segmentation]) area = polygons.area()[0].item() elif isinstance(segmentation, dict): # RLE area = mask_util.area(segmentation).item() else: raise TypeError(f"Unknown segmentation type {type(segmentation)}!") else: # Computing areas using bounding boxes if to_bbox_mode == BoxMode.XYWH_ABS: bbox_xy = BoxMode.convert(bbox, to_bbox_mode, BoxMode.XYXY_ABS) area = Boxes([bbox_xy]).area()[0].item() else: area = RotatedBoxes([bbox]).area()[0].item() if "keypoints" in annotation: keypoints = annotation["keypoints"] # list[int] for idx, v in enumerate(keypoints): if idx % 3 != 2: # COCO's segmentation coordinates are floating points in [0, H or W], # but keypoint coordinates are integers in [0, H-1 or W-1] # For COCO format consistency we substract 0.5 # https://github.com/facebookresearch/detectron2/pull/175#issuecomment-551202163 keypoints[idx] = v - 0.5 if "num_keypoints" in annotation: num_keypoints = annotation["num_keypoints"] else: num_keypoints = sum(kp > 0 for kp in keypoints[2::3]) # COCO requirement: # linking annotations to images # "id" field must start with 1 coco_annotation["id"] = len(coco_annotations) + 1 coco_annotation["image_id"] = coco_image["id"] coco_annotation["bbox"] = [round(float(x), 3) for x in bbox] coco_annotation["area"] = float(area) coco_annotation["iscrowd"] = int(annotation.get("iscrowd", 0)) coco_annotation["category_id"] = int(reverse_id_mapper(annotation["category_id"])) # Add optional fields if "keypoints" in annotation: coco_annotation["keypoints"] = keypoints coco_annotation["num_keypoints"] = num_keypoints if "segmentation" in annotation: seg = coco_annotation["segmentation"] = annotation["segmentation"] if isinstance(seg, dict): # RLE counts = seg["counts"] if not isinstance(counts, str): # make it json-serializable seg["counts"] = counts.decode("ascii") coco_annotations.append(coco_annotation) logger.info( "Conversion finished, " f"#images: {len(coco_images)}, #annotations: {len(coco_annotations)}" ) info = { "date_created": str(datetime.datetime.now()), "description": "Automatically generated COCO json file for Detectron2.", } coco_dict = {"info": info, "images": coco_images, "categories": categories, "licenses": None} if len(coco_annotations) > 0: coco_dict["annotations"] = coco_annotations return coco_dict def convert_to_coco_json(dataset_name, output_file, allow_cached=True): """ Converts dataset into COCO format and saves it to a json file. dataset_name must be registered in DatasetCatalog and in detectron2's standard format. Args: dataset_name: reference from the config file to the catalogs must be registered in DatasetCatalog and in detectron2's standard format output_file: path of json file that will be saved to allow_cached: if json file is already present then skip conversion """ # TODO: The dataset or the conversion script *may* change, # a checksum would be useful for validating the cached data PathManager.mkdirs(os.path.dirname(output_file)) with file_lock(output_file): if PathManager.exists(output_file) and allow_cached: logger.warning( f"Using previously cached COCO format annotations at '{output_file}'. " "You need to clear the cache file if your dataset has been modified." ) else: logger.info(f"Converting annotations of dataset '{dataset_name}' to COCO format ...)") coco_dict = convert_to_coco_dict(dataset_name) logger.info(f"Caching COCO format annotations at '{output_file}' ...") tmp_file = output_file + ".tmp" with PathManager.open(tmp_file, "w") as f: json.dump(coco_dict, f) shutil.move(tmp_file, output_file) def register_coco_instances(name, metadata, json_file, image_root): """ Register a dataset in COCO's json annotation format for instance detection, instance segmentation and keypoint detection. (i.e., Type 1 and 2 in http://cocodataset.org/#format-data. `instances*.json` and `person_keypoints*.json` in the dataset). This is an example of how to register a new dataset. You can do something similar to this function, to register new datasets. Args: name (str): the name that identifies a dataset, e.g. "coco_2014_train". metadata (dict): extra metadata associated with this dataset. You can leave it as an empty dict. json_file (str): path to the json instance annotation file. image_root (str or path-like): directory which contains all the images. """ assert isinstance(name, str), name assert isinstance(json_file, (str, os.PathLike)), json_file assert isinstance(image_root, (str, os.PathLike)), image_root # 1. register a function which returns dicts DatasetCatalog.register(name, lambda: load_coco_json(json_file, image_root, name)) # 2. Optionally, add metadata about this dataset, # since they might be useful in evaluation, visualization or logging MetadataCatalog.get(name).set( json_file=json_file, image_root=image_root, evaluator_type="coco", **metadata ) if __name__ == "__main__": """ Test the COCO json dataset loader. Usage: python -m detectron2.data.datasets.coco \ path/to/json path/to/image_root dataset_name "dataset_name" can be "coco_2014_minival_100", or other pre-registered ones """ from detectron2.utils.logger import setup_logger from detectron2.utils.visualizer import Visualizer import detectron2.data.datasets # noqa # add pre-defined metadata import sys logger = setup_logger(name=__name__) assert sys.argv[3] in DatasetCatalog.list() meta = MetadataCatalog.get(sys.argv[3]) dicts = load_coco_json(sys.argv[1], sys.argv[2], sys.argv[3]) logger.info("Done loading {} samples.".format(len(dicts))) dirname = "coco-data-vis" os.makedirs(dirname, exist_ok=True) for d in dicts: img = np.array(Image.open(d["file_name"])) visualizer = Visualizer(img, metadata=meta) vis = visualizer.draw_dataset_dict(d) fpath = os.path.join(dirname, os.path.basename(d["file_name"])) vis.save(fpath) ================================================ FILE: detectron2/detectron2/data/datasets/coco_panoptic.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import json import os from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.utils.file_io import PathManager from .coco import load_coco_json, load_sem_seg __all__ = ["register_coco_panoptic", "register_coco_panoptic_separated"] def load_coco_panoptic_json(json_file, image_dir, gt_dir, meta): """ Args: image_dir (str): path to the raw dataset. e.g., "~/coco/train2017". gt_dir (str): path to the raw annotations. e.g., "~/coco/panoptic_train2017". json_file (str): path to the json file. e.g., "~/coco/annotations/panoptic_train2017.json". Returns: list[dict]: a list of dicts in Detectron2 standard format. (See `Using Custom Datasets `_ ) """ def _convert_category_id(segment_info, meta): if segment_info["category_id"] in meta["thing_dataset_id_to_contiguous_id"]: segment_info["category_id"] = meta["thing_dataset_id_to_contiguous_id"][ segment_info["category_id"] ] segment_info["isthing"] = True else: segment_info["category_id"] = meta["stuff_dataset_id_to_contiguous_id"][ segment_info["category_id"] ] segment_info["isthing"] = False return segment_info with PathManager.open(json_file) as f: json_info = json.load(f) ret = [] for ann in json_info["annotations"]: image_id = int(ann["image_id"]) # TODO: currently we assume image and label has the same filename but # different extension, and images have extension ".jpg" for COCO. Need # to make image extension a user-provided argument if we extend this # function to support other COCO-like datasets. image_file = os.path.join(image_dir, os.path.splitext(ann["file_name"])[0] + ".jpg") label_file = os.path.join(gt_dir, ann["file_name"]) segments_info = [_convert_category_id(x, meta) for x in ann["segments_info"]] ret.append( { "file_name": image_file, "image_id": image_id, "pan_seg_file_name": label_file, "segments_info": segments_info, } ) assert len(ret), f"No images found in {image_dir}!" assert PathManager.isfile(ret[0]["file_name"]), ret[0]["file_name"] assert PathManager.isfile(ret[0]["pan_seg_file_name"]), ret[0]["pan_seg_file_name"] return ret def register_coco_panoptic( name, metadata, image_root, panoptic_root, panoptic_json, instances_json=None ): """ Register a "standard" version of COCO panoptic segmentation dataset named `name`. The dictionaries in this registered dataset follows detectron2's standard format. Hence it's called "standard". Args: name (str): the name that identifies a dataset, e.g. "coco_2017_train_panoptic" metadata (dict): extra metadata associated with this dataset. image_root (str): directory which contains all the images panoptic_root (str): directory which contains panoptic annotation images in COCO format panoptic_json (str): path to the json panoptic annotation file in COCO format sem_seg_root (none): not used, to be consistent with `register_coco_panoptic_separated`. instances_json (str): path to the json instance annotation file """ panoptic_name = name DatasetCatalog.register( panoptic_name, lambda: load_coco_panoptic_json(panoptic_json, image_root, panoptic_root, metadata), ) MetadataCatalog.get(panoptic_name).set( panoptic_root=panoptic_root, image_root=image_root, panoptic_json=panoptic_json, json_file=instances_json, evaluator_type="coco_panoptic_seg", ignore_label=255, label_divisor=1000, **metadata, ) def register_coco_panoptic_separated( name, metadata, image_root, panoptic_root, panoptic_json, sem_seg_root, instances_json ): """ Register a "separated" version of COCO panoptic segmentation dataset named `name`. The annotations in this registered dataset will contain both instance annotations and semantic annotations, each with its own contiguous ids. Hence it's called "separated". It follows the setting used by the PanopticFPN paper: 1. The instance annotations directly come from polygons in the COCO instances annotation task, rather than from the masks in the COCO panoptic annotations. The two format have small differences: Polygons in the instance annotations may have overlaps. The mask annotations are produced by labeling the overlapped polygons with depth ordering. 2. The semantic annotations are converted from panoptic annotations, where all "things" are assigned a semantic id of 0. All semantic categories will therefore have ids in contiguous range [1, #stuff_categories]. This function will also register a pure semantic segmentation dataset named ``name + '_stuffonly'``. Args: name (str): the name that identifies a dataset, e.g. "coco_2017_train_panoptic" metadata (dict): extra metadata associated with this dataset. image_root (str): directory which contains all the images panoptic_root (str): directory which contains panoptic annotation images panoptic_json (str): path to the json panoptic annotation file sem_seg_root (str): directory which contains all the ground truth segmentation annotations. instances_json (str): path to the json instance annotation file """ panoptic_name = name + "_separated" DatasetCatalog.register( panoptic_name, lambda: merge_to_panoptic( load_coco_json(instances_json, image_root, panoptic_name), load_sem_seg(sem_seg_root, image_root), ), ) MetadataCatalog.get(panoptic_name).set( panoptic_root=panoptic_root, image_root=image_root, panoptic_json=panoptic_json, sem_seg_root=sem_seg_root, json_file=instances_json, # TODO rename evaluator_type="coco_panoptic_seg", ignore_label=255, **metadata, ) semantic_name = name + "_stuffonly" DatasetCatalog.register(semantic_name, lambda: load_sem_seg(sem_seg_root, image_root)) MetadataCatalog.get(semantic_name).set( sem_seg_root=sem_seg_root, image_root=image_root, evaluator_type="sem_seg", ignore_label=255, **metadata, ) def merge_to_panoptic(detection_dicts, sem_seg_dicts): """ Create dataset dicts for panoptic segmentation, by merging two dicts using "file_name" field to match their entries. Args: detection_dicts (list[dict]): lists of dicts for object detection or instance segmentation. sem_seg_dicts (list[dict]): lists of dicts for semantic segmentation. Returns: list[dict] (one per input image): Each dict contains all (key, value) pairs from dicts in both detection_dicts and sem_seg_dicts that correspond to the same image. The function assumes that the same key in different dicts has the same value. """ results = [] sem_seg_file_to_entry = {x["file_name"]: x for x in sem_seg_dicts} assert len(sem_seg_file_to_entry) > 0 for det_dict in detection_dicts: dic = copy.copy(det_dict) dic.update(sem_seg_file_to_entry[dic["file_name"]]) results.append(dic) return results if __name__ == "__main__": """ Test the COCO panoptic dataset loader. Usage: python -m detectron2.data.datasets.coco_panoptic \ path/to/image_root path/to/panoptic_root path/to/panoptic_json dataset_name 10 "dataset_name" can be "coco_2017_train_panoptic", or other pre-registered ones """ from detectron2.utils.logger import setup_logger from detectron2.utils.visualizer import Visualizer import detectron2.data.datasets # noqa # add pre-defined metadata import sys from PIL import Image import numpy as np logger = setup_logger(name=__name__) assert sys.argv[4] in DatasetCatalog.list() meta = MetadataCatalog.get(sys.argv[4]) dicts = load_coco_panoptic_json(sys.argv[3], sys.argv[1], sys.argv[2], meta.as_dict()) logger.info("Done loading {} samples.".format(len(dicts))) dirname = "coco-data-vis" os.makedirs(dirname, exist_ok=True) num_imgs_to_vis = int(sys.argv[5]) for i, d in enumerate(dicts): img = np.array(Image.open(d["file_name"])) visualizer = Visualizer(img, metadata=meta) vis = visualizer.draw_dataset_dict(d) fpath = os.path.join(dirname, os.path.basename(d["file_name"])) vis.save(fpath) if i + 1 >= num_imgs_to_vis: break ================================================ FILE: detectron2/detectron2/data/datasets/lvis.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import os from fvcore.common.timer import Timer from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.structures import BoxMode from detectron2.utils.file_io import PathManager from .builtin_meta import _get_coco_instances_meta from .lvis_v0_5_categories import LVIS_CATEGORIES as LVIS_V0_5_CATEGORIES from .lvis_v1_categories import LVIS_CATEGORIES as LVIS_V1_CATEGORIES from .lvis_v1_category_image_count import LVIS_CATEGORY_IMAGE_COUNT as LVIS_V1_CATEGORY_IMAGE_COUNT """ This file contains functions to parse LVIS-format annotations into dicts in the "Detectron2 format". """ logger = logging.getLogger(__name__) __all__ = ["load_lvis_json", "register_lvis_instances", "get_lvis_instances_meta"] def register_lvis_instances(name, metadata, json_file, image_root): """ Register a dataset in LVIS's json annotation format for instance detection and segmentation. Args: name (str): a name that identifies the dataset, e.g. "lvis_v0.5_train". metadata (dict): extra metadata associated with this dataset. It can be an empty dict. json_file (str): path to the json instance annotation file. image_root (str or path-like): directory which contains all the images. """ DatasetCatalog.register(name, lambda: load_lvis_json(json_file, image_root, name)) MetadataCatalog.get(name).set( json_file=json_file, image_root=image_root, evaluator_type="lvis", **metadata ) def load_lvis_json(json_file, image_root, dataset_name=None, extra_annotation_keys=None): """ Load a json file in LVIS's annotation format. Args: json_file (str): full path to the LVIS json annotation file. image_root (str): the directory where the images in this json file exists. dataset_name (str): the name of the dataset (e.g., "lvis_v0.5_train"). If provided, this function will put "thing_classes" into the metadata associated with this dataset. extra_annotation_keys (list[str]): list of per-annotation keys that should also be loaded into the dataset dict (besides "bbox", "bbox_mode", "category_id", "segmentation"). The values for these keys will be returned as-is. Returns: list[dict]: a list of dicts in Detectron2 standard format. (See `Using Custom Datasets `_ ) Notes: 1. This function does not read the image files. The results do not have the "image" field. """ from lvis import LVIS json_file = PathManager.get_local_path(json_file) timer = Timer() lvis_api = LVIS(json_file) if timer.seconds() > 1: logger.info("Loading {} takes {:.2f} seconds.".format(json_file, timer.seconds())) if dataset_name is not None: meta = get_lvis_instances_meta(dataset_name) MetadataCatalog.get(dataset_name).set(**meta) # sort indices for reproducible results img_ids = sorted(lvis_api.imgs.keys()) # imgs is a list of dicts, each looks something like: # {'license': 4, # 'url': 'http://farm6.staticflickr.com/5454/9413846304_881d5e5c3b_z.jpg', # 'file_name': 'COCO_val2014_000000001268.jpg', # 'height': 427, # 'width': 640, # 'date_captured': '2013-11-17 05:57:24', # 'id': 1268} imgs = lvis_api.load_imgs(img_ids) # anns is a list[list[dict]], where each dict is an annotation # record for an object. The inner list enumerates the objects in an image # and the outer list enumerates over images. Example of anns[0]: # [{'segmentation': [[192.81, # 247.09, # ... # 219.03, # 249.06]], # 'area': 1035.749, # 'image_id': 1268, # 'bbox': [192.81, 224.8, 74.73, 33.43], # 'category_id': 16, # 'id': 42986}, # ...] anns = [lvis_api.img_ann_map[img_id] for img_id in img_ids] # Sanity check that each annotation has a unique id ann_ids = [ann["id"] for anns_per_image in anns for ann in anns_per_image] assert len(set(ann_ids)) == len(ann_ids), "Annotation ids in '{}' are not unique".format( json_file ) imgs_anns = list(zip(imgs, anns)) logger.info("Loaded {} images in the LVIS format from {}".format(len(imgs_anns), json_file)) if extra_annotation_keys: logger.info( "The following extra annotation keys will be loaded: {} ".format(extra_annotation_keys) ) else: extra_annotation_keys = [] def get_file_name(img_root, img_dict): # Determine the path including the split folder ("train2017", "val2017", "test2017") from # the coco_url field. Example: # 'coco_url': 'http://images.cocodataset.org/train2017/000000155379.jpg' split_folder, file_name = img_dict["coco_url"].split("/")[-2:] return os.path.join(img_root + split_folder, file_name) dataset_dicts = [] for (img_dict, anno_dict_list) in imgs_anns: record = {} record["file_name"] = get_file_name(image_root, img_dict) record["height"] = img_dict["height"] record["width"] = img_dict["width"] record["not_exhaustive_category_ids"] = img_dict.get("not_exhaustive_category_ids", []) record["neg_category_ids"] = img_dict.get("neg_category_ids", []) image_id = record["image_id"] = img_dict["id"] objs = [] for anno in anno_dict_list: # Check that the image_id in this annotation is the same as # the image_id we're looking at. # This fails only when the data parsing logic or the annotation file is buggy. assert anno["image_id"] == image_id obj = {"bbox": anno["bbox"], "bbox_mode": BoxMode.XYWH_ABS} # LVIS data loader can be used to load COCO dataset categories. In this case `meta` # variable will have a field with COCO-specific category mapping. if dataset_name is not None and "thing_dataset_id_to_contiguous_id" in meta: obj["category_id"] = meta["thing_dataset_id_to_contiguous_id"][anno["category_id"]] else: obj["category_id"] = anno["category_id"] - 1 # Convert 1-indexed to 0-indexed segm = anno["segmentation"] # list[list[float]] # filter out invalid polygons (< 3 points) valid_segm = [poly for poly in segm if len(poly) % 2 == 0 and len(poly) >= 6] assert len(segm) == len( valid_segm ), "Annotation contains an invalid polygon with < 3 points" assert len(segm) > 0 obj["segmentation"] = segm for extra_ann_key in extra_annotation_keys: obj[extra_ann_key] = anno[extra_ann_key] objs.append(obj) record["annotations"] = objs dataset_dicts.append(record) return dataset_dicts def get_lvis_instances_meta(dataset_name): """ Load LVIS metadata. Args: dataset_name (str): LVIS dataset name without the split name (e.g., "lvis_v0.5"). Returns: dict: LVIS metadata with keys: thing_classes """ if "cocofied" in dataset_name: return _get_coco_instances_meta() if "v0.5" in dataset_name: return _get_lvis_instances_meta_v0_5() elif "v1" in dataset_name: return _get_lvis_instances_meta_v1() raise ValueError("No built-in metadata for dataset {}".format(dataset_name)) def _get_lvis_instances_meta_v0_5(): assert len(LVIS_V0_5_CATEGORIES) == 1230 cat_ids = [k["id"] for k in LVIS_V0_5_CATEGORIES] assert min(cat_ids) == 1 and max(cat_ids) == len( cat_ids ), "Category ids are not in [1, #categories], as expected" # Ensure that the category list is sorted by id lvis_categories = sorted(LVIS_V0_5_CATEGORIES, key=lambda x: x["id"]) thing_classes = [k["synonyms"][0] for k in lvis_categories] meta = {"thing_classes": thing_classes} return meta def _get_lvis_instances_meta_v1(): assert len(LVIS_V1_CATEGORIES) == 1203 cat_ids = [k["id"] for k in LVIS_V1_CATEGORIES] assert min(cat_ids) == 1 and max(cat_ids) == len( cat_ids ), "Category ids are not in [1, #categories], as expected" # Ensure that the category list is sorted by id lvis_categories = sorted(LVIS_V1_CATEGORIES, key=lambda x: x["id"]) thing_classes = [k["synonyms"][0] for k in lvis_categories] meta = {"thing_classes": thing_classes, "class_image_count": LVIS_V1_CATEGORY_IMAGE_COUNT} return meta if __name__ == "__main__": """ Test the LVIS json dataset loader. Usage: python -m detectron2.data.datasets.lvis \ path/to/json path/to/image_root dataset_name vis_limit """ import sys import numpy as np from detectron2.utils.logger import setup_logger from PIL import Image import detectron2.data.datasets # noqa # add pre-defined metadata from detectron2.utils.visualizer import Visualizer logger = setup_logger(name=__name__) meta = MetadataCatalog.get(sys.argv[3]) dicts = load_lvis_json(sys.argv[1], sys.argv[2], sys.argv[3]) logger.info("Done loading {} samples.".format(len(dicts))) dirname = "lvis-data-vis" os.makedirs(dirname, exist_ok=True) for d in dicts[: int(sys.argv[4])]: img = np.array(Image.open(d["file_name"])) visualizer = Visualizer(img, metadata=meta) vis = visualizer.draw_dataset_dict(d) fpath = os.path.join(dirname, os.path.basename(d["file_name"])) vis.save(fpath) ================================================ FILE: detectron2/detectron2/data/datasets/lvis_v0_5_categories.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # Autogen with # with open("lvis_v0.5_val.json", "r") as f: # a = json.load(f) # c = a["categories"] # for x in c: # del x["image_count"] # del x["instance_count"] # LVIS_CATEGORIES = repr(c) + " # noqa" # fmt: off LVIS_CATEGORIES = [{'frequency': 'r', 'id': 1, 'synset': 'acorn.n.01', 'synonyms': ['acorn'], 'def': 'nut from an oak tree', 'name': 'acorn'}, {'frequency': 'c', 'id': 2, 'synset': 'aerosol.n.02', 'synonyms': ['aerosol_can', 'spray_can'], 'def': 'a dispenser that holds a substance under pressure', 'name': 'aerosol_can'}, {'frequency': 'f', 'id': 3, 'synset': 'air_conditioner.n.01', 'synonyms': ['air_conditioner'], 'def': 'a machine that keeps air cool and dry', 'name': 'air_conditioner'}, {'frequency': 'f', 'id': 4, 'synset': 'airplane.n.01', 'synonyms': ['airplane', 'aeroplane'], 'def': 'an aircraft that has a fixed wing and is powered by propellers or jets', 'name': 'airplane'}, {'frequency': 'c', 'id': 5, 'synset': 'alarm_clock.n.01', 'synonyms': ['alarm_clock'], 'def': 'a clock that wakes a sleeper at some preset time', 'name': 'alarm_clock'}, {'frequency': 'c', 'id': 6, 'synset': 'alcohol.n.01', 'synonyms': ['alcohol', 'alcoholic_beverage'], 'def': 'a liquor or brew containing alcohol as the active agent', 'name': 'alcohol'}, {'frequency': 'r', 'id': 7, 'synset': 'alligator.n.02', 'synonyms': ['alligator', 'gator'], 'def': 'amphibious reptiles related to crocodiles but with shorter broader snouts', 'name': 'alligator'}, {'frequency': 'c', 'id': 8, 'synset': 'almond.n.02', 'synonyms': ['almond'], 'def': 'oval-shaped edible seed of the almond tree', 'name': 'almond'}, {'frequency': 'c', 'id': 9, 'synset': 'ambulance.n.01', 'synonyms': ['ambulance'], 'def': 'a vehicle that takes people to and from hospitals', 'name': 'ambulance'}, {'frequency': 'r', 'id': 10, 'synset': 'amplifier.n.01', 'synonyms': ['amplifier'], 'def': 'electronic equipment that increases strength of signals', 'name': 'amplifier'}, {'frequency': 'c', 'id': 11, 'synset': 'anklet.n.03', 'synonyms': ['anklet', 'ankle_bracelet'], 'def': 'an ornament worn around the ankle', 'name': 'anklet'}, {'frequency': 'f', 'id': 12, 'synset': 'antenna.n.01', 'synonyms': ['antenna', 'aerial', 'transmitting_aerial'], 'def': 'an electrical device that sends or receives radio or television signals', 'name': 'antenna'}, {'frequency': 'f', 'id': 13, 'synset': 'apple.n.01', 'synonyms': ['apple'], 'def': 'fruit with red or yellow or green skin and sweet to tart crisp whitish flesh', 'name': 'apple'}, {'frequency': 'r', 'id': 14, 'synset': 'apple_juice.n.01', 'synonyms': ['apple_juice'], 'def': 'the juice of apples', 'name': 'apple_juice'}, {'frequency': 'r', 'id': 15, 'synset': 'applesauce.n.01', 'synonyms': ['applesauce'], 'def': 'puree of stewed apples usually sweetened and spiced', 'name': 'applesauce'}, {'frequency': 'r', 'id': 16, 'synset': 'apricot.n.02', 'synonyms': ['apricot'], 'def': 'downy yellow to rosy-colored fruit resembling a small peach', 'name': 'apricot'}, {'frequency': 'f', 'id': 17, 'synset': 'apron.n.01', 'synonyms': ['apron'], 'def': 'a garment of cloth that is tied about the waist and worn to protect clothing', 'name': 'apron'}, {'frequency': 'c', 'id': 18, 'synset': 'aquarium.n.01', 'synonyms': ['aquarium', 'fish_tank'], 'def': 'a tank/pool/bowl filled with water for keeping live fish and underwater animals', 'name': 'aquarium'}, {'frequency': 'c', 'id': 19, 'synset': 'armband.n.02', 'synonyms': ['armband'], 'def': 'a band worn around the upper arm', 'name': 'armband'}, {'frequency': 'f', 'id': 20, 'synset': 'armchair.n.01', 'synonyms': ['armchair'], 'def': 'chair with a support on each side for arms', 'name': 'armchair'}, {'frequency': 'r', 'id': 21, 'synset': 'armoire.n.01', 'synonyms': ['armoire'], 'def': 'a large wardrobe or cabinet', 'name': 'armoire'}, {'frequency': 'r', 'id': 22, 'synset': 'armor.n.01', 'synonyms': ['armor', 'armour'], 'def': 'protective covering made of metal and used in combat', 'name': 'armor'}, {'frequency': 'c', 'id': 23, 'synset': 'artichoke.n.02', 'synonyms': ['artichoke'], 'def': 'a thistlelike flower head with edible fleshy leaves and heart', 'name': 'artichoke'}, {'frequency': 'f', 'id': 24, 'synset': 'ashcan.n.01', 'synonyms': ['trash_can', 'garbage_can', 'wastebin', 'dustbin', 'trash_barrel', 'trash_bin'], 'def': 'a bin that holds rubbish until it is collected', 'name': 'trash_can'}, {'frequency': 'c', 'id': 25, 'synset': 'ashtray.n.01', 'synonyms': ['ashtray'], 'def': "a receptacle for the ash from smokers' cigars or cigarettes", 'name': 'ashtray'}, {'frequency': 'c', 'id': 26, 'synset': 'asparagus.n.02', 'synonyms': ['asparagus'], 'def': 'edible young shoots of the asparagus plant', 'name': 'asparagus'}, {'frequency': 'c', 'id': 27, 'synset': 'atomizer.n.01', 'synonyms': ['atomizer', 'atomiser', 'spray', 'sprayer', 'nebulizer', 'nebuliser'], 'def': 'a dispenser that turns a liquid (such as perfume) into a fine mist', 'name': 'atomizer'}, {'frequency': 'c', 'id': 28, 'synset': 'avocado.n.01', 'synonyms': ['avocado'], 'def': 'a pear-shaped fruit with green or blackish skin and rich yellowish pulp enclosing a single large seed', 'name': 'avocado'}, {'frequency': 'c', 'id': 29, 'synset': 'award.n.02', 'synonyms': ['award', 'accolade'], 'def': 'a tangible symbol signifying approval or distinction', 'name': 'award'}, {'frequency': 'f', 'id': 30, 'synset': 'awning.n.01', 'synonyms': ['awning'], 'def': 'a canopy made of canvas to shelter people or things from rain or sun', 'name': 'awning'}, {'frequency': 'r', 'id': 31, 'synset': 'ax.n.01', 'synonyms': ['ax', 'axe'], 'def': 'an edge tool with a heavy bladed head mounted across a handle', 'name': 'ax'}, {'frequency': 'f', 'id': 32, 'synset': 'baby_buggy.n.01', 'synonyms': ['baby_buggy', 'baby_carriage', 'perambulator', 'pram', 'stroller'], 'def': 'a small vehicle with four wheels in which a baby or child is pushed around', 'name': 'baby_buggy'}, {'frequency': 'c', 'id': 33, 'synset': 'backboard.n.01', 'synonyms': ['basketball_backboard'], 'def': 'a raised vertical board with basket attached; used to play basketball', 'name': 'basketball_backboard'}, {'frequency': 'f', 'id': 34, 'synset': 'backpack.n.01', 'synonyms': ['backpack', 'knapsack', 'packsack', 'rucksack', 'haversack'], 'def': 'a bag carried by a strap on your back or shoulder', 'name': 'backpack'}, {'frequency': 'f', 'id': 35, 'synset': 'bag.n.04', 'synonyms': ['handbag', 'purse', 'pocketbook'], 'def': 'a container used for carrying money and small personal items or accessories', 'name': 'handbag'}, {'frequency': 'f', 'id': 36, 'synset': 'bag.n.06', 'synonyms': ['suitcase', 'baggage', 'luggage'], 'def': 'cases used to carry belongings when traveling', 'name': 'suitcase'}, {'frequency': 'c', 'id': 37, 'synset': 'bagel.n.01', 'synonyms': ['bagel', 'beigel'], 'def': 'glazed yeast-raised doughnut-shaped roll with hard crust', 'name': 'bagel'}, {'frequency': 'r', 'id': 38, 'synset': 'bagpipe.n.01', 'synonyms': ['bagpipe'], 'def': 'a tubular wind instrument; the player blows air into a bag and squeezes it out', 'name': 'bagpipe'}, {'frequency': 'r', 'id': 39, 'synset': 'baguet.n.01', 'synonyms': ['baguet', 'baguette'], 'def': 'narrow French stick loaf', 'name': 'baguet'}, {'frequency': 'r', 'id': 40, 'synset': 'bait.n.02', 'synonyms': ['bait', 'lure'], 'def': 'something used to lure fish or other animals into danger so they can be trapped or killed', 'name': 'bait'}, {'frequency': 'f', 'id': 41, 'synset': 'ball.n.06', 'synonyms': ['ball'], 'def': 'a spherical object used as a plaything', 'name': 'ball'}, {'frequency': 'r', 'id': 42, 'synset': 'ballet_skirt.n.01', 'synonyms': ['ballet_skirt', 'tutu'], 'def': 'very short skirt worn by ballerinas', 'name': 'ballet_skirt'}, {'frequency': 'f', 'id': 43, 'synset': 'balloon.n.01', 'synonyms': ['balloon'], 'def': 'large tough nonrigid bag filled with gas or heated air', 'name': 'balloon'}, {'frequency': 'c', 'id': 44, 'synset': 'bamboo.n.02', 'synonyms': ['bamboo'], 'def': 'woody tropical grass having hollow woody stems', 'name': 'bamboo'}, {'frequency': 'f', 'id': 45, 'synset': 'banana.n.02', 'synonyms': ['banana'], 'def': 'elongated crescent-shaped yellow fruit with soft sweet flesh', 'name': 'banana'}, {'frequency': 'r', 'id': 46, 'synset': 'band_aid.n.01', 'synonyms': ['Band_Aid'], 'def': 'trade name for an adhesive bandage to cover small cuts or blisters', 'name': 'Band_Aid'}, {'frequency': 'c', 'id': 47, 'synset': 'bandage.n.01', 'synonyms': ['bandage'], 'def': 'a piece of soft material that covers and protects an injured part of the body', 'name': 'bandage'}, {'frequency': 'c', 'id': 48, 'synset': 'bandanna.n.01', 'synonyms': ['bandanna', 'bandana'], 'def': 'large and brightly colored handkerchief; often used as a neckerchief', 'name': 'bandanna'}, {'frequency': 'r', 'id': 49, 'synset': 'banjo.n.01', 'synonyms': ['banjo'], 'def': 'a stringed instrument of the guitar family with a long neck and circular body', 'name': 'banjo'}, {'frequency': 'f', 'id': 50, 'synset': 'banner.n.01', 'synonyms': ['banner', 'streamer'], 'def': 'long strip of cloth or paper used for decoration or advertising', 'name': 'banner'}, {'frequency': 'r', 'id': 51, 'synset': 'barbell.n.01', 'synonyms': ['barbell'], 'def': 'a bar to which heavy discs are attached at each end; used in weightlifting', 'name': 'barbell'}, {'frequency': 'r', 'id': 52, 'synset': 'barge.n.01', 'synonyms': ['barge'], 'def': 'a flatbottom boat for carrying heavy loads (especially on canals)', 'name': 'barge'}, {'frequency': 'f', 'id': 53, 'synset': 'barrel.n.02', 'synonyms': ['barrel', 'cask'], 'def': 'a cylindrical container that holds liquids', 'name': 'barrel'}, {'frequency': 'c', 'id': 54, 'synset': 'barrette.n.01', 'synonyms': ['barrette'], 'def': "a pin for holding women's hair in place", 'name': 'barrette'}, {'frequency': 'c', 'id': 55, 'synset': 'barrow.n.03', 'synonyms': ['barrow', 'garden_cart', 'lawn_cart', 'wheelbarrow'], 'def': 'a cart for carrying small loads; has handles and one or more wheels', 'name': 'barrow'}, {'frequency': 'f', 'id': 56, 'synset': 'base.n.03', 'synonyms': ['baseball_base'], 'def': 'a place that the runner must touch before scoring', 'name': 'baseball_base'}, {'frequency': 'f', 'id': 57, 'synset': 'baseball.n.02', 'synonyms': ['baseball'], 'def': 'a ball used in playing baseball', 'name': 'baseball'}, {'frequency': 'f', 'id': 58, 'synset': 'baseball_bat.n.01', 'synonyms': ['baseball_bat'], 'def': 'an implement used in baseball by the batter', 'name': 'baseball_bat'}, {'frequency': 'f', 'id': 59, 'synset': 'baseball_cap.n.01', 'synonyms': ['baseball_cap', 'jockey_cap', 'golf_cap'], 'def': 'a cap with a bill', 'name': 'baseball_cap'}, {'frequency': 'f', 'id': 60, 'synset': 'baseball_glove.n.01', 'synonyms': ['baseball_glove', 'baseball_mitt'], 'def': 'the handwear used by fielders in playing baseball', 'name': 'baseball_glove'}, {'frequency': 'f', 'id': 61, 'synset': 'basket.n.01', 'synonyms': ['basket', 'handbasket'], 'def': 'a container that is usually woven and has handles', 'name': 'basket'}, {'frequency': 'c', 'id': 62, 'synset': 'basket.n.03', 'synonyms': ['basketball_hoop'], 'def': 'metal hoop supporting a net through which players try to throw the basketball', 'name': 'basketball_hoop'}, {'frequency': 'c', 'id': 63, 'synset': 'basketball.n.02', 'synonyms': ['basketball'], 'def': 'an inflated ball used in playing basketball', 'name': 'basketball'}, {'frequency': 'r', 'id': 64, 'synset': 'bass_horn.n.01', 'synonyms': ['bass_horn', 'sousaphone', 'tuba'], 'def': 'the lowest brass wind instrument', 'name': 'bass_horn'}, {'frequency': 'r', 'id': 65, 'synset': 'bat.n.01', 'synonyms': ['bat_(animal)'], 'def': 'nocturnal mouselike mammal with forelimbs modified to form membranous wings', 'name': 'bat_(animal)'}, {'frequency': 'f', 'id': 66, 'synset': 'bath_mat.n.01', 'synonyms': ['bath_mat'], 'def': 'a heavy towel or mat to stand on while drying yourself after a bath', 'name': 'bath_mat'}, {'frequency': 'f', 'id': 67, 'synset': 'bath_towel.n.01', 'synonyms': ['bath_towel'], 'def': 'a large towel; to dry yourself after a bath', 'name': 'bath_towel'}, {'frequency': 'c', 'id': 68, 'synset': 'bathrobe.n.01', 'synonyms': ['bathrobe'], 'def': 'a loose-fitting robe of towelling; worn after a bath or swim', 'name': 'bathrobe'}, {'frequency': 'f', 'id': 69, 'synset': 'bathtub.n.01', 'synonyms': ['bathtub', 'bathing_tub'], 'def': 'a large open container that you fill with water and use to wash the body', 'name': 'bathtub'}, {'frequency': 'r', 'id': 70, 'synset': 'batter.n.02', 'synonyms': ['batter_(food)'], 'def': 'a liquid or semiliquid mixture, as of flour, eggs, and milk, used in cooking', 'name': 'batter_(food)'}, {'frequency': 'c', 'id': 71, 'synset': 'battery.n.02', 'synonyms': ['battery'], 'def': 'a portable device that produces electricity', 'name': 'battery'}, {'frequency': 'r', 'id': 72, 'synset': 'beach_ball.n.01', 'synonyms': ['beachball'], 'def': 'large and light ball; for play at the seaside', 'name': 'beachball'}, {'frequency': 'c', 'id': 73, 'synset': 'bead.n.01', 'synonyms': ['bead'], 'def': 'a small ball with a hole through the middle used for ornamentation, jewellery, etc.', 'name': 'bead'}, {'frequency': 'r', 'id': 74, 'synset': 'beaker.n.01', 'synonyms': ['beaker'], 'def': 'a flatbottomed jar made of glass or plastic; used for chemistry', 'name': 'beaker'}, {'frequency': 'c', 'id': 75, 'synset': 'bean_curd.n.01', 'synonyms': ['bean_curd', 'tofu'], 'def': 'cheeselike food made of curdled soybean milk', 'name': 'bean_curd'}, {'frequency': 'c', 'id': 76, 'synset': 'beanbag.n.01', 'synonyms': ['beanbag'], 'def': 'a bag filled with dried beans or similar items; used in games or to sit on', 'name': 'beanbag'}, {'frequency': 'f', 'id': 77, 'synset': 'beanie.n.01', 'synonyms': ['beanie', 'beany'], 'def': 'a small skullcap; formerly worn by schoolboys and college freshmen', 'name': 'beanie'}, {'frequency': 'f', 'id': 78, 'synset': 'bear.n.01', 'synonyms': ['bear'], 'def': 'large carnivorous or omnivorous mammals with shaggy coats and claws', 'name': 'bear'}, {'frequency': 'f', 'id': 79, 'synset': 'bed.n.01', 'synonyms': ['bed'], 'def': 'a piece of furniture that provides a place to sleep', 'name': 'bed'}, {'frequency': 'c', 'id': 80, 'synset': 'bedspread.n.01', 'synonyms': ['bedspread', 'bedcover', 'bed_covering', 'counterpane', 'spread'], 'def': 'decorative cover for a bed', 'name': 'bedspread'}, {'frequency': 'f', 'id': 81, 'synset': 'beef.n.01', 'synonyms': ['cow'], 'def': 'cattle that are reared for their meat', 'name': 'cow'}, {'frequency': 'c', 'id': 82, 'synset': 'beef.n.02', 'synonyms': ['beef_(food)', 'boeuf_(food)'], 'def': 'meat from an adult domestic bovine', 'name': 'beef_(food)'}, {'frequency': 'r', 'id': 83, 'synset': 'beeper.n.01', 'synonyms': ['beeper', 'pager'], 'def': 'an device that beeps when the person carrying it is being paged', 'name': 'beeper'}, {'frequency': 'f', 'id': 84, 'synset': 'beer_bottle.n.01', 'synonyms': ['beer_bottle'], 'def': 'a bottle that holds beer', 'name': 'beer_bottle'}, {'frequency': 'c', 'id': 85, 'synset': 'beer_can.n.01', 'synonyms': ['beer_can'], 'def': 'a can that holds beer', 'name': 'beer_can'}, {'frequency': 'r', 'id': 86, 'synset': 'beetle.n.01', 'synonyms': ['beetle'], 'def': 'insect with hard wing covers', 'name': 'beetle'}, {'frequency': 'f', 'id': 87, 'synset': 'bell.n.01', 'synonyms': ['bell'], 'def': 'a hollow device made of metal that makes a ringing sound when struck', 'name': 'bell'}, {'frequency': 'f', 'id': 88, 'synset': 'bell_pepper.n.02', 'synonyms': ['bell_pepper', 'capsicum'], 'def': 'large bell-shaped sweet pepper in green or red or yellow or orange or black varieties', 'name': 'bell_pepper'}, {'frequency': 'f', 'id': 89, 'synset': 'belt.n.02', 'synonyms': ['belt'], 'def': 'a band to tie or buckle around the body (usually at the waist)', 'name': 'belt'}, {'frequency': 'f', 'id': 90, 'synset': 'belt_buckle.n.01', 'synonyms': ['belt_buckle'], 'def': 'the buckle used to fasten a belt', 'name': 'belt_buckle'}, {'frequency': 'f', 'id': 91, 'synset': 'bench.n.01', 'synonyms': ['bench'], 'def': 'a long seat for more than one person', 'name': 'bench'}, {'frequency': 'c', 'id': 92, 'synset': 'beret.n.01', 'synonyms': ['beret'], 'def': 'a cap with no brim or bill; made of soft cloth', 'name': 'beret'}, {'frequency': 'c', 'id': 93, 'synset': 'bib.n.02', 'synonyms': ['bib'], 'def': 'a napkin tied under the chin of a child while eating', 'name': 'bib'}, {'frequency': 'r', 'id': 94, 'synset': 'bible.n.01', 'synonyms': ['Bible'], 'def': 'the sacred writings of the Christian religions', 'name': 'Bible'}, {'frequency': 'f', 'id': 95, 'synset': 'bicycle.n.01', 'synonyms': ['bicycle', 'bike_(bicycle)'], 'def': 'a wheeled vehicle that has two wheels and is moved by foot pedals', 'name': 'bicycle'}, {'frequency': 'f', 'id': 96, 'synset': 'bill.n.09', 'synonyms': ['visor', 'vizor'], 'def': 'a brim that projects to the front to shade the eyes', 'name': 'visor'}, {'frequency': 'c', 'id': 97, 'synset': 'binder.n.03', 'synonyms': ['binder', 'ring-binder'], 'def': 'holds loose papers or magazines', 'name': 'binder'}, {'frequency': 'c', 'id': 98, 'synset': 'binoculars.n.01', 'synonyms': ['binoculars', 'field_glasses', 'opera_glasses'], 'def': 'an optical instrument designed for simultaneous use by both eyes', 'name': 'binoculars'}, {'frequency': 'f', 'id': 99, 'synset': 'bird.n.01', 'synonyms': ['bird'], 'def': 'animal characterized by feathers and wings', 'name': 'bird'}, {'frequency': 'r', 'id': 100, 'synset': 'bird_feeder.n.01', 'synonyms': ['birdfeeder'], 'def': 'an outdoor device that supplies food for wild birds', 'name': 'birdfeeder'}, {'frequency': 'r', 'id': 101, 'synset': 'birdbath.n.01', 'synonyms': ['birdbath'], 'def': 'an ornamental basin (usually in a garden) for birds to bathe in', 'name': 'birdbath'}, {'frequency': 'c', 'id': 102, 'synset': 'birdcage.n.01', 'synonyms': ['birdcage'], 'def': 'a cage in which a bird can be kept', 'name': 'birdcage'}, {'frequency': 'c', 'id': 103, 'synset': 'birdhouse.n.01', 'synonyms': ['birdhouse'], 'def': 'a shelter for birds', 'name': 'birdhouse'}, {'frequency': 'f', 'id': 104, 'synset': 'birthday_cake.n.01', 'synonyms': ['birthday_cake'], 'def': 'decorated cake served at a birthday party', 'name': 'birthday_cake'}, {'frequency': 'r', 'id': 105, 'synset': 'birthday_card.n.01', 'synonyms': ['birthday_card'], 'def': 'a card expressing a birthday greeting', 'name': 'birthday_card'}, {'frequency': 'r', 'id': 106, 'synset': 'biscuit.n.01', 'synonyms': ['biscuit_(bread)'], 'def': 'small round bread leavened with baking-powder or soda', 'name': 'biscuit_(bread)'}, {'frequency': 'r', 'id': 107, 'synset': 'black_flag.n.01', 'synonyms': ['pirate_flag'], 'def': 'a flag usually bearing a white skull and crossbones on a black background', 'name': 'pirate_flag'}, {'frequency': 'c', 'id': 108, 'synset': 'black_sheep.n.02', 'synonyms': ['black_sheep'], 'def': 'sheep with a black coat', 'name': 'black_sheep'}, {'frequency': 'c', 'id': 109, 'synset': 'blackboard.n.01', 'synonyms': ['blackboard', 'chalkboard'], 'def': 'sheet of slate; for writing with chalk', 'name': 'blackboard'}, {'frequency': 'f', 'id': 110, 'synset': 'blanket.n.01', 'synonyms': ['blanket'], 'def': 'bedding that keeps a person warm in bed', 'name': 'blanket'}, {'frequency': 'c', 'id': 111, 'synset': 'blazer.n.01', 'synonyms': ['blazer', 'sport_jacket', 'sport_coat', 'sports_jacket', 'sports_coat'], 'def': 'lightweight jacket; often striped in the colors of a club or school', 'name': 'blazer'}, {'frequency': 'f', 'id': 112, 'synset': 'blender.n.01', 'synonyms': ['blender', 'liquidizer', 'liquidiser'], 'def': 'an electrically powered mixer that mix or chop or liquefy foods', 'name': 'blender'}, {'frequency': 'r', 'id': 113, 'synset': 'blimp.n.02', 'synonyms': ['blimp'], 'def': 'a small nonrigid airship used for observation or as a barrage balloon', 'name': 'blimp'}, {'frequency': 'c', 'id': 114, 'synset': 'blinker.n.01', 'synonyms': ['blinker', 'flasher'], 'def': 'a light that flashes on and off; used as a signal or to send messages', 'name': 'blinker'}, {'frequency': 'c', 'id': 115, 'synset': 'blueberry.n.02', 'synonyms': ['blueberry'], 'def': 'sweet edible dark-blue berries of blueberry plants', 'name': 'blueberry'}, {'frequency': 'r', 'id': 116, 'synset': 'boar.n.02', 'synonyms': ['boar'], 'def': 'an uncastrated male hog', 'name': 'boar'}, {'frequency': 'r', 'id': 117, 'synset': 'board.n.09', 'synonyms': ['gameboard'], 'def': 'a flat portable surface (usually rectangular) designed for board games', 'name': 'gameboard'}, {'frequency': 'f', 'id': 118, 'synset': 'boat.n.01', 'synonyms': ['boat', 'ship_(boat)'], 'def': 'a vessel for travel on water', 'name': 'boat'}, {'frequency': 'c', 'id': 119, 'synset': 'bobbin.n.01', 'synonyms': ['bobbin', 'spool', 'reel'], 'def': 'a thing around which thread/tape/film or other flexible materials can be wound', 'name': 'bobbin'}, {'frequency': 'r', 'id': 120, 'synset': 'bobby_pin.n.01', 'synonyms': ['bobby_pin', 'hairgrip'], 'def': 'a flat wire hairpin used to hold bobbed hair in place', 'name': 'bobby_pin'}, {'frequency': 'c', 'id': 121, 'synset': 'boiled_egg.n.01', 'synonyms': ['boiled_egg', 'coddled_egg'], 'def': 'egg cooked briefly in the shell in gently boiling water', 'name': 'boiled_egg'}, {'frequency': 'r', 'id': 122, 'synset': 'bolo_tie.n.01', 'synonyms': ['bolo_tie', 'bolo', 'bola_tie', 'bola'], 'def': 'a cord fastened around the neck with an ornamental clasp and worn as a necktie', 'name': 'bolo_tie'}, {'frequency': 'c', 'id': 123, 'synset': 'bolt.n.03', 'synonyms': ['deadbolt'], 'def': 'the part of a lock that is engaged or withdrawn with a key', 'name': 'deadbolt'}, {'frequency': 'f', 'id': 124, 'synset': 'bolt.n.06', 'synonyms': ['bolt'], 'def': 'a screw that screws into a nut to form a fastener', 'name': 'bolt'}, {'frequency': 'r', 'id': 125, 'synset': 'bonnet.n.01', 'synonyms': ['bonnet'], 'def': 'a hat tied under the chin', 'name': 'bonnet'}, {'frequency': 'f', 'id': 126, 'synset': 'book.n.01', 'synonyms': ['book'], 'def': 'a written work or composition that has been published', 'name': 'book'}, {'frequency': 'r', 'id': 127, 'synset': 'book_bag.n.01', 'synonyms': ['book_bag'], 'def': 'a bag in which students carry their books', 'name': 'book_bag'}, {'frequency': 'c', 'id': 128, 'synset': 'bookcase.n.01', 'synonyms': ['bookcase'], 'def': 'a piece of furniture with shelves for storing books', 'name': 'bookcase'}, {'frequency': 'c', 'id': 129, 'synset': 'booklet.n.01', 'synonyms': ['booklet', 'brochure', 'leaflet', 'pamphlet'], 'def': 'a small book usually having a paper cover', 'name': 'booklet'}, {'frequency': 'r', 'id': 130, 'synset': 'bookmark.n.01', 'synonyms': ['bookmark', 'bookmarker'], 'def': 'a marker (a piece of paper or ribbon) placed between the pages of a book', 'name': 'bookmark'}, {'frequency': 'r', 'id': 131, 'synset': 'boom.n.04', 'synonyms': ['boom_microphone', 'microphone_boom'], 'def': 'a pole carrying an overhead microphone projected over a film or tv set', 'name': 'boom_microphone'}, {'frequency': 'f', 'id': 132, 'synset': 'boot.n.01', 'synonyms': ['boot'], 'def': 'footwear that covers the whole foot and lower leg', 'name': 'boot'}, {'frequency': 'f', 'id': 133, 'synset': 'bottle.n.01', 'synonyms': ['bottle'], 'def': 'a glass or plastic vessel used for storing drinks or other liquids', 'name': 'bottle'}, {'frequency': 'c', 'id': 134, 'synset': 'bottle_opener.n.01', 'synonyms': ['bottle_opener'], 'def': 'an opener for removing caps or corks from bottles', 'name': 'bottle_opener'}, {'frequency': 'c', 'id': 135, 'synset': 'bouquet.n.01', 'synonyms': ['bouquet'], 'def': 'an arrangement of flowers that is usually given as a present', 'name': 'bouquet'}, {'frequency': 'r', 'id': 136, 'synset': 'bow.n.04', 'synonyms': ['bow_(weapon)'], 'def': 'a weapon for shooting arrows', 'name': 'bow_(weapon)'}, {'frequency': 'f', 'id': 137, 'synset': 'bow.n.08', 'synonyms': ['bow_(decorative_ribbons)'], 'def': 'a decorative interlacing of ribbons', 'name': 'bow_(decorative_ribbons)'}, {'frequency': 'f', 'id': 138, 'synset': 'bow_tie.n.01', 'synonyms': ['bow-tie', 'bowtie'], 'def': "a man's tie that ties in a bow", 'name': 'bow-tie'}, {'frequency': 'f', 'id': 139, 'synset': 'bowl.n.03', 'synonyms': ['bowl'], 'def': 'a dish that is round and open at the top for serving foods', 'name': 'bowl'}, {'frequency': 'r', 'id': 140, 'synset': 'bowl.n.08', 'synonyms': ['pipe_bowl'], 'def': 'a small round container that is open at the top for holding tobacco', 'name': 'pipe_bowl'}, {'frequency': 'c', 'id': 141, 'synset': 'bowler_hat.n.01', 'synonyms': ['bowler_hat', 'bowler', 'derby_hat', 'derby', 'plug_hat'], 'def': 'a felt hat that is round and hard with a narrow brim', 'name': 'bowler_hat'}, {'frequency': 'r', 'id': 142, 'synset': 'bowling_ball.n.01', 'synonyms': ['bowling_ball'], 'def': 'a large ball with finger holes used in the sport of bowling', 'name': 'bowling_ball'}, {'frequency': 'r', 'id': 143, 'synset': 'bowling_pin.n.01', 'synonyms': ['bowling_pin'], 'def': 'a club-shaped wooden object used in bowling', 'name': 'bowling_pin'}, {'frequency': 'r', 'id': 144, 'synset': 'boxing_glove.n.01', 'synonyms': ['boxing_glove'], 'def': 'large glove coverings the fists of a fighter worn for the sport of boxing', 'name': 'boxing_glove'}, {'frequency': 'c', 'id': 145, 'synset': 'brace.n.06', 'synonyms': ['suspenders'], 'def': 'elastic straps that hold trousers up (usually used in the plural)', 'name': 'suspenders'}, {'frequency': 'f', 'id': 146, 'synset': 'bracelet.n.02', 'synonyms': ['bracelet', 'bangle'], 'def': 'jewelry worn around the wrist for decoration', 'name': 'bracelet'}, {'frequency': 'r', 'id': 147, 'synset': 'brass.n.07', 'synonyms': ['brass_plaque'], 'def': 'a memorial made of brass', 'name': 'brass_plaque'}, {'frequency': 'c', 'id': 148, 'synset': 'brassiere.n.01', 'synonyms': ['brassiere', 'bra', 'bandeau'], 'def': 'an undergarment worn by women to support their breasts', 'name': 'brassiere'}, {'frequency': 'c', 'id': 149, 'synset': 'bread-bin.n.01', 'synonyms': ['bread-bin', 'breadbox'], 'def': 'a container used to keep bread or cake in', 'name': 'bread-bin'}, {'frequency': 'r', 'id': 150, 'synset': 'breechcloth.n.01', 'synonyms': ['breechcloth', 'breechclout', 'loincloth'], 'def': 'a garment that provides covering for the loins', 'name': 'breechcloth'}, {'frequency': 'c', 'id': 151, 'synset': 'bridal_gown.n.01', 'synonyms': ['bridal_gown', 'wedding_gown', 'wedding_dress'], 'def': 'a gown worn by the bride at a wedding', 'name': 'bridal_gown'}, {'frequency': 'c', 'id': 152, 'synset': 'briefcase.n.01', 'synonyms': ['briefcase'], 'def': 'a case with a handle; for carrying papers or files or books', 'name': 'briefcase'}, {'frequency': 'c', 'id': 153, 'synset': 'bristle_brush.n.01', 'synonyms': ['bristle_brush'], 'def': 'a brush that is made with the short stiff hairs of an animal or plant', 'name': 'bristle_brush'}, {'frequency': 'f', 'id': 154, 'synset': 'broccoli.n.01', 'synonyms': ['broccoli'], 'def': 'plant with dense clusters of tight green flower buds', 'name': 'broccoli'}, {'frequency': 'r', 'id': 155, 'synset': 'brooch.n.01', 'synonyms': ['broach'], 'def': 'a decorative pin worn by women', 'name': 'broach'}, {'frequency': 'c', 'id': 156, 'synset': 'broom.n.01', 'synonyms': ['broom'], 'def': 'bundle of straws or twigs attached to a long handle; used for cleaning', 'name': 'broom'}, {'frequency': 'c', 'id': 157, 'synset': 'brownie.n.03', 'synonyms': ['brownie'], 'def': 'square or bar of very rich chocolate cake usually with nuts', 'name': 'brownie'}, {'frequency': 'c', 'id': 158, 'synset': 'brussels_sprouts.n.01', 'synonyms': ['brussels_sprouts'], 'def': 'the small edible cabbage-like buds growing along a stalk', 'name': 'brussels_sprouts'}, {'frequency': 'r', 'id': 159, 'synset': 'bubble_gum.n.01', 'synonyms': ['bubble_gum'], 'def': 'a kind of chewing gum that can be blown into bubbles', 'name': 'bubble_gum'}, {'frequency': 'f', 'id': 160, 'synset': 'bucket.n.01', 'synonyms': ['bucket', 'pail'], 'def': 'a roughly cylindrical vessel that is open at the top', 'name': 'bucket'}, {'frequency': 'r', 'id': 161, 'synset': 'buggy.n.01', 'synonyms': ['horse_buggy'], 'def': 'a small lightweight carriage; drawn by a single horse', 'name': 'horse_buggy'}, {'frequency': 'c', 'id': 162, 'synset': 'bull.n.11', 'synonyms': ['bull'], 'def': 'mature male cow', 'name': 'bull'}, {'frequency': 'r', 'id': 163, 'synset': 'bulldog.n.01', 'synonyms': ['bulldog'], 'def': 'a thickset short-haired dog with a large head and strong undershot lower jaw', 'name': 'bulldog'}, {'frequency': 'r', 'id': 164, 'synset': 'bulldozer.n.01', 'synonyms': ['bulldozer', 'dozer'], 'def': 'large powerful tractor; a large blade in front flattens areas of ground', 'name': 'bulldozer'}, {'frequency': 'c', 'id': 165, 'synset': 'bullet_train.n.01', 'synonyms': ['bullet_train'], 'def': 'a high-speed passenger train', 'name': 'bullet_train'}, {'frequency': 'c', 'id': 166, 'synset': 'bulletin_board.n.02', 'synonyms': ['bulletin_board', 'notice_board'], 'def': 'a board that hangs on a wall; displays announcements', 'name': 'bulletin_board'}, {'frequency': 'r', 'id': 167, 'synset': 'bulletproof_vest.n.01', 'synonyms': ['bulletproof_vest'], 'def': 'a vest capable of resisting the impact of a bullet', 'name': 'bulletproof_vest'}, {'frequency': 'c', 'id': 168, 'synset': 'bullhorn.n.01', 'synonyms': ['bullhorn', 'megaphone'], 'def': 'a portable loudspeaker with built-in microphone and amplifier', 'name': 'bullhorn'}, {'frequency': 'r', 'id': 169, 'synset': 'bully_beef.n.01', 'synonyms': ['corned_beef', 'corn_beef'], 'def': 'beef cured or pickled in brine', 'name': 'corned_beef'}, {'frequency': 'f', 'id': 170, 'synset': 'bun.n.01', 'synonyms': ['bun', 'roll'], 'def': 'small rounded bread either plain or sweet', 'name': 'bun'}, {'frequency': 'c', 'id': 171, 'synset': 'bunk_bed.n.01', 'synonyms': ['bunk_bed'], 'def': 'beds built one above the other', 'name': 'bunk_bed'}, {'frequency': 'f', 'id': 172, 'synset': 'buoy.n.01', 'synonyms': ['buoy'], 'def': 'a float attached by rope to the seabed to mark channels in a harbor or underwater hazards', 'name': 'buoy'}, {'frequency': 'r', 'id': 173, 'synset': 'burrito.n.01', 'synonyms': ['burrito'], 'def': 'a flour tortilla folded around a filling', 'name': 'burrito'}, {'frequency': 'f', 'id': 174, 'synset': 'bus.n.01', 'synonyms': ['bus_(vehicle)', 'autobus', 'charabanc', 'double-decker', 'motorbus', 'motorcoach'], 'def': 'a vehicle carrying many passengers; used for public transport', 'name': 'bus_(vehicle)'}, {'frequency': 'c', 'id': 175, 'synset': 'business_card.n.01', 'synonyms': ['business_card'], 'def': "a card on which are printed the person's name and business affiliation", 'name': 'business_card'}, {'frequency': 'c', 'id': 176, 'synset': 'butcher_knife.n.01', 'synonyms': ['butcher_knife'], 'def': 'a large sharp knife for cutting or trimming meat', 'name': 'butcher_knife'}, {'frequency': 'c', 'id': 177, 'synset': 'butter.n.01', 'synonyms': ['butter'], 'def': 'an edible emulsion of fat globules made by churning milk or cream; for cooking and table use', 'name': 'butter'}, {'frequency': 'c', 'id': 178, 'synset': 'butterfly.n.01', 'synonyms': ['butterfly'], 'def': 'insect typically having a slender body with knobbed antennae and broad colorful wings', 'name': 'butterfly'}, {'frequency': 'f', 'id': 179, 'synset': 'button.n.01', 'synonyms': ['button'], 'def': 'a round fastener sewn to shirts and coats etc to fit through buttonholes', 'name': 'button'}, {'frequency': 'f', 'id': 180, 'synset': 'cab.n.03', 'synonyms': ['cab_(taxi)', 'taxi', 'taxicab'], 'def': 'a car that takes passengers where they want to go in exchange for money', 'name': 'cab_(taxi)'}, {'frequency': 'r', 'id': 181, 'synset': 'cabana.n.01', 'synonyms': ['cabana'], 'def': 'a small tent used as a dressing room beside the sea or a swimming pool', 'name': 'cabana'}, {'frequency': 'r', 'id': 182, 'synset': 'cabin_car.n.01', 'synonyms': ['cabin_car', 'caboose'], 'def': 'a car on a freight train for use of the train crew; usually the last car on the train', 'name': 'cabin_car'}, {'frequency': 'f', 'id': 183, 'synset': 'cabinet.n.01', 'synonyms': ['cabinet'], 'def': 'a piece of furniture resembling a cupboard with doors and shelves and drawers', 'name': 'cabinet'}, {'frequency': 'r', 'id': 184, 'synset': 'cabinet.n.03', 'synonyms': ['locker', 'storage_locker'], 'def': 'a storage compartment for clothes and valuables; usually it has a lock', 'name': 'locker'}, {'frequency': 'f', 'id': 185, 'synset': 'cake.n.03', 'synonyms': ['cake'], 'def': 'baked goods made from or based on a mixture of flour, sugar, eggs, and fat', 'name': 'cake'}, {'frequency': 'c', 'id': 186, 'synset': 'calculator.n.02', 'synonyms': ['calculator'], 'def': 'a small machine that is used for mathematical calculations', 'name': 'calculator'}, {'frequency': 'f', 'id': 187, 'synset': 'calendar.n.02', 'synonyms': ['calendar'], 'def': 'a list or register of events (appointments/social events/court cases, etc)', 'name': 'calendar'}, {'frequency': 'c', 'id': 188, 'synset': 'calf.n.01', 'synonyms': ['calf'], 'def': 'young of domestic cattle', 'name': 'calf'}, {'frequency': 'c', 'id': 189, 'synset': 'camcorder.n.01', 'synonyms': ['camcorder'], 'def': 'a portable television camera and videocassette recorder', 'name': 'camcorder'}, {'frequency': 'c', 'id': 190, 'synset': 'camel.n.01', 'synonyms': ['camel'], 'def': 'cud-chewing mammal used as a draft or saddle animal in desert regions', 'name': 'camel'}, {'frequency': 'f', 'id': 191, 'synset': 'camera.n.01', 'synonyms': ['camera'], 'def': 'equipment for taking photographs', 'name': 'camera'}, {'frequency': 'c', 'id': 192, 'synset': 'camera_lens.n.01', 'synonyms': ['camera_lens'], 'def': 'a lens that focuses the image in a camera', 'name': 'camera_lens'}, {'frequency': 'c', 'id': 193, 'synset': 'camper.n.02', 'synonyms': ['camper_(vehicle)', 'camping_bus', 'motor_home'], 'def': 'a recreational vehicle equipped for camping out while traveling', 'name': 'camper_(vehicle)'}, {'frequency': 'f', 'id': 194, 'synset': 'can.n.01', 'synonyms': ['can', 'tin_can'], 'def': 'airtight sealed metal container for food or drink or paint etc.', 'name': 'can'}, {'frequency': 'c', 'id': 195, 'synset': 'can_opener.n.01', 'synonyms': ['can_opener', 'tin_opener'], 'def': 'a device for cutting cans open', 'name': 'can_opener'}, {'frequency': 'r', 'id': 196, 'synset': 'candelabrum.n.01', 'synonyms': ['candelabrum', 'candelabra'], 'def': 'branched candlestick; ornamental; has several lights', 'name': 'candelabrum'}, {'frequency': 'f', 'id': 197, 'synset': 'candle.n.01', 'synonyms': ['candle', 'candlestick'], 'def': 'stick of wax with a wick in the middle', 'name': 'candle'}, {'frequency': 'f', 'id': 198, 'synset': 'candlestick.n.01', 'synonyms': ['candle_holder'], 'def': 'a holder with sockets for candles', 'name': 'candle_holder'}, {'frequency': 'r', 'id': 199, 'synset': 'candy_bar.n.01', 'synonyms': ['candy_bar'], 'def': 'a candy shaped as a bar', 'name': 'candy_bar'}, {'frequency': 'c', 'id': 200, 'synset': 'candy_cane.n.01', 'synonyms': ['candy_cane'], 'def': 'a hard candy in the shape of a rod (usually with stripes)', 'name': 'candy_cane'}, {'frequency': 'c', 'id': 201, 'synset': 'cane.n.01', 'synonyms': ['walking_cane'], 'def': 'a stick that people can lean on to help them walk', 'name': 'walking_cane'}, {'frequency': 'c', 'id': 202, 'synset': 'canister.n.02', 'synonyms': ['canister', 'cannister'], 'def': 'metal container for storing dry foods such as tea or flour', 'name': 'canister'}, {'frequency': 'r', 'id': 203, 'synset': 'cannon.n.02', 'synonyms': ['cannon'], 'def': 'heavy gun fired from a tank', 'name': 'cannon'}, {'frequency': 'c', 'id': 204, 'synset': 'canoe.n.01', 'synonyms': ['canoe'], 'def': 'small and light boat; pointed at both ends; propelled with a paddle', 'name': 'canoe'}, {'frequency': 'r', 'id': 205, 'synset': 'cantaloup.n.02', 'synonyms': ['cantaloup', 'cantaloupe'], 'def': 'the fruit of a cantaloup vine; small to medium-sized melon with yellowish flesh', 'name': 'cantaloup'}, {'frequency': 'r', 'id': 206, 'synset': 'canteen.n.01', 'synonyms': ['canteen'], 'def': 'a flask for carrying water; used by soldiers or travelers', 'name': 'canteen'}, {'frequency': 'c', 'id': 207, 'synset': 'cap.n.01', 'synonyms': ['cap_(headwear)'], 'def': 'a tight-fitting headwear', 'name': 'cap_(headwear)'}, {'frequency': 'f', 'id': 208, 'synset': 'cap.n.02', 'synonyms': ['bottle_cap', 'cap_(container_lid)'], 'def': 'a top (as for a bottle)', 'name': 'bottle_cap'}, {'frequency': 'r', 'id': 209, 'synset': 'cape.n.02', 'synonyms': ['cape'], 'def': 'a sleeveless garment like a cloak but shorter', 'name': 'cape'}, {'frequency': 'c', 'id': 210, 'synset': 'cappuccino.n.01', 'synonyms': ['cappuccino', 'coffee_cappuccino'], 'def': 'equal parts of espresso and steamed milk', 'name': 'cappuccino'}, {'frequency': 'f', 'id': 211, 'synset': 'car.n.01', 'synonyms': ['car_(automobile)', 'auto_(automobile)', 'automobile'], 'def': 'a motor vehicle with four wheels', 'name': 'car_(automobile)'}, {'frequency': 'f', 'id': 212, 'synset': 'car.n.02', 'synonyms': ['railcar_(part_of_a_train)', 'railway_car_(part_of_a_train)', 'railroad_car_(part_of_a_train)'], 'def': 'a wheeled vehicle adapted to the rails of railroad', 'name': 'railcar_(part_of_a_train)'}, {'frequency': 'r', 'id': 213, 'synset': 'car.n.04', 'synonyms': ['elevator_car'], 'def': 'where passengers ride up and down', 'name': 'elevator_car'}, {'frequency': 'r', 'id': 214, 'synset': 'car_battery.n.01', 'synonyms': ['car_battery', 'automobile_battery'], 'def': 'a battery in a motor vehicle', 'name': 'car_battery'}, {'frequency': 'c', 'id': 215, 'synset': 'card.n.02', 'synonyms': ['identity_card'], 'def': 'a card certifying the identity of the bearer', 'name': 'identity_card'}, {'frequency': 'c', 'id': 216, 'synset': 'card.n.03', 'synonyms': ['card'], 'def': 'a rectangular piece of paper used to send messages (e.g. greetings or pictures)', 'name': 'card'}, {'frequency': 'r', 'id': 217, 'synset': 'cardigan.n.01', 'synonyms': ['cardigan'], 'def': 'knitted jacket that is fastened up the front with buttons or a zipper', 'name': 'cardigan'}, {'frequency': 'r', 'id': 218, 'synset': 'cargo_ship.n.01', 'synonyms': ['cargo_ship', 'cargo_vessel'], 'def': 'a ship designed to carry cargo', 'name': 'cargo_ship'}, {'frequency': 'r', 'id': 219, 'synset': 'carnation.n.01', 'synonyms': ['carnation'], 'def': 'plant with pink to purple-red spice-scented usually double flowers', 'name': 'carnation'}, {'frequency': 'c', 'id': 220, 'synset': 'carriage.n.02', 'synonyms': ['horse_carriage'], 'def': 'a vehicle with wheels drawn by one or more horses', 'name': 'horse_carriage'}, {'frequency': 'f', 'id': 221, 'synset': 'carrot.n.01', 'synonyms': ['carrot'], 'def': 'deep orange edible root of the cultivated carrot plant', 'name': 'carrot'}, {'frequency': 'c', 'id': 222, 'synset': 'carryall.n.01', 'synonyms': ['tote_bag'], 'def': 'a capacious bag or basket', 'name': 'tote_bag'}, {'frequency': 'c', 'id': 223, 'synset': 'cart.n.01', 'synonyms': ['cart'], 'def': 'a heavy open wagon usually having two wheels and drawn by an animal', 'name': 'cart'}, {'frequency': 'c', 'id': 224, 'synset': 'carton.n.02', 'synonyms': ['carton'], 'def': 'a box made of cardboard; opens by flaps on top', 'name': 'carton'}, {'frequency': 'c', 'id': 225, 'synset': 'cash_register.n.01', 'synonyms': ['cash_register', 'register_(for_cash_transactions)'], 'def': 'a cashbox with an adding machine to register transactions', 'name': 'cash_register'}, {'frequency': 'r', 'id': 226, 'synset': 'casserole.n.01', 'synonyms': ['casserole'], 'def': 'food cooked and served in a casserole', 'name': 'casserole'}, {'frequency': 'r', 'id': 227, 'synset': 'cassette.n.01', 'synonyms': ['cassette'], 'def': 'a container that holds a magnetic tape used for recording or playing sound or video', 'name': 'cassette'}, {'frequency': 'c', 'id': 228, 'synset': 'cast.n.05', 'synonyms': ['cast', 'plaster_cast', 'plaster_bandage'], 'def': 'bandage consisting of a firm covering that immobilizes broken bones while they heal', 'name': 'cast'}, {'frequency': 'f', 'id': 229, 'synset': 'cat.n.01', 'synonyms': ['cat'], 'def': 'a domestic house cat', 'name': 'cat'}, {'frequency': 'c', 'id': 230, 'synset': 'cauliflower.n.02', 'synonyms': ['cauliflower'], 'def': 'edible compact head of white undeveloped flowers', 'name': 'cauliflower'}, {'frequency': 'r', 'id': 231, 'synset': 'caviar.n.01', 'synonyms': ['caviar', 'caviare'], 'def': "salted roe of sturgeon or other large fish; usually served as an hors d'oeuvre", 'name': 'caviar'}, {'frequency': 'c', 'id': 232, 'synset': 'cayenne.n.02', 'synonyms': ['cayenne_(spice)', 'cayenne_pepper_(spice)', 'red_pepper_(spice)'], 'def': 'ground pods and seeds of pungent red peppers of the genus Capsicum', 'name': 'cayenne_(spice)'}, {'frequency': 'c', 'id': 233, 'synset': 'cd_player.n.01', 'synonyms': ['CD_player'], 'def': 'electronic equipment for playing compact discs (CDs)', 'name': 'CD_player'}, {'frequency': 'c', 'id': 234, 'synset': 'celery.n.01', 'synonyms': ['celery'], 'def': 'widely cultivated herb with aromatic leaf stalks that are eaten raw or cooked', 'name': 'celery'}, {'frequency': 'f', 'id': 235, 'synset': 'cellular_telephone.n.01', 'synonyms': ['cellular_telephone', 'cellular_phone', 'cellphone', 'mobile_phone', 'smart_phone'], 'def': 'a hand-held mobile telephone', 'name': 'cellular_telephone'}, {'frequency': 'r', 'id': 236, 'synset': 'chain_mail.n.01', 'synonyms': ['chain_mail', 'ring_mail', 'chain_armor', 'chain_armour', 'ring_armor', 'ring_armour'], 'def': '(Middle Ages) flexible armor made of interlinked metal rings', 'name': 'chain_mail'}, {'frequency': 'f', 'id': 237, 'synset': 'chair.n.01', 'synonyms': ['chair'], 'def': 'a seat for one person, with a support for the back', 'name': 'chair'}, {'frequency': 'r', 'id': 238, 'synset': 'chaise_longue.n.01', 'synonyms': ['chaise_longue', 'chaise', 'daybed'], 'def': 'a long chair; for reclining', 'name': 'chaise_longue'}, {'frequency': 'r', 'id': 239, 'synset': 'champagne.n.01', 'synonyms': ['champagne'], 'def': 'a white sparkling wine produced in Champagne or resembling that produced there', 'name': 'champagne'}, {'frequency': 'f', 'id': 240, 'synset': 'chandelier.n.01', 'synonyms': ['chandelier'], 'def': 'branched lighting fixture; often ornate; hangs from the ceiling', 'name': 'chandelier'}, {'frequency': 'r', 'id': 241, 'synset': 'chap.n.04', 'synonyms': ['chap'], 'def': 'leather leggings without a seat; worn over trousers by cowboys to protect their legs', 'name': 'chap'}, {'frequency': 'r', 'id': 242, 'synset': 'checkbook.n.01', 'synonyms': ['checkbook', 'chequebook'], 'def': 'a book issued to holders of checking accounts', 'name': 'checkbook'}, {'frequency': 'r', 'id': 243, 'synset': 'checkerboard.n.01', 'synonyms': ['checkerboard'], 'def': 'a board having 64 squares of two alternating colors', 'name': 'checkerboard'}, {'frequency': 'c', 'id': 244, 'synset': 'cherry.n.03', 'synonyms': ['cherry'], 'def': 'a red fruit with a single hard stone', 'name': 'cherry'}, {'frequency': 'r', 'id': 245, 'synset': 'chessboard.n.01', 'synonyms': ['chessboard'], 'def': 'a checkerboard used to play chess', 'name': 'chessboard'}, {'frequency': 'r', 'id': 246, 'synset': 'chest_of_drawers.n.01', 'synonyms': ['chest_of_drawers_(furniture)', 'bureau_(furniture)', 'chest_(furniture)'], 'def': 'furniture with drawers for keeping clothes', 'name': 'chest_of_drawers_(furniture)'}, {'frequency': 'c', 'id': 247, 'synset': 'chicken.n.02', 'synonyms': ['chicken_(animal)'], 'def': 'a domestic fowl bred for flesh or eggs', 'name': 'chicken_(animal)'}, {'frequency': 'c', 'id': 248, 'synset': 'chicken_wire.n.01', 'synonyms': ['chicken_wire'], 'def': 'a galvanized wire network with a hexagonal mesh; used to build fences', 'name': 'chicken_wire'}, {'frequency': 'r', 'id': 249, 'synset': 'chickpea.n.01', 'synonyms': ['chickpea', 'garbanzo'], 'def': 'the seed of the chickpea plant; usually dried', 'name': 'chickpea'}, {'frequency': 'r', 'id': 250, 'synset': 'chihuahua.n.03', 'synonyms': ['Chihuahua'], 'def': 'an old breed of tiny short-haired dog with protruding eyes from Mexico', 'name': 'Chihuahua'}, {'frequency': 'r', 'id': 251, 'synset': 'chili.n.02', 'synonyms': ['chili_(vegetable)', 'chili_pepper_(vegetable)', 'chilli_(vegetable)', 'chilly_(vegetable)', 'chile_(vegetable)'], 'def': 'very hot and finely tapering pepper of special pungency', 'name': 'chili_(vegetable)'}, {'frequency': 'r', 'id': 252, 'synset': 'chime.n.01', 'synonyms': ['chime', 'gong'], 'def': 'an instrument consisting of a set of bells that are struck with a hammer', 'name': 'chime'}, {'frequency': 'r', 'id': 253, 'synset': 'chinaware.n.01', 'synonyms': ['chinaware'], 'def': 'dishware made of high quality porcelain', 'name': 'chinaware'}, {'frequency': 'c', 'id': 254, 'synset': 'chip.n.04', 'synonyms': ['crisp_(potato_chip)', 'potato_chip'], 'def': 'a thin crisp slice of potato fried in deep fat', 'name': 'crisp_(potato_chip)'}, {'frequency': 'r', 'id': 255, 'synset': 'chip.n.06', 'synonyms': ['poker_chip'], 'def': 'a small disk-shaped counter used to represent money when gambling', 'name': 'poker_chip'}, {'frequency': 'c', 'id': 256, 'synset': 'chocolate_bar.n.01', 'synonyms': ['chocolate_bar'], 'def': 'a bar of chocolate candy', 'name': 'chocolate_bar'}, {'frequency': 'c', 'id': 257, 'synset': 'chocolate_cake.n.01', 'synonyms': ['chocolate_cake'], 'def': 'cake containing chocolate', 'name': 'chocolate_cake'}, {'frequency': 'r', 'id': 258, 'synset': 'chocolate_milk.n.01', 'synonyms': ['chocolate_milk'], 'def': 'milk flavored with chocolate syrup', 'name': 'chocolate_milk'}, {'frequency': 'r', 'id': 259, 'synset': 'chocolate_mousse.n.01', 'synonyms': ['chocolate_mousse'], 'def': 'dessert mousse made with chocolate', 'name': 'chocolate_mousse'}, {'frequency': 'f', 'id': 260, 'synset': 'choker.n.03', 'synonyms': ['choker', 'collar', 'neckband'], 'def': 'necklace that fits tightly around the neck', 'name': 'choker'}, {'frequency': 'f', 'id': 261, 'synset': 'chopping_board.n.01', 'synonyms': ['chopping_board', 'cutting_board', 'chopping_block'], 'def': 'a wooden board where meats or vegetables can be cut', 'name': 'chopping_board'}, {'frequency': 'c', 'id': 262, 'synset': 'chopstick.n.01', 'synonyms': ['chopstick'], 'def': 'one of a pair of slender sticks used as oriental tableware to eat food with', 'name': 'chopstick'}, {'frequency': 'f', 'id': 263, 'synset': 'christmas_tree.n.05', 'synonyms': ['Christmas_tree'], 'def': 'an ornamented evergreen used as a Christmas decoration', 'name': 'Christmas_tree'}, {'frequency': 'c', 'id': 264, 'synset': 'chute.n.02', 'synonyms': ['slide'], 'def': 'sloping channel through which things can descend', 'name': 'slide'}, {'frequency': 'r', 'id': 265, 'synset': 'cider.n.01', 'synonyms': ['cider', 'cyder'], 'def': 'a beverage made from juice pressed from apples', 'name': 'cider'}, {'frequency': 'r', 'id': 266, 'synset': 'cigar_box.n.01', 'synonyms': ['cigar_box'], 'def': 'a box for holding cigars', 'name': 'cigar_box'}, {'frequency': 'c', 'id': 267, 'synset': 'cigarette.n.01', 'synonyms': ['cigarette'], 'def': 'finely ground tobacco wrapped in paper; for smoking', 'name': 'cigarette'}, {'frequency': 'c', 'id': 268, 'synset': 'cigarette_case.n.01', 'synonyms': ['cigarette_case', 'cigarette_pack'], 'def': 'a small flat case for holding cigarettes', 'name': 'cigarette_case'}, {'frequency': 'f', 'id': 269, 'synset': 'cistern.n.02', 'synonyms': ['cistern', 'water_tank'], 'def': 'a tank that holds the water used to flush a toilet', 'name': 'cistern'}, {'frequency': 'r', 'id': 270, 'synset': 'clarinet.n.01', 'synonyms': ['clarinet'], 'def': 'a single-reed instrument with a straight tube', 'name': 'clarinet'}, {'frequency': 'r', 'id': 271, 'synset': 'clasp.n.01', 'synonyms': ['clasp'], 'def': 'a fastener (as a buckle or hook) that is used to hold two things together', 'name': 'clasp'}, {'frequency': 'c', 'id': 272, 'synset': 'cleansing_agent.n.01', 'synonyms': ['cleansing_agent', 'cleanser', 'cleaner'], 'def': 'a preparation used in cleaning something', 'name': 'cleansing_agent'}, {'frequency': 'r', 'id': 273, 'synset': 'clementine.n.01', 'synonyms': ['clementine'], 'def': 'a variety of mandarin orange', 'name': 'clementine'}, {'frequency': 'c', 'id': 274, 'synset': 'clip.n.03', 'synonyms': ['clip'], 'def': 'any of various small fasteners used to hold loose articles together', 'name': 'clip'}, {'frequency': 'c', 'id': 275, 'synset': 'clipboard.n.01', 'synonyms': ['clipboard'], 'def': 'a small writing board with a clip at the top for holding papers', 'name': 'clipboard'}, {'frequency': 'f', 'id': 276, 'synset': 'clock.n.01', 'synonyms': ['clock', 'timepiece', 'timekeeper'], 'def': 'a timepiece that shows the time of day', 'name': 'clock'}, {'frequency': 'f', 'id': 277, 'synset': 'clock_tower.n.01', 'synonyms': ['clock_tower'], 'def': 'a tower with a large clock visible high up on an outside face', 'name': 'clock_tower'}, {'frequency': 'c', 'id': 278, 'synset': 'clothes_hamper.n.01', 'synonyms': ['clothes_hamper', 'laundry_basket', 'clothes_basket'], 'def': 'a hamper that holds dirty clothes to be washed or wet clothes to be dried', 'name': 'clothes_hamper'}, {'frequency': 'c', 'id': 279, 'synset': 'clothespin.n.01', 'synonyms': ['clothespin', 'clothes_peg'], 'def': 'wood or plastic fastener; for holding clothes on a clothesline', 'name': 'clothespin'}, {'frequency': 'r', 'id': 280, 'synset': 'clutch_bag.n.01', 'synonyms': ['clutch_bag'], 'def': "a woman's strapless purse that is carried in the hand", 'name': 'clutch_bag'}, {'frequency': 'f', 'id': 281, 'synset': 'coaster.n.03', 'synonyms': ['coaster'], 'def': 'a covering (plate or mat) that protects the surface of a table', 'name': 'coaster'}, {'frequency': 'f', 'id': 282, 'synset': 'coat.n.01', 'synonyms': ['coat'], 'def': 'an outer garment that has sleeves and covers the body from shoulder down', 'name': 'coat'}, {'frequency': 'c', 'id': 283, 'synset': 'coat_hanger.n.01', 'synonyms': ['coat_hanger', 'clothes_hanger', 'dress_hanger'], 'def': "a hanger that is shaped like a person's shoulders", 'name': 'coat_hanger'}, {'frequency': 'r', 'id': 284, 'synset': 'coatrack.n.01', 'synonyms': ['coatrack', 'hatrack'], 'def': 'a rack with hooks for temporarily holding coats and hats', 'name': 'coatrack'}, {'frequency': 'c', 'id': 285, 'synset': 'cock.n.04', 'synonyms': ['cock', 'rooster'], 'def': 'adult male chicken', 'name': 'cock'}, {'frequency': 'c', 'id': 286, 'synset': 'coconut.n.02', 'synonyms': ['coconut', 'cocoanut'], 'def': 'large hard-shelled brown oval nut with a fibrous husk', 'name': 'coconut'}, {'frequency': 'r', 'id': 287, 'synset': 'coffee_filter.n.01', 'synonyms': ['coffee_filter'], 'def': 'filter (usually of paper) that passes the coffee and retains the coffee grounds', 'name': 'coffee_filter'}, {'frequency': 'f', 'id': 288, 'synset': 'coffee_maker.n.01', 'synonyms': ['coffee_maker', 'coffee_machine'], 'def': 'a kitchen appliance for brewing coffee automatically', 'name': 'coffee_maker'}, {'frequency': 'f', 'id': 289, 'synset': 'coffee_table.n.01', 'synonyms': ['coffee_table', 'cocktail_table'], 'def': 'low table where magazines can be placed and coffee or cocktails are served', 'name': 'coffee_table'}, {'frequency': 'c', 'id': 290, 'synset': 'coffeepot.n.01', 'synonyms': ['coffeepot'], 'def': 'tall pot in which coffee is brewed', 'name': 'coffeepot'}, {'frequency': 'r', 'id': 291, 'synset': 'coil.n.05', 'synonyms': ['coil'], 'def': 'tubing that is wound in a spiral', 'name': 'coil'}, {'frequency': 'c', 'id': 292, 'synset': 'coin.n.01', 'synonyms': ['coin'], 'def': 'a flat metal piece (usually a disc) used as money', 'name': 'coin'}, {'frequency': 'r', 'id': 293, 'synset': 'colander.n.01', 'synonyms': ['colander', 'cullender'], 'def': 'bowl-shaped strainer; used to wash or drain foods', 'name': 'colander'}, {'frequency': 'c', 'id': 294, 'synset': 'coleslaw.n.01', 'synonyms': ['coleslaw', 'slaw'], 'def': 'basically shredded cabbage', 'name': 'coleslaw'}, {'frequency': 'r', 'id': 295, 'synset': 'coloring_material.n.01', 'synonyms': ['coloring_material', 'colouring_material'], 'def': 'any material used for its color', 'name': 'coloring_material'}, {'frequency': 'r', 'id': 296, 'synset': 'combination_lock.n.01', 'synonyms': ['combination_lock'], 'def': 'lock that can be opened only by turning dials in a special sequence', 'name': 'combination_lock'}, {'frequency': 'c', 'id': 297, 'synset': 'comforter.n.04', 'synonyms': ['pacifier', 'teething_ring'], 'def': 'device used for an infant to suck or bite on', 'name': 'pacifier'}, {'frequency': 'r', 'id': 298, 'synset': 'comic_book.n.01', 'synonyms': ['comic_book'], 'def': 'a magazine devoted to comic strips', 'name': 'comic_book'}, {'frequency': 'f', 'id': 299, 'synset': 'computer_keyboard.n.01', 'synonyms': ['computer_keyboard', 'keyboard_(computer)'], 'def': 'a keyboard that is a data input device for computers', 'name': 'computer_keyboard'}, {'frequency': 'r', 'id': 300, 'synset': 'concrete_mixer.n.01', 'synonyms': ['concrete_mixer', 'cement_mixer'], 'def': 'a machine with a large revolving drum in which cement/concrete is mixed', 'name': 'concrete_mixer'}, {'frequency': 'f', 'id': 301, 'synset': 'cone.n.01', 'synonyms': ['cone', 'traffic_cone'], 'def': 'a cone-shaped object used to direct traffic', 'name': 'cone'}, {'frequency': 'f', 'id': 302, 'synset': 'control.n.09', 'synonyms': ['control', 'controller'], 'def': 'a mechanism that controls the operation of a machine', 'name': 'control'}, {'frequency': 'r', 'id': 303, 'synset': 'convertible.n.01', 'synonyms': ['convertible_(automobile)'], 'def': 'a car that has top that can be folded or removed', 'name': 'convertible_(automobile)'}, {'frequency': 'r', 'id': 304, 'synset': 'convertible.n.03', 'synonyms': ['sofa_bed'], 'def': 'a sofa that can be converted into a bed', 'name': 'sofa_bed'}, {'frequency': 'c', 'id': 305, 'synset': 'cookie.n.01', 'synonyms': ['cookie', 'cooky', 'biscuit_(cookie)'], 'def': "any of various small flat sweet cakes (`biscuit' is the British term)", 'name': 'cookie'}, {'frequency': 'r', 'id': 306, 'synset': 'cookie_jar.n.01', 'synonyms': ['cookie_jar', 'cooky_jar'], 'def': 'a jar in which cookies are kept (and sometimes money is hidden)', 'name': 'cookie_jar'}, {'frequency': 'r', 'id': 307, 'synset': 'cooking_utensil.n.01', 'synonyms': ['cooking_utensil'], 'def': 'a kitchen utensil made of material that does not melt easily; used for cooking', 'name': 'cooking_utensil'}, {'frequency': 'f', 'id': 308, 'synset': 'cooler.n.01', 'synonyms': ['cooler_(for_food)', 'ice_chest'], 'def': 'an insulated box for storing food often with ice', 'name': 'cooler_(for_food)'}, {'frequency': 'c', 'id': 309, 'synset': 'cork.n.04', 'synonyms': ['cork_(bottle_plug)', 'bottle_cork'], 'def': 'the plug in the mouth of a bottle (especially a wine bottle)', 'name': 'cork_(bottle_plug)'}, {'frequency': 'r', 'id': 310, 'synset': 'corkboard.n.01', 'synonyms': ['corkboard'], 'def': 'a sheet consisting of cork granules', 'name': 'corkboard'}, {'frequency': 'r', 'id': 311, 'synset': 'corkscrew.n.01', 'synonyms': ['corkscrew', 'bottle_screw'], 'def': 'a bottle opener that pulls corks', 'name': 'corkscrew'}, {'frequency': 'c', 'id': 312, 'synset': 'corn.n.03', 'synonyms': ['edible_corn', 'corn', 'maize'], 'def': 'ears of corn that can be prepared and served for human food', 'name': 'edible_corn'}, {'frequency': 'r', 'id': 313, 'synset': 'cornbread.n.01', 'synonyms': ['cornbread'], 'def': 'bread made primarily of cornmeal', 'name': 'cornbread'}, {'frequency': 'c', 'id': 314, 'synset': 'cornet.n.01', 'synonyms': ['cornet', 'horn', 'trumpet'], 'def': 'a brass musical instrument with a narrow tube and a flared bell and many valves', 'name': 'cornet'}, {'frequency': 'c', 'id': 315, 'synset': 'cornice.n.01', 'synonyms': ['cornice', 'valance', 'valance_board', 'pelmet'], 'def': 'a decorative framework to conceal curtain fixtures at the top of a window casing', 'name': 'cornice'}, {'frequency': 'r', 'id': 316, 'synset': 'cornmeal.n.01', 'synonyms': ['cornmeal'], 'def': 'coarsely ground corn', 'name': 'cornmeal'}, {'frequency': 'r', 'id': 317, 'synset': 'corset.n.01', 'synonyms': ['corset', 'girdle'], 'def': "a woman's close-fitting foundation garment", 'name': 'corset'}, {'frequency': 'r', 'id': 318, 'synset': 'cos.n.02', 'synonyms': ['romaine_lettuce'], 'def': 'lettuce with long dark-green leaves in a loosely packed elongated head', 'name': 'romaine_lettuce'}, {'frequency': 'c', 'id': 319, 'synset': 'costume.n.04', 'synonyms': ['costume'], 'def': 'the attire characteristic of a country or a time or a social class', 'name': 'costume'}, {'frequency': 'r', 'id': 320, 'synset': 'cougar.n.01', 'synonyms': ['cougar', 'puma', 'catamount', 'mountain_lion', 'panther'], 'def': 'large American feline resembling a lion', 'name': 'cougar'}, {'frequency': 'r', 'id': 321, 'synset': 'coverall.n.01', 'synonyms': ['coverall'], 'def': 'a loose-fitting protective garment that is worn over other clothing', 'name': 'coverall'}, {'frequency': 'r', 'id': 322, 'synset': 'cowbell.n.01', 'synonyms': ['cowbell'], 'def': 'a bell hung around the neck of cow so that the cow can be easily located', 'name': 'cowbell'}, {'frequency': 'f', 'id': 323, 'synset': 'cowboy_hat.n.01', 'synonyms': ['cowboy_hat', 'ten-gallon_hat'], 'def': 'a hat with a wide brim and a soft crown; worn by American ranch hands', 'name': 'cowboy_hat'}, {'frequency': 'r', 'id': 324, 'synset': 'crab.n.01', 'synonyms': ['crab_(animal)'], 'def': 'decapod having eyes on short stalks and a broad flattened shell and pincers', 'name': 'crab_(animal)'}, {'frequency': 'c', 'id': 325, 'synset': 'cracker.n.01', 'synonyms': ['cracker'], 'def': 'a thin crisp wafer', 'name': 'cracker'}, {'frequency': 'r', 'id': 326, 'synset': 'crape.n.01', 'synonyms': ['crape', 'crepe', 'French_pancake'], 'def': 'small very thin pancake', 'name': 'crape'}, {'frequency': 'f', 'id': 327, 'synset': 'crate.n.01', 'synonyms': ['crate'], 'def': 'a rugged box (usually made of wood); used for shipping', 'name': 'crate'}, {'frequency': 'r', 'id': 328, 'synset': 'crayon.n.01', 'synonyms': ['crayon', 'wax_crayon'], 'def': 'writing or drawing implement made of a colored stick of composition wax', 'name': 'crayon'}, {'frequency': 'r', 'id': 329, 'synset': 'cream_pitcher.n.01', 'synonyms': ['cream_pitcher'], 'def': 'a small pitcher for serving cream', 'name': 'cream_pitcher'}, {'frequency': 'r', 'id': 330, 'synset': 'credit_card.n.01', 'synonyms': ['credit_card', 'charge_card', 'debit_card'], 'def': 'a card, usually plastic, used to pay for goods and services', 'name': 'credit_card'}, {'frequency': 'c', 'id': 331, 'synset': 'crescent_roll.n.01', 'synonyms': ['crescent_roll', 'croissant'], 'def': 'very rich flaky crescent-shaped roll', 'name': 'crescent_roll'}, {'frequency': 'c', 'id': 332, 'synset': 'crib.n.01', 'synonyms': ['crib', 'cot'], 'def': 'baby bed with high sides made of slats', 'name': 'crib'}, {'frequency': 'c', 'id': 333, 'synset': 'crock.n.03', 'synonyms': ['crock_pot', 'earthenware_jar'], 'def': 'an earthen jar (made of baked clay)', 'name': 'crock_pot'}, {'frequency': 'f', 'id': 334, 'synset': 'crossbar.n.01', 'synonyms': ['crossbar'], 'def': 'a horizontal bar that goes across something', 'name': 'crossbar'}, {'frequency': 'r', 'id': 335, 'synset': 'crouton.n.01', 'synonyms': ['crouton'], 'def': 'a small piece of toasted or fried bread; served in soup or salads', 'name': 'crouton'}, {'frequency': 'r', 'id': 336, 'synset': 'crow.n.01', 'synonyms': ['crow'], 'def': 'black birds having a raucous call', 'name': 'crow'}, {'frequency': 'c', 'id': 337, 'synset': 'crown.n.04', 'synonyms': ['crown'], 'def': 'an ornamental jeweled headdress signifying sovereignty', 'name': 'crown'}, {'frequency': 'c', 'id': 338, 'synset': 'crucifix.n.01', 'synonyms': ['crucifix'], 'def': 'representation of the cross on which Jesus died', 'name': 'crucifix'}, {'frequency': 'c', 'id': 339, 'synset': 'cruise_ship.n.01', 'synonyms': ['cruise_ship', 'cruise_liner'], 'def': 'a passenger ship used commercially for pleasure cruises', 'name': 'cruise_ship'}, {'frequency': 'c', 'id': 340, 'synset': 'cruiser.n.01', 'synonyms': ['police_cruiser', 'patrol_car', 'police_car', 'squad_car'], 'def': 'a car in which policemen cruise the streets', 'name': 'police_cruiser'}, {'frequency': 'c', 'id': 341, 'synset': 'crumb.n.03', 'synonyms': ['crumb'], 'def': 'small piece of e.g. bread or cake', 'name': 'crumb'}, {'frequency': 'r', 'id': 342, 'synset': 'crutch.n.01', 'synonyms': ['crutch'], 'def': 'a wooden or metal staff that fits under the armpit and reaches to the ground', 'name': 'crutch'}, {'frequency': 'c', 'id': 343, 'synset': 'cub.n.03', 'synonyms': ['cub_(animal)'], 'def': 'the young of certain carnivorous mammals such as the bear or wolf or lion', 'name': 'cub_(animal)'}, {'frequency': 'r', 'id': 344, 'synset': 'cube.n.05', 'synonyms': ['cube', 'square_block'], 'def': 'a block in the (approximate) shape of a cube', 'name': 'cube'}, {'frequency': 'f', 'id': 345, 'synset': 'cucumber.n.02', 'synonyms': ['cucumber', 'cuke'], 'def': 'cylindrical green fruit with thin green rind and white flesh eaten as a vegetable', 'name': 'cucumber'}, {'frequency': 'c', 'id': 346, 'synset': 'cufflink.n.01', 'synonyms': ['cufflink'], 'def': 'jewelry consisting of linked buttons used to fasten the cuffs of a shirt', 'name': 'cufflink'}, {'frequency': 'f', 'id': 347, 'synset': 'cup.n.01', 'synonyms': ['cup'], 'def': 'a small open container usually used for drinking; usually has a handle', 'name': 'cup'}, {'frequency': 'c', 'id': 348, 'synset': 'cup.n.08', 'synonyms': ['trophy_cup'], 'def': 'a metal vessel with handles that is awarded as a trophy to a competition winner', 'name': 'trophy_cup'}, {'frequency': 'c', 'id': 349, 'synset': 'cupcake.n.01', 'synonyms': ['cupcake'], 'def': 'small cake baked in a muffin tin', 'name': 'cupcake'}, {'frequency': 'r', 'id': 350, 'synset': 'curler.n.01', 'synonyms': ['hair_curler', 'hair_roller', 'hair_crimper'], 'def': 'a cylindrical tube around which the hair is wound to curl it', 'name': 'hair_curler'}, {'frequency': 'r', 'id': 351, 'synset': 'curling_iron.n.01', 'synonyms': ['curling_iron'], 'def': 'a cylindrical home appliance that heats hair that has been curled around it', 'name': 'curling_iron'}, {'frequency': 'f', 'id': 352, 'synset': 'curtain.n.01', 'synonyms': ['curtain', 'drapery'], 'def': 'hanging cloth used as a blind (especially for a window)', 'name': 'curtain'}, {'frequency': 'f', 'id': 353, 'synset': 'cushion.n.03', 'synonyms': ['cushion'], 'def': 'a soft bag filled with air or padding such as feathers or foam rubber', 'name': 'cushion'}, {'frequency': 'r', 'id': 354, 'synset': 'custard.n.01', 'synonyms': ['custard'], 'def': 'sweetened mixture of milk and eggs baked or boiled or frozen', 'name': 'custard'}, {'frequency': 'c', 'id': 355, 'synset': 'cutter.n.06', 'synonyms': ['cutting_tool'], 'def': 'a cutting implement; a tool for cutting', 'name': 'cutting_tool'}, {'frequency': 'r', 'id': 356, 'synset': 'cylinder.n.04', 'synonyms': ['cylinder'], 'def': 'a cylindrical container', 'name': 'cylinder'}, {'frequency': 'r', 'id': 357, 'synset': 'cymbal.n.01', 'synonyms': ['cymbal'], 'def': 'a percussion instrument consisting of a concave brass disk', 'name': 'cymbal'}, {'frequency': 'r', 'id': 358, 'synset': 'dachshund.n.01', 'synonyms': ['dachshund', 'dachsie', 'badger_dog'], 'def': 'small long-bodied short-legged breed of dog having a short sleek coat and long drooping ears', 'name': 'dachshund'}, {'frequency': 'r', 'id': 359, 'synset': 'dagger.n.01', 'synonyms': ['dagger'], 'def': 'a short knife with a pointed blade used for piercing or stabbing', 'name': 'dagger'}, {'frequency': 'r', 'id': 360, 'synset': 'dartboard.n.01', 'synonyms': ['dartboard'], 'def': 'a circular board of wood or cork used as the target in the game of darts', 'name': 'dartboard'}, {'frequency': 'r', 'id': 361, 'synset': 'date.n.08', 'synonyms': ['date_(fruit)'], 'def': 'sweet edible fruit of the date palm with a single long woody seed', 'name': 'date_(fruit)'}, {'frequency': 'f', 'id': 362, 'synset': 'deck_chair.n.01', 'synonyms': ['deck_chair', 'beach_chair'], 'def': 'a folding chair for use outdoors; a wooden frame supports a length of canvas', 'name': 'deck_chair'}, {'frequency': 'c', 'id': 363, 'synset': 'deer.n.01', 'synonyms': ['deer', 'cervid'], 'def': "distinguished from Bovidae by the male's having solid deciduous antlers", 'name': 'deer'}, {'frequency': 'c', 'id': 364, 'synset': 'dental_floss.n.01', 'synonyms': ['dental_floss', 'floss'], 'def': 'a soft thread for cleaning the spaces between the teeth', 'name': 'dental_floss'}, {'frequency': 'f', 'id': 365, 'synset': 'desk.n.01', 'synonyms': ['desk'], 'def': 'a piece of furniture with a writing surface and usually drawers or other compartments', 'name': 'desk'}, {'frequency': 'r', 'id': 366, 'synset': 'detergent.n.01', 'synonyms': ['detergent'], 'def': 'a surface-active chemical widely used in industry and laundering', 'name': 'detergent'}, {'frequency': 'c', 'id': 367, 'synset': 'diaper.n.01', 'synonyms': ['diaper'], 'def': 'garment consisting of a folded cloth drawn up between the legs and fastened at the waist', 'name': 'diaper'}, {'frequency': 'r', 'id': 368, 'synset': 'diary.n.01', 'synonyms': ['diary', 'journal'], 'def': 'a daily written record of (usually personal) experiences and observations', 'name': 'diary'}, {'frequency': 'r', 'id': 369, 'synset': 'die.n.01', 'synonyms': ['die', 'dice'], 'def': 'a small cube with 1 to 6 spots on the six faces; used in gambling', 'name': 'die'}, {'frequency': 'r', 'id': 370, 'synset': 'dinghy.n.01', 'synonyms': ['dinghy', 'dory', 'rowboat'], 'def': 'a small boat of shallow draft with seats and oars with which it is propelled', 'name': 'dinghy'}, {'frequency': 'f', 'id': 371, 'synset': 'dining_table.n.01', 'synonyms': ['dining_table'], 'def': 'a table at which meals are served', 'name': 'dining_table'}, {'frequency': 'r', 'id': 372, 'synset': 'dinner_jacket.n.01', 'synonyms': ['tux', 'tuxedo'], 'def': 'semiformal evening dress for men', 'name': 'tux'}, {'frequency': 'c', 'id': 373, 'synset': 'dish.n.01', 'synonyms': ['dish'], 'def': 'a piece of dishware normally used as a container for holding or serving food', 'name': 'dish'}, {'frequency': 'c', 'id': 374, 'synset': 'dish.n.05', 'synonyms': ['dish_antenna'], 'def': 'directional antenna consisting of a parabolic reflector', 'name': 'dish_antenna'}, {'frequency': 'c', 'id': 375, 'synset': 'dishrag.n.01', 'synonyms': ['dishrag', 'dishcloth'], 'def': 'a cloth for washing dishes', 'name': 'dishrag'}, {'frequency': 'c', 'id': 376, 'synset': 'dishtowel.n.01', 'synonyms': ['dishtowel', 'tea_towel'], 'def': 'a towel for drying dishes', 'name': 'dishtowel'}, {'frequency': 'f', 'id': 377, 'synset': 'dishwasher.n.01', 'synonyms': ['dishwasher', 'dishwashing_machine'], 'def': 'a machine for washing dishes', 'name': 'dishwasher'}, {'frequency': 'r', 'id': 378, 'synset': 'dishwasher_detergent.n.01', 'synonyms': ['dishwasher_detergent', 'dishwashing_detergent', 'dishwashing_liquid'], 'def': 'a low-sudsing detergent designed for use in dishwashers', 'name': 'dishwasher_detergent'}, {'frequency': 'r', 'id': 379, 'synset': 'diskette.n.01', 'synonyms': ['diskette', 'floppy', 'floppy_disk'], 'def': 'a small plastic magnetic disk enclosed in a stiff envelope used to store data', 'name': 'diskette'}, {'frequency': 'c', 'id': 380, 'synset': 'dispenser.n.01', 'synonyms': ['dispenser'], 'def': 'a container so designed that the contents can be used in prescribed amounts', 'name': 'dispenser'}, {'frequency': 'c', 'id': 381, 'synset': 'dixie_cup.n.01', 'synonyms': ['Dixie_cup', 'paper_cup'], 'def': 'a disposable cup made of paper; for holding drinks', 'name': 'Dixie_cup'}, {'frequency': 'f', 'id': 382, 'synset': 'dog.n.01', 'synonyms': ['dog'], 'def': 'a common domesticated dog', 'name': 'dog'}, {'frequency': 'f', 'id': 383, 'synset': 'dog_collar.n.01', 'synonyms': ['dog_collar'], 'def': 'a collar for a dog', 'name': 'dog_collar'}, {'frequency': 'c', 'id': 384, 'synset': 'doll.n.01', 'synonyms': ['doll'], 'def': 'a toy replica of a HUMAN (NOT AN ANIMAL)', 'name': 'doll'}, {'frequency': 'r', 'id': 385, 'synset': 'dollar.n.02', 'synonyms': ['dollar', 'dollar_bill', 'one_dollar_bill'], 'def': 'a piece of paper money worth one dollar', 'name': 'dollar'}, {'frequency': 'r', 'id': 386, 'synset': 'dolphin.n.02', 'synonyms': ['dolphin'], 'def': 'any of various small toothed whales with a beaklike snout; larger than porpoises', 'name': 'dolphin'}, {'frequency': 'c', 'id': 387, 'synset': 'domestic_ass.n.01', 'synonyms': ['domestic_ass', 'donkey'], 'def': 'domestic beast of burden descended from the African wild ass; patient but stubborn', 'name': 'domestic_ass'}, {'frequency': 'r', 'id': 388, 'synset': 'domino.n.03', 'synonyms': ['eye_mask'], 'def': 'a mask covering the upper part of the face but with holes for the eyes', 'name': 'eye_mask'}, {'frequency': 'r', 'id': 389, 'synset': 'doorbell.n.01', 'synonyms': ['doorbell', 'buzzer'], 'def': 'a button at an outer door that gives a ringing or buzzing signal when pushed', 'name': 'doorbell'}, {'frequency': 'f', 'id': 390, 'synset': 'doorknob.n.01', 'synonyms': ['doorknob', 'doorhandle'], 'def': "a knob used to open a door (often called `doorhandle' in Great Britain)", 'name': 'doorknob'}, {'frequency': 'c', 'id': 391, 'synset': 'doormat.n.02', 'synonyms': ['doormat', 'welcome_mat'], 'def': 'a mat placed outside an exterior door for wiping the shoes before entering', 'name': 'doormat'}, {'frequency': 'f', 'id': 392, 'synset': 'doughnut.n.02', 'synonyms': ['doughnut', 'donut'], 'def': 'a small ring-shaped friedcake', 'name': 'doughnut'}, {'frequency': 'r', 'id': 393, 'synset': 'dove.n.01', 'synonyms': ['dove'], 'def': 'any of numerous small pigeons', 'name': 'dove'}, {'frequency': 'r', 'id': 394, 'synset': 'dragonfly.n.01', 'synonyms': ['dragonfly'], 'def': 'slender-bodied non-stinging insect having iridescent wings that are outspread at rest', 'name': 'dragonfly'}, {'frequency': 'f', 'id': 395, 'synset': 'drawer.n.01', 'synonyms': ['drawer'], 'def': 'a boxlike container in a piece of furniture; made so as to slide in and out', 'name': 'drawer'}, {'frequency': 'c', 'id': 396, 'synset': 'drawers.n.01', 'synonyms': ['underdrawers', 'boxers', 'boxershorts'], 'def': 'underpants worn by men', 'name': 'underdrawers'}, {'frequency': 'f', 'id': 397, 'synset': 'dress.n.01', 'synonyms': ['dress', 'frock'], 'def': 'a one-piece garment for a woman; has skirt and bodice', 'name': 'dress'}, {'frequency': 'c', 'id': 398, 'synset': 'dress_hat.n.01', 'synonyms': ['dress_hat', 'high_hat', 'opera_hat', 'silk_hat', 'top_hat'], 'def': "a man's hat with a tall crown; usually covered with silk or with beaver fur", 'name': 'dress_hat'}, {'frequency': 'c', 'id': 399, 'synset': 'dress_suit.n.01', 'synonyms': ['dress_suit'], 'def': 'formalwear consisting of full evening dress for men', 'name': 'dress_suit'}, {'frequency': 'c', 'id': 400, 'synset': 'dresser.n.05', 'synonyms': ['dresser'], 'def': 'a cabinet with shelves', 'name': 'dresser'}, {'frequency': 'c', 'id': 401, 'synset': 'drill.n.01', 'synonyms': ['drill'], 'def': 'a tool with a sharp rotating point for making holes in hard materials', 'name': 'drill'}, {'frequency': 'r', 'id': 402, 'synset': 'drinking_fountain.n.01', 'synonyms': ['drinking_fountain'], 'def': 'a public fountain to provide a jet of drinking water', 'name': 'drinking_fountain'}, {'frequency': 'r', 'id': 403, 'synset': 'drone.n.04', 'synonyms': ['drone'], 'def': 'an aircraft without a pilot that is operated by remote control', 'name': 'drone'}, {'frequency': 'r', 'id': 404, 'synset': 'dropper.n.01', 'synonyms': ['dropper', 'eye_dropper'], 'def': 'pipet consisting of a small tube with a vacuum bulb at one end for drawing liquid in and releasing it a drop at a time', 'name': 'dropper'}, {'frequency': 'c', 'id': 405, 'synset': 'drum.n.01', 'synonyms': ['drum_(musical_instrument)'], 'def': 'a musical percussion instrument; usually consists of a hollow cylinder with a membrane stretched across each end', 'name': 'drum_(musical_instrument)'}, {'frequency': 'r', 'id': 406, 'synset': 'drumstick.n.02', 'synonyms': ['drumstick'], 'def': 'a stick used for playing a drum', 'name': 'drumstick'}, {'frequency': 'f', 'id': 407, 'synset': 'duck.n.01', 'synonyms': ['duck'], 'def': 'small web-footed broad-billed swimming bird', 'name': 'duck'}, {'frequency': 'r', 'id': 408, 'synset': 'duckling.n.02', 'synonyms': ['duckling'], 'def': 'young duck', 'name': 'duckling'}, {'frequency': 'c', 'id': 409, 'synset': 'duct_tape.n.01', 'synonyms': ['duct_tape'], 'def': 'a wide silvery adhesive tape', 'name': 'duct_tape'}, {'frequency': 'f', 'id': 410, 'synset': 'duffel_bag.n.01', 'synonyms': ['duffel_bag', 'duffle_bag', 'duffel', 'duffle'], 'def': 'a large cylindrical bag of heavy cloth', 'name': 'duffel_bag'}, {'frequency': 'r', 'id': 411, 'synset': 'dumbbell.n.01', 'synonyms': ['dumbbell'], 'def': 'an exercising weight with two ball-like ends connected by a short handle', 'name': 'dumbbell'}, {'frequency': 'c', 'id': 412, 'synset': 'dumpster.n.01', 'synonyms': ['dumpster'], 'def': 'a container designed to receive and transport and dump waste', 'name': 'dumpster'}, {'frequency': 'r', 'id': 413, 'synset': 'dustpan.n.02', 'synonyms': ['dustpan'], 'def': 'a short-handled receptacle into which dust can be swept', 'name': 'dustpan'}, {'frequency': 'r', 'id': 414, 'synset': 'dutch_oven.n.02', 'synonyms': ['Dutch_oven'], 'def': 'iron or earthenware cooking pot; used for stews', 'name': 'Dutch_oven'}, {'frequency': 'c', 'id': 415, 'synset': 'eagle.n.01', 'synonyms': ['eagle'], 'def': 'large birds of prey noted for their broad wings and strong soaring flight', 'name': 'eagle'}, {'frequency': 'f', 'id': 416, 'synset': 'earphone.n.01', 'synonyms': ['earphone', 'earpiece', 'headphone'], 'def': 'device for listening to audio that is held over or inserted into the ear', 'name': 'earphone'}, {'frequency': 'r', 'id': 417, 'synset': 'earplug.n.01', 'synonyms': ['earplug'], 'def': 'a soft plug that is inserted into the ear canal to block sound', 'name': 'earplug'}, {'frequency': 'f', 'id': 418, 'synset': 'earring.n.01', 'synonyms': ['earring'], 'def': 'jewelry to ornament the ear', 'name': 'earring'}, {'frequency': 'c', 'id': 419, 'synset': 'easel.n.01', 'synonyms': ['easel'], 'def': "an upright tripod for displaying something (usually an artist's canvas)", 'name': 'easel'}, {'frequency': 'r', 'id': 420, 'synset': 'eclair.n.01', 'synonyms': ['eclair'], 'def': 'oblong cream puff', 'name': 'eclair'}, {'frequency': 'r', 'id': 421, 'synset': 'eel.n.01', 'synonyms': ['eel'], 'def': 'an elongate fish with fatty flesh', 'name': 'eel'}, {'frequency': 'f', 'id': 422, 'synset': 'egg.n.02', 'synonyms': ['egg', 'eggs'], 'def': 'oval reproductive body of a fowl (especially a hen) used as food', 'name': 'egg'}, {'frequency': 'r', 'id': 423, 'synset': 'egg_roll.n.01', 'synonyms': ['egg_roll', 'spring_roll'], 'def': 'minced vegetables and meat wrapped in a pancake and fried', 'name': 'egg_roll'}, {'frequency': 'c', 'id': 424, 'synset': 'egg_yolk.n.01', 'synonyms': ['egg_yolk', 'yolk_(egg)'], 'def': 'the yellow spherical part of an egg', 'name': 'egg_yolk'}, {'frequency': 'c', 'id': 425, 'synset': 'eggbeater.n.02', 'synonyms': ['eggbeater', 'eggwhisk'], 'def': 'a mixer for beating eggs or whipping cream', 'name': 'eggbeater'}, {'frequency': 'c', 'id': 426, 'synset': 'eggplant.n.01', 'synonyms': ['eggplant', 'aubergine'], 'def': 'egg-shaped vegetable having a shiny skin typically dark purple', 'name': 'eggplant'}, {'frequency': 'r', 'id': 427, 'synset': 'electric_chair.n.01', 'synonyms': ['electric_chair'], 'def': 'a chair-shaped instrument of execution by electrocution', 'name': 'electric_chair'}, {'frequency': 'f', 'id': 428, 'synset': 'electric_refrigerator.n.01', 'synonyms': ['refrigerator'], 'def': 'a refrigerator in which the coolant is pumped around by an electric motor', 'name': 'refrigerator'}, {'frequency': 'f', 'id': 429, 'synset': 'elephant.n.01', 'synonyms': ['elephant'], 'def': 'a common elephant', 'name': 'elephant'}, {'frequency': 'r', 'id': 430, 'synset': 'elk.n.01', 'synonyms': ['elk', 'moose'], 'def': 'large northern deer with enormous flattened antlers in the male', 'name': 'elk'}, {'frequency': 'c', 'id': 431, 'synset': 'envelope.n.01', 'synonyms': ['envelope'], 'def': 'a flat (usually rectangular) container for a letter, thin package, etc.', 'name': 'envelope'}, {'frequency': 'c', 'id': 432, 'synset': 'eraser.n.01', 'synonyms': ['eraser'], 'def': 'an implement used to erase something', 'name': 'eraser'}, {'frequency': 'r', 'id': 433, 'synset': 'escargot.n.01', 'synonyms': ['escargot'], 'def': 'edible snail usually served in the shell with a sauce of melted butter and garlic', 'name': 'escargot'}, {'frequency': 'r', 'id': 434, 'synset': 'eyepatch.n.01', 'synonyms': ['eyepatch'], 'def': 'a protective cloth covering for an injured eye', 'name': 'eyepatch'}, {'frequency': 'r', 'id': 435, 'synset': 'falcon.n.01', 'synonyms': ['falcon'], 'def': 'birds of prey having long pointed powerful wings adapted for swift flight', 'name': 'falcon'}, {'frequency': 'f', 'id': 436, 'synset': 'fan.n.01', 'synonyms': ['fan'], 'def': 'a device for creating a current of air by movement of a surface or surfaces', 'name': 'fan'}, {'frequency': 'f', 'id': 437, 'synset': 'faucet.n.01', 'synonyms': ['faucet', 'spigot', 'tap'], 'def': 'a regulator for controlling the flow of a liquid from a reservoir', 'name': 'faucet'}, {'frequency': 'r', 'id': 438, 'synset': 'fedora.n.01', 'synonyms': ['fedora'], 'def': 'a hat made of felt with a creased crown', 'name': 'fedora'}, {'frequency': 'r', 'id': 439, 'synset': 'ferret.n.02', 'synonyms': ['ferret'], 'def': 'domesticated albino variety of the European polecat bred for hunting rats and rabbits', 'name': 'ferret'}, {'frequency': 'c', 'id': 440, 'synset': 'ferris_wheel.n.01', 'synonyms': ['Ferris_wheel'], 'def': 'a large wheel with suspended seats that remain upright as the wheel rotates', 'name': 'Ferris_wheel'}, {'frequency': 'r', 'id': 441, 'synset': 'ferry.n.01', 'synonyms': ['ferry', 'ferryboat'], 'def': 'a boat that transports people or vehicles across a body of water and operates on a regular schedule', 'name': 'ferry'}, {'frequency': 'r', 'id': 442, 'synset': 'fig.n.04', 'synonyms': ['fig_(fruit)'], 'def': 'fleshy sweet pear-shaped yellowish or purple fruit eaten fresh or preserved or dried', 'name': 'fig_(fruit)'}, {'frequency': 'c', 'id': 443, 'synset': 'fighter.n.02', 'synonyms': ['fighter_jet', 'fighter_aircraft', 'attack_aircraft'], 'def': 'a high-speed military or naval airplane designed to destroy enemy targets', 'name': 'fighter_jet'}, {'frequency': 'f', 'id': 444, 'synset': 'figurine.n.01', 'synonyms': ['figurine'], 'def': 'a small carved or molded figure', 'name': 'figurine'}, {'frequency': 'c', 'id': 445, 'synset': 'file.n.03', 'synonyms': ['file_cabinet', 'filing_cabinet'], 'def': 'office furniture consisting of a container for keeping papers in order', 'name': 'file_cabinet'}, {'frequency': 'r', 'id': 446, 'synset': 'file.n.04', 'synonyms': ['file_(tool)'], 'def': 'a steel hand tool with small sharp teeth on some or all of its surfaces; used for smoothing wood or metal', 'name': 'file_(tool)'}, {'frequency': 'f', 'id': 447, 'synset': 'fire_alarm.n.02', 'synonyms': ['fire_alarm', 'smoke_alarm'], 'def': 'an alarm that is tripped off by fire or smoke', 'name': 'fire_alarm'}, {'frequency': 'c', 'id': 448, 'synset': 'fire_engine.n.01', 'synonyms': ['fire_engine', 'fire_truck'], 'def': 'large trucks that carry firefighters and equipment to the site of a fire', 'name': 'fire_engine'}, {'frequency': 'c', 'id': 449, 'synset': 'fire_extinguisher.n.01', 'synonyms': ['fire_extinguisher', 'extinguisher'], 'def': 'a manually operated device for extinguishing small fires', 'name': 'fire_extinguisher'}, {'frequency': 'c', 'id': 450, 'synset': 'fire_hose.n.01', 'synonyms': ['fire_hose'], 'def': 'a large hose that carries water from a fire hydrant to the site of the fire', 'name': 'fire_hose'}, {'frequency': 'f', 'id': 451, 'synset': 'fireplace.n.01', 'synonyms': ['fireplace'], 'def': 'an open recess in a wall at the base of a chimney where a fire can be built', 'name': 'fireplace'}, {'frequency': 'f', 'id': 452, 'synset': 'fireplug.n.01', 'synonyms': ['fireplug', 'fire_hydrant', 'hydrant'], 'def': 'an upright hydrant for drawing water to use in fighting a fire', 'name': 'fireplug'}, {'frequency': 'c', 'id': 453, 'synset': 'fish.n.01', 'synonyms': ['fish'], 'def': 'any of various mostly cold-blooded aquatic vertebrates usually having scales and breathing through gills', 'name': 'fish'}, {'frequency': 'r', 'id': 454, 'synset': 'fish.n.02', 'synonyms': ['fish_(food)'], 'def': 'the flesh of fish used as food', 'name': 'fish_(food)'}, {'frequency': 'r', 'id': 455, 'synset': 'fishbowl.n.02', 'synonyms': ['fishbowl', 'goldfish_bowl'], 'def': 'a transparent bowl in which small fish are kept', 'name': 'fishbowl'}, {'frequency': 'r', 'id': 456, 'synset': 'fishing_boat.n.01', 'synonyms': ['fishing_boat', 'fishing_vessel'], 'def': 'a vessel for fishing', 'name': 'fishing_boat'}, {'frequency': 'c', 'id': 457, 'synset': 'fishing_rod.n.01', 'synonyms': ['fishing_rod', 'fishing_pole'], 'def': 'a rod that is used in fishing to extend the fishing line', 'name': 'fishing_rod'}, {'frequency': 'f', 'id': 458, 'synset': 'flag.n.01', 'synonyms': ['flag'], 'def': 'emblem usually consisting of a rectangular piece of cloth of distinctive design (do not include pole)', 'name': 'flag'}, {'frequency': 'f', 'id': 459, 'synset': 'flagpole.n.02', 'synonyms': ['flagpole', 'flagstaff'], 'def': 'a tall staff or pole on which a flag is raised', 'name': 'flagpole'}, {'frequency': 'c', 'id': 460, 'synset': 'flamingo.n.01', 'synonyms': ['flamingo'], 'def': 'large pink web-footed bird with down-bent bill', 'name': 'flamingo'}, {'frequency': 'c', 'id': 461, 'synset': 'flannel.n.01', 'synonyms': ['flannel'], 'def': 'a soft light woolen fabric; used for clothing', 'name': 'flannel'}, {'frequency': 'r', 'id': 462, 'synset': 'flash.n.10', 'synonyms': ['flash', 'flashbulb'], 'def': 'a lamp for providing momentary light to take a photograph', 'name': 'flash'}, {'frequency': 'c', 'id': 463, 'synset': 'flashlight.n.01', 'synonyms': ['flashlight', 'torch'], 'def': 'a small portable battery-powered electric lamp', 'name': 'flashlight'}, {'frequency': 'r', 'id': 464, 'synset': 'fleece.n.03', 'synonyms': ['fleece'], 'def': 'a soft bulky fabric with deep pile; used chiefly for clothing', 'name': 'fleece'}, {'frequency': 'f', 'id': 465, 'synset': 'flip-flop.n.02', 'synonyms': ['flip-flop_(sandal)'], 'def': 'a backless sandal held to the foot by a thong between two toes', 'name': 'flip-flop_(sandal)'}, {'frequency': 'c', 'id': 466, 'synset': 'flipper.n.01', 'synonyms': ['flipper_(footwear)', 'fin_(footwear)'], 'def': 'a shoe to aid a person in swimming', 'name': 'flipper_(footwear)'}, {'frequency': 'f', 'id': 467, 'synset': 'flower_arrangement.n.01', 'synonyms': ['flower_arrangement', 'floral_arrangement'], 'def': 'a decorative arrangement of flowers', 'name': 'flower_arrangement'}, {'frequency': 'c', 'id': 468, 'synset': 'flute.n.02', 'synonyms': ['flute_glass', 'champagne_flute'], 'def': 'a tall narrow wineglass', 'name': 'flute_glass'}, {'frequency': 'r', 'id': 469, 'synset': 'foal.n.01', 'synonyms': ['foal'], 'def': 'a young horse', 'name': 'foal'}, {'frequency': 'c', 'id': 470, 'synset': 'folding_chair.n.01', 'synonyms': ['folding_chair'], 'def': 'a chair that can be folded flat for storage', 'name': 'folding_chair'}, {'frequency': 'c', 'id': 471, 'synset': 'food_processor.n.01', 'synonyms': ['food_processor'], 'def': 'a kitchen appliance for shredding, blending, chopping, or slicing food', 'name': 'food_processor'}, {'frequency': 'c', 'id': 472, 'synset': 'football.n.02', 'synonyms': ['football_(American)'], 'def': 'the inflated oblong ball used in playing American football', 'name': 'football_(American)'}, {'frequency': 'r', 'id': 473, 'synset': 'football_helmet.n.01', 'synonyms': ['football_helmet'], 'def': 'a padded helmet with a face mask to protect the head of football players', 'name': 'football_helmet'}, {'frequency': 'c', 'id': 474, 'synset': 'footstool.n.01', 'synonyms': ['footstool', 'footrest'], 'def': 'a low seat or a stool to rest the feet of a seated person', 'name': 'footstool'}, {'frequency': 'f', 'id': 475, 'synset': 'fork.n.01', 'synonyms': ['fork'], 'def': 'cutlery used for serving and eating food', 'name': 'fork'}, {'frequency': 'r', 'id': 476, 'synset': 'forklift.n.01', 'synonyms': ['forklift'], 'def': 'an industrial vehicle with a power operated fork in front that can be inserted under loads to lift and move them', 'name': 'forklift'}, {'frequency': 'r', 'id': 477, 'synset': 'freight_car.n.01', 'synonyms': ['freight_car'], 'def': 'a railway car that carries freight', 'name': 'freight_car'}, {'frequency': 'r', 'id': 478, 'synset': 'french_toast.n.01', 'synonyms': ['French_toast'], 'def': 'bread slice dipped in egg and milk and fried', 'name': 'French_toast'}, {'frequency': 'c', 'id': 479, 'synset': 'freshener.n.01', 'synonyms': ['freshener', 'air_freshener'], 'def': 'anything that freshens', 'name': 'freshener'}, {'frequency': 'f', 'id': 480, 'synset': 'frisbee.n.01', 'synonyms': ['frisbee'], 'def': 'a light, plastic disk propelled with a flip of the wrist for recreation or competition', 'name': 'frisbee'}, {'frequency': 'c', 'id': 481, 'synset': 'frog.n.01', 'synonyms': ['frog', 'toad', 'toad_frog'], 'def': 'a tailless stout-bodied amphibians with long hind limbs for leaping', 'name': 'frog'}, {'frequency': 'c', 'id': 482, 'synset': 'fruit_juice.n.01', 'synonyms': ['fruit_juice'], 'def': 'drink produced by squeezing or crushing fruit', 'name': 'fruit_juice'}, {'frequency': 'r', 'id': 483, 'synset': 'fruit_salad.n.01', 'synonyms': ['fruit_salad'], 'def': 'salad composed of fruits', 'name': 'fruit_salad'}, {'frequency': 'c', 'id': 484, 'synset': 'frying_pan.n.01', 'synonyms': ['frying_pan', 'frypan', 'skillet'], 'def': 'a pan used for frying foods', 'name': 'frying_pan'}, {'frequency': 'r', 'id': 485, 'synset': 'fudge.n.01', 'synonyms': ['fudge'], 'def': 'soft creamy candy', 'name': 'fudge'}, {'frequency': 'r', 'id': 486, 'synset': 'funnel.n.02', 'synonyms': ['funnel'], 'def': 'a cone-shaped utensil used to channel a substance into a container with a small mouth', 'name': 'funnel'}, {'frequency': 'c', 'id': 487, 'synset': 'futon.n.01', 'synonyms': ['futon'], 'def': 'a pad that is used for sleeping on the floor or on a raised frame', 'name': 'futon'}, {'frequency': 'r', 'id': 488, 'synset': 'gag.n.02', 'synonyms': ['gag', 'muzzle'], 'def': "restraint put into a person's mouth to prevent speaking or shouting", 'name': 'gag'}, {'frequency': 'r', 'id': 489, 'synset': 'garbage.n.03', 'synonyms': ['garbage'], 'def': 'a receptacle where waste can be discarded', 'name': 'garbage'}, {'frequency': 'c', 'id': 490, 'synset': 'garbage_truck.n.01', 'synonyms': ['garbage_truck'], 'def': 'a truck for collecting domestic refuse', 'name': 'garbage_truck'}, {'frequency': 'c', 'id': 491, 'synset': 'garden_hose.n.01', 'synonyms': ['garden_hose'], 'def': 'a hose used for watering a lawn or garden', 'name': 'garden_hose'}, {'frequency': 'c', 'id': 492, 'synset': 'gargle.n.01', 'synonyms': ['gargle', 'mouthwash'], 'def': 'a medicated solution used for gargling and rinsing the mouth', 'name': 'gargle'}, {'frequency': 'r', 'id': 493, 'synset': 'gargoyle.n.02', 'synonyms': ['gargoyle'], 'def': 'an ornament consisting of a grotesquely carved figure of a person or animal', 'name': 'gargoyle'}, {'frequency': 'c', 'id': 494, 'synset': 'garlic.n.02', 'synonyms': ['garlic', 'ail'], 'def': 'aromatic bulb used as seasoning', 'name': 'garlic'}, {'frequency': 'r', 'id': 495, 'synset': 'gasmask.n.01', 'synonyms': ['gasmask', 'respirator', 'gas_helmet'], 'def': 'a protective face mask with a filter', 'name': 'gasmask'}, {'frequency': 'r', 'id': 496, 'synset': 'gazelle.n.01', 'synonyms': ['gazelle'], 'def': 'small swift graceful antelope of Africa and Asia having lustrous eyes', 'name': 'gazelle'}, {'frequency': 'c', 'id': 497, 'synset': 'gelatin.n.02', 'synonyms': ['gelatin', 'jelly'], 'def': 'an edible jelly made with gelatin and used as a dessert or salad base or a coating for foods', 'name': 'gelatin'}, {'frequency': 'r', 'id': 498, 'synset': 'gem.n.02', 'synonyms': ['gemstone'], 'def': 'a crystalline rock that can be cut and polished for jewelry', 'name': 'gemstone'}, {'frequency': 'c', 'id': 499, 'synset': 'giant_panda.n.01', 'synonyms': ['giant_panda', 'panda', 'panda_bear'], 'def': 'large black-and-white herbivorous mammal of bamboo forests of China and Tibet', 'name': 'giant_panda'}, {'frequency': 'c', 'id': 500, 'synset': 'gift_wrap.n.01', 'synonyms': ['gift_wrap'], 'def': 'attractive wrapping paper suitable for wrapping gifts', 'name': 'gift_wrap'}, {'frequency': 'c', 'id': 501, 'synset': 'ginger.n.03', 'synonyms': ['ginger', 'gingerroot'], 'def': 'the root of the common ginger plant; used fresh as a seasoning', 'name': 'ginger'}, {'frequency': 'f', 'id': 502, 'synset': 'giraffe.n.01', 'synonyms': ['giraffe'], 'def': 'tall animal having a spotted coat and small horns and very long neck and legs', 'name': 'giraffe'}, {'frequency': 'c', 'id': 503, 'synset': 'girdle.n.02', 'synonyms': ['cincture', 'sash', 'waistband', 'waistcloth'], 'def': 'a band of material around the waist that strengthens a skirt or trousers', 'name': 'cincture'}, {'frequency': 'f', 'id': 504, 'synset': 'glass.n.02', 'synonyms': ['glass_(drink_container)', 'drinking_glass'], 'def': 'a container for holding liquids while drinking', 'name': 'glass_(drink_container)'}, {'frequency': 'c', 'id': 505, 'synset': 'globe.n.03', 'synonyms': ['globe'], 'def': 'a sphere on which a map (especially of the earth) is represented', 'name': 'globe'}, {'frequency': 'f', 'id': 506, 'synset': 'glove.n.02', 'synonyms': ['glove'], 'def': 'handwear covering the hand', 'name': 'glove'}, {'frequency': 'c', 'id': 507, 'synset': 'goat.n.01', 'synonyms': ['goat'], 'def': 'a common goat', 'name': 'goat'}, {'frequency': 'f', 'id': 508, 'synset': 'goggles.n.01', 'synonyms': ['goggles'], 'def': 'tight-fitting spectacles worn to protect the eyes', 'name': 'goggles'}, {'frequency': 'r', 'id': 509, 'synset': 'goldfish.n.01', 'synonyms': ['goldfish'], 'def': 'small golden or orange-red freshwater fishes used as pond or aquarium pets', 'name': 'goldfish'}, {'frequency': 'r', 'id': 510, 'synset': 'golf_club.n.02', 'synonyms': ['golf_club', 'golf-club'], 'def': 'golf equipment used by a golfer to hit a golf ball', 'name': 'golf_club'}, {'frequency': 'c', 'id': 511, 'synset': 'golfcart.n.01', 'synonyms': ['golfcart'], 'def': 'a small motor vehicle in which golfers can ride between shots', 'name': 'golfcart'}, {'frequency': 'r', 'id': 512, 'synset': 'gondola.n.02', 'synonyms': ['gondola_(boat)'], 'def': 'long narrow flat-bottomed boat propelled by sculling; traditionally used on canals of Venice', 'name': 'gondola_(boat)'}, {'frequency': 'c', 'id': 513, 'synset': 'goose.n.01', 'synonyms': ['goose'], 'def': 'loud, web-footed long-necked aquatic birds usually larger than ducks', 'name': 'goose'}, {'frequency': 'r', 'id': 514, 'synset': 'gorilla.n.01', 'synonyms': ['gorilla'], 'def': 'largest ape', 'name': 'gorilla'}, {'frequency': 'r', 'id': 515, 'synset': 'gourd.n.02', 'synonyms': ['gourd'], 'def': 'any of numerous inedible fruits with hard rinds', 'name': 'gourd'}, {'frequency': 'r', 'id': 516, 'synset': 'gown.n.04', 'synonyms': ['surgical_gown', 'scrubs_(surgical_clothing)'], 'def': 'protective garment worn by surgeons during operations', 'name': 'surgical_gown'}, {'frequency': 'f', 'id': 517, 'synset': 'grape.n.01', 'synonyms': ['grape'], 'def': 'any of various juicy fruit with green or purple skins; grow in clusters', 'name': 'grape'}, {'frequency': 'r', 'id': 518, 'synset': 'grasshopper.n.01', 'synonyms': ['grasshopper'], 'def': 'plant-eating insect with hind legs adapted for leaping', 'name': 'grasshopper'}, {'frequency': 'c', 'id': 519, 'synset': 'grater.n.01', 'synonyms': ['grater'], 'def': 'utensil with sharp perforations for shredding foods (as vegetables or cheese)', 'name': 'grater'}, {'frequency': 'c', 'id': 520, 'synset': 'gravestone.n.01', 'synonyms': ['gravestone', 'headstone', 'tombstone'], 'def': 'a stone that is used to mark a grave', 'name': 'gravestone'}, {'frequency': 'r', 'id': 521, 'synset': 'gravy_boat.n.01', 'synonyms': ['gravy_boat', 'gravy_holder'], 'def': 'a dish (often boat-shaped) for serving gravy or sauce', 'name': 'gravy_boat'}, {'frequency': 'c', 'id': 522, 'synset': 'green_bean.n.02', 'synonyms': ['green_bean'], 'def': 'a common bean plant cultivated for its slender green edible pods', 'name': 'green_bean'}, {'frequency': 'c', 'id': 523, 'synset': 'green_onion.n.01', 'synonyms': ['green_onion', 'spring_onion', 'scallion'], 'def': 'a young onion before the bulb has enlarged', 'name': 'green_onion'}, {'frequency': 'r', 'id': 524, 'synset': 'griddle.n.01', 'synonyms': ['griddle'], 'def': 'cooking utensil consisting of a flat heated surface on which food is cooked', 'name': 'griddle'}, {'frequency': 'r', 'id': 525, 'synset': 'grillroom.n.01', 'synonyms': ['grillroom', 'grill_(restaurant)'], 'def': 'a restaurant where food is cooked on a grill', 'name': 'grillroom'}, {'frequency': 'r', 'id': 526, 'synset': 'grinder.n.04', 'synonyms': ['grinder_(tool)'], 'def': 'a machine tool that polishes metal', 'name': 'grinder_(tool)'}, {'frequency': 'r', 'id': 527, 'synset': 'grits.n.01', 'synonyms': ['grits', 'hominy_grits'], 'def': 'coarsely ground corn boiled as a breakfast dish', 'name': 'grits'}, {'frequency': 'c', 'id': 528, 'synset': 'grizzly.n.01', 'synonyms': ['grizzly', 'grizzly_bear'], 'def': 'powerful brownish-yellow bear of the uplands of western North America', 'name': 'grizzly'}, {'frequency': 'c', 'id': 529, 'synset': 'grocery_bag.n.01', 'synonyms': ['grocery_bag'], 'def': "a sack for holding customer's groceries", 'name': 'grocery_bag'}, {'frequency': 'r', 'id': 530, 'synset': 'guacamole.n.01', 'synonyms': ['guacamole'], 'def': 'a dip made of mashed avocado mixed with chopped onions and other seasonings', 'name': 'guacamole'}, {'frequency': 'f', 'id': 531, 'synset': 'guitar.n.01', 'synonyms': ['guitar'], 'def': 'a stringed instrument usually having six strings; played by strumming or plucking', 'name': 'guitar'}, {'frequency': 'c', 'id': 532, 'synset': 'gull.n.02', 'synonyms': ['gull', 'seagull'], 'def': 'mostly white aquatic bird having long pointed wings and short legs', 'name': 'gull'}, {'frequency': 'c', 'id': 533, 'synset': 'gun.n.01', 'synonyms': ['gun'], 'def': 'a weapon that discharges a bullet at high velocity from a metal tube', 'name': 'gun'}, {'frequency': 'r', 'id': 534, 'synset': 'hair_spray.n.01', 'synonyms': ['hair_spray'], 'def': 'substance sprayed on the hair to hold it in place', 'name': 'hair_spray'}, {'frequency': 'c', 'id': 535, 'synset': 'hairbrush.n.01', 'synonyms': ['hairbrush'], 'def': "a brush used to groom a person's hair", 'name': 'hairbrush'}, {'frequency': 'c', 'id': 536, 'synset': 'hairnet.n.01', 'synonyms': ['hairnet'], 'def': 'a small net that someone wears over their hair to keep it in place', 'name': 'hairnet'}, {'frequency': 'c', 'id': 537, 'synset': 'hairpin.n.01', 'synonyms': ['hairpin'], 'def': "a double pronged pin used to hold women's hair in place", 'name': 'hairpin'}, {'frequency': 'f', 'id': 538, 'synset': 'ham.n.01', 'synonyms': ['ham', 'jambon', 'gammon'], 'def': 'meat cut from the thigh of a hog (usually smoked)', 'name': 'ham'}, {'frequency': 'c', 'id': 539, 'synset': 'hamburger.n.01', 'synonyms': ['hamburger', 'beefburger', 'burger'], 'def': 'a sandwich consisting of a patty of minced beef served on a bun', 'name': 'hamburger'}, {'frequency': 'c', 'id': 540, 'synset': 'hammer.n.02', 'synonyms': ['hammer'], 'def': 'a hand tool with a heavy head and a handle; used to deliver an impulsive force by striking', 'name': 'hammer'}, {'frequency': 'r', 'id': 541, 'synset': 'hammock.n.02', 'synonyms': ['hammock'], 'def': 'a hanging bed of canvas or rope netting (usually suspended between two trees)', 'name': 'hammock'}, {'frequency': 'r', 'id': 542, 'synset': 'hamper.n.02', 'synonyms': ['hamper'], 'def': 'a basket usually with a cover', 'name': 'hamper'}, {'frequency': 'r', 'id': 543, 'synset': 'hamster.n.01', 'synonyms': ['hamster'], 'def': 'short-tailed burrowing rodent with large cheek pouches', 'name': 'hamster'}, {'frequency': 'c', 'id': 544, 'synset': 'hand_blower.n.01', 'synonyms': ['hair_dryer'], 'def': 'a hand-held electric blower that can blow warm air onto the hair', 'name': 'hair_dryer'}, {'frequency': 'r', 'id': 545, 'synset': 'hand_glass.n.01', 'synonyms': ['hand_glass', 'hand_mirror'], 'def': 'a mirror intended to be held in the hand', 'name': 'hand_glass'}, {'frequency': 'f', 'id': 546, 'synset': 'hand_towel.n.01', 'synonyms': ['hand_towel', 'face_towel'], 'def': 'a small towel used to dry the hands or face', 'name': 'hand_towel'}, {'frequency': 'c', 'id': 547, 'synset': 'handcart.n.01', 'synonyms': ['handcart', 'pushcart', 'hand_truck'], 'def': 'wheeled vehicle that can be pushed by a person', 'name': 'handcart'}, {'frequency': 'r', 'id': 548, 'synset': 'handcuff.n.01', 'synonyms': ['handcuff'], 'def': 'shackle that consists of a metal loop that can be locked around the wrist', 'name': 'handcuff'}, {'frequency': 'c', 'id': 549, 'synset': 'handkerchief.n.01', 'synonyms': ['handkerchief'], 'def': 'a square piece of cloth used for wiping the eyes or nose or as a costume accessory', 'name': 'handkerchief'}, {'frequency': 'f', 'id': 550, 'synset': 'handle.n.01', 'synonyms': ['handle', 'grip', 'handgrip'], 'def': 'the appendage to an object that is designed to be held in order to use or move it', 'name': 'handle'}, {'frequency': 'r', 'id': 551, 'synset': 'handsaw.n.01', 'synonyms': ['handsaw', "carpenter's_saw"], 'def': 'a saw used with one hand for cutting wood', 'name': 'handsaw'}, {'frequency': 'r', 'id': 552, 'synset': 'hardback.n.01', 'synonyms': ['hardback_book', 'hardcover_book'], 'def': 'a book with cardboard or cloth or leather covers', 'name': 'hardback_book'}, {'frequency': 'r', 'id': 553, 'synset': 'harmonium.n.01', 'synonyms': ['harmonium', 'organ_(musical_instrument)', 'reed_organ_(musical_instrument)'], 'def': 'a free-reed instrument in which air is forced through the reeds by bellows', 'name': 'harmonium'}, {'frequency': 'f', 'id': 554, 'synset': 'hat.n.01', 'synonyms': ['hat'], 'def': 'headwear that protects the head from bad weather, sun, or worn for fashion', 'name': 'hat'}, {'frequency': 'r', 'id': 555, 'synset': 'hatbox.n.01', 'synonyms': ['hatbox'], 'def': 'a round piece of luggage for carrying hats', 'name': 'hatbox'}, {'frequency': 'r', 'id': 556, 'synset': 'hatch.n.03', 'synonyms': ['hatch'], 'def': 'a movable barrier covering a hatchway', 'name': 'hatch'}, {'frequency': 'c', 'id': 557, 'synset': 'head_covering.n.01', 'synonyms': ['veil'], 'def': 'a garment that covers the head and face', 'name': 'veil'}, {'frequency': 'f', 'id': 558, 'synset': 'headband.n.01', 'synonyms': ['headband'], 'def': 'a band worn around or over the head', 'name': 'headband'}, {'frequency': 'f', 'id': 559, 'synset': 'headboard.n.01', 'synonyms': ['headboard'], 'def': 'a vertical board or panel forming the head of a bedstead', 'name': 'headboard'}, {'frequency': 'f', 'id': 560, 'synset': 'headlight.n.01', 'synonyms': ['headlight', 'headlamp'], 'def': 'a powerful light with reflector; attached to the front of an automobile or locomotive', 'name': 'headlight'}, {'frequency': 'c', 'id': 561, 'synset': 'headscarf.n.01', 'synonyms': ['headscarf'], 'def': 'a kerchief worn over the head and tied under the chin', 'name': 'headscarf'}, {'frequency': 'r', 'id': 562, 'synset': 'headset.n.01', 'synonyms': ['headset'], 'def': 'receiver consisting of a pair of headphones', 'name': 'headset'}, {'frequency': 'c', 'id': 563, 'synset': 'headstall.n.01', 'synonyms': ['headstall_(for_horses)', 'headpiece_(for_horses)'], 'def': "the band that is the part of a bridle that fits around a horse's head", 'name': 'headstall_(for_horses)'}, {'frequency': 'r', 'id': 564, 'synset': 'hearing_aid.n.02', 'synonyms': ['hearing_aid'], 'def': 'an acoustic device used to direct sound to the ear of a hearing-impaired person', 'name': 'hearing_aid'}, {'frequency': 'c', 'id': 565, 'synset': 'heart.n.02', 'synonyms': ['heart'], 'def': 'a muscular organ; its contractions move the blood through the body', 'name': 'heart'}, {'frequency': 'c', 'id': 566, 'synset': 'heater.n.01', 'synonyms': ['heater', 'warmer'], 'def': 'device that heats water or supplies warmth to a room', 'name': 'heater'}, {'frequency': 'c', 'id': 567, 'synset': 'helicopter.n.01', 'synonyms': ['helicopter'], 'def': 'an aircraft without wings that obtains its lift from the rotation of overhead blades', 'name': 'helicopter'}, {'frequency': 'f', 'id': 568, 'synset': 'helmet.n.02', 'synonyms': ['helmet'], 'def': 'a protective headgear made of hard material to resist blows', 'name': 'helmet'}, {'frequency': 'r', 'id': 569, 'synset': 'heron.n.02', 'synonyms': ['heron'], 'def': 'grey or white wading bird with long neck and long legs and (usually) long bill', 'name': 'heron'}, {'frequency': 'c', 'id': 570, 'synset': 'highchair.n.01', 'synonyms': ['highchair', 'feeding_chair'], 'def': 'a chair for feeding a very young child', 'name': 'highchair'}, {'frequency': 'f', 'id': 571, 'synset': 'hinge.n.01', 'synonyms': ['hinge'], 'def': 'a joint that holds two parts together so that one can swing relative to the other', 'name': 'hinge'}, {'frequency': 'r', 'id': 572, 'synset': 'hippopotamus.n.01', 'synonyms': ['hippopotamus'], 'def': 'massive thick-skinned animal living in or around rivers of tropical Africa', 'name': 'hippopotamus'}, {'frequency': 'r', 'id': 573, 'synset': 'hockey_stick.n.01', 'synonyms': ['hockey_stick'], 'def': 'sports implement consisting of a stick used by hockey players to move the puck', 'name': 'hockey_stick'}, {'frequency': 'c', 'id': 574, 'synset': 'hog.n.03', 'synonyms': ['hog', 'pig'], 'def': 'domestic swine', 'name': 'hog'}, {'frequency': 'f', 'id': 575, 'synset': 'home_plate.n.01', 'synonyms': ['home_plate_(baseball)', 'home_base_(baseball)'], 'def': '(baseball) a rubber slab where the batter stands; it must be touched by a base runner in order to score', 'name': 'home_plate_(baseball)'}, {'frequency': 'c', 'id': 576, 'synset': 'honey.n.01', 'synonyms': ['honey'], 'def': 'a sweet yellow liquid produced by bees', 'name': 'honey'}, {'frequency': 'f', 'id': 577, 'synset': 'hood.n.06', 'synonyms': ['fume_hood', 'exhaust_hood'], 'def': 'metal covering leading to a vent that exhausts smoke or fumes', 'name': 'fume_hood'}, {'frequency': 'f', 'id': 578, 'synset': 'hook.n.05', 'synonyms': ['hook'], 'def': 'a curved or bent implement for suspending or pulling something', 'name': 'hook'}, {'frequency': 'f', 'id': 579, 'synset': 'horse.n.01', 'synonyms': ['horse'], 'def': 'a common horse', 'name': 'horse'}, {'frequency': 'f', 'id': 580, 'synset': 'hose.n.03', 'synonyms': ['hose', 'hosepipe'], 'def': 'a flexible pipe for conveying a liquid or gas', 'name': 'hose'}, {'frequency': 'r', 'id': 581, 'synset': 'hot-air_balloon.n.01', 'synonyms': ['hot-air_balloon'], 'def': 'balloon for travel through the air in a basket suspended below a large bag of heated air', 'name': 'hot-air_balloon'}, {'frequency': 'r', 'id': 582, 'synset': 'hot_plate.n.01', 'synonyms': ['hotplate'], 'def': 'a portable electric appliance for heating or cooking or keeping food warm', 'name': 'hotplate'}, {'frequency': 'c', 'id': 583, 'synset': 'hot_sauce.n.01', 'synonyms': ['hot_sauce'], 'def': 'a pungent peppery sauce', 'name': 'hot_sauce'}, {'frequency': 'r', 'id': 584, 'synset': 'hourglass.n.01', 'synonyms': ['hourglass'], 'def': 'a sandglass timer that runs for sixty minutes', 'name': 'hourglass'}, {'frequency': 'r', 'id': 585, 'synset': 'houseboat.n.01', 'synonyms': ['houseboat'], 'def': 'a barge that is designed and equipped for use as a dwelling', 'name': 'houseboat'}, {'frequency': 'r', 'id': 586, 'synset': 'hummingbird.n.01', 'synonyms': ['hummingbird'], 'def': 'tiny American bird having brilliant iridescent plumage and long slender bills', 'name': 'hummingbird'}, {'frequency': 'r', 'id': 587, 'synset': 'hummus.n.01', 'synonyms': ['hummus', 'humus', 'hommos', 'hoummos', 'humous'], 'def': 'a thick spread made from mashed chickpeas', 'name': 'hummus'}, {'frequency': 'c', 'id': 588, 'synset': 'ice_bear.n.01', 'synonyms': ['polar_bear'], 'def': 'white bear of Arctic regions', 'name': 'polar_bear'}, {'frequency': 'c', 'id': 589, 'synset': 'ice_cream.n.01', 'synonyms': ['icecream'], 'def': 'frozen dessert containing cream and sugar and flavoring', 'name': 'icecream'}, {'frequency': 'r', 'id': 590, 'synset': 'ice_lolly.n.01', 'synonyms': ['popsicle'], 'def': 'ice cream or water ice on a small wooden stick', 'name': 'popsicle'}, {'frequency': 'c', 'id': 591, 'synset': 'ice_maker.n.01', 'synonyms': ['ice_maker'], 'def': 'an appliance included in some electric refrigerators for making ice cubes', 'name': 'ice_maker'}, {'frequency': 'r', 'id': 592, 'synset': 'ice_pack.n.01', 'synonyms': ['ice_pack', 'ice_bag'], 'def': 'a waterproof bag filled with ice: applied to the body (especially the head) to cool or reduce swelling', 'name': 'ice_pack'}, {'frequency': 'r', 'id': 593, 'synset': 'ice_skate.n.01', 'synonyms': ['ice_skate'], 'def': 'skate consisting of a boot with a steel blade fitted to the sole', 'name': 'ice_skate'}, {'frequency': 'r', 'id': 594, 'synset': 'ice_tea.n.01', 'synonyms': ['ice_tea', 'iced_tea'], 'def': 'strong tea served over ice', 'name': 'ice_tea'}, {'frequency': 'c', 'id': 595, 'synset': 'igniter.n.01', 'synonyms': ['igniter', 'ignitor', 'lighter'], 'def': 'a substance or device used to start a fire', 'name': 'igniter'}, {'frequency': 'r', 'id': 596, 'synset': 'incense.n.01', 'synonyms': ['incense'], 'def': 'a substance that produces a fragrant odor when burned', 'name': 'incense'}, {'frequency': 'r', 'id': 597, 'synset': 'inhaler.n.01', 'synonyms': ['inhaler', 'inhalator'], 'def': 'a dispenser that produces a chemical vapor to be inhaled through mouth or nose', 'name': 'inhaler'}, {'frequency': 'c', 'id': 598, 'synset': 'ipod.n.01', 'synonyms': ['iPod'], 'def': 'a pocket-sized device used to play music files', 'name': 'iPod'}, {'frequency': 'c', 'id': 599, 'synset': 'iron.n.04', 'synonyms': ['iron_(for_clothing)', 'smoothing_iron_(for_clothing)'], 'def': 'home appliance consisting of a flat metal base that is heated and used to smooth cloth', 'name': 'iron_(for_clothing)'}, {'frequency': 'r', 'id': 600, 'synset': 'ironing_board.n.01', 'synonyms': ['ironing_board'], 'def': 'narrow padded board on collapsible supports; used for ironing clothes', 'name': 'ironing_board'}, {'frequency': 'f', 'id': 601, 'synset': 'jacket.n.01', 'synonyms': ['jacket'], 'def': 'a waist-length coat', 'name': 'jacket'}, {'frequency': 'r', 'id': 602, 'synset': 'jam.n.01', 'synonyms': ['jam'], 'def': 'preserve of crushed fruit', 'name': 'jam'}, {'frequency': 'f', 'id': 603, 'synset': 'jean.n.01', 'synonyms': ['jean', 'blue_jean', 'denim'], 'def': '(usually plural) close-fitting trousers of heavy denim for manual work or casual wear', 'name': 'jean'}, {'frequency': 'c', 'id': 604, 'synset': 'jeep.n.01', 'synonyms': ['jeep', 'landrover'], 'def': 'a car suitable for traveling over rough terrain', 'name': 'jeep'}, {'frequency': 'r', 'id': 605, 'synset': 'jelly_bean.n.01', 'synonyms': ['jelly_bean', 'jelly_egg'], 'def': 'sugar-glazed jellied candy', 'name': 'jelly_bean'}, {'frequency': 'f', 'id': 606, 'synset': 'jersey.n.03', 'synonyms': ['jersey', 'T-shirt', 'tee_shirt'], 'def': 'a close-fitting pullover shirt', 'name': 'jersey'}, {'frequency': 'c', 'id': 607, 'synset': 'jet.n.01', 'synonyms': ['jet_plane', 'jet-propelled_plane'], 'def': 'an airplane powered by one or more jet engines', 'name': 'jet_plane'}, {'frequency': 'c', 'id': 608, 'synset': 'jewelry.n.01', 'synonyms': ['jewelry', 'jewellery'], 'def': 'an adornment (as a bracelet or ring or necklace) made of precious metals and set with gems (or imitation gems)', 'name': 'jewelry'}, {'frequency': 'r', 'id': 609, 'synset': 'joystick.n.02', 'synonyms': ['joystick'], 'def': 'a control device for computers consisting of a vertical handle that can move freely in two directions', 'name': 'joystick'}, {'frequency': 'r', 'id': 610, 'synset': 'jump_suit.n.01', 'synonyms': ['jumpsuit'], 'def': "one-piece garment fashioned after a parachutist's uniform", 'name': 'jumpsuit'}, {'frequency': 'c', 'id': 611, 'synset': 'kayak.n.01', 'synonyms': ['kayak'], 'def': 'a small canoe consisting of a light frame made watertight with animal skins', 'name': 'kayak'}, {'frequency': 'r', 'id': 612, 'synset': 'keg.n.02', 'synonyms': ['keg'], 'def': 'small cask or barrel', 'name': 'keg'}, {'frequency': 'r', 'id': 613, 'synset': 'kennel.n.01', 'synonyms': ['kennel', 'doghouse'], 'def': 'outbuilding that serves as a shelter for a dog', 'name': 'kennel'}, {'frequency': 'c', 'id': 614, 'synset': 'kettle.n.01', 'synonyms': ['kettle', 'boiler'], 'def': 'a metal pot for stewing or boiling; usually has a lid', 'name': 'kettle'}, {'frequency': 'f', 'id': 615, 'synset': 'key.n.01', 'synonyms': ['key'], 'def': 'metal instrument used to unlock a lock', 'name': 'key'}, {'frequency': 'r', 'id': 616, 'synset': 'keycard.n.01', 'synonyms': ['keycard'], 'def': 'a plastic card used to gain access typically to a door', 'name': 'keycard'}, {'frequency': 'r', 'id': 617, 'synset': 'kilt.n.01', 'synonyms': ['kilt'], 'def': 'a knee-length pleated tartan skirt worn by men as part of the traditional dress in the Highlands of northern Scotland', 'name': 'kilt'}, {'frequency': 'c', 'id': 618, 'synset': 'kimono.n.01', 'synonyms': ['kimono'], 'def': 'a loose robe; imitated from robes originally worn by Japanese', 'name': 'kimono'}, {'frequency': 'f', 'id': 619, 'synset': 'kitchen_sink.n.01', 'synonyms': ['kitchen_sink'], 'def': 'a sink in a kitchen', 'name': 'kitchen_sink'}, {'frequency': 'c', 'id': 620, 'synset': 'kitchen_table.n.01', 'synonyms': ['kitchen_table'], 'def': 'a table in the kitchen', 'name': 'kitchen_table'}, {'frequency': 'f', 'id': 621, 'synset': 'kite.n.03', 'synonyms': ['kite'], 'def': 'plaything consisting of a light frame covered with tissue paper; flown in wind at end of a string', 'name': 'kite'}, {'frequency': 'c', 'id': 622, 'synset': 'kitten.n.01', 'synonyms': ['kitten', 'kitty'], 'def': 'young domestic cat', 'name': 'kitten'}, {'frequency': 'c', 'id': 623, 'synset': 'kiwi.n.03', 'synonyms': ['kiwi_fruit'], 'def': 'fuzzy brown egg-shaped fruit with slightly tart green flesh', 'name': 'kiwi_fruit'}, {'frequency': 'f', 'id': 624, 'synset': 'knee_pad.n.01', 'synonyms': ['knee_pad'], 'def': 'protective garment consisting of a pad worn by football or baseball or hockey players', 'name': 'knee_pad'}, {'frequency': 'f', 'id': 625, 'synset': 'knife.n.01', 'synonyms': ['knife'], 'def': 'tool with a blade and point used as a cutting instrument', 'name': 'knife'}, {'frequency': 'r', 'id': 626, 'synset': 'knight.n.02', 'synonyms': ['knight_(chess_piece)', 'horse_(chess_piece)'], 'def': 'a chess game piece shaped to resemble the head of a horse', 'name': 'knight_(chess_piece)'}, {'frequency': 'r', 'id': 627, 'synset': 'knitting_needle.n.01', 'synonyms': ['knitting_needle'], 'def': 'needle consisting of a slender rod with pointed ends; usually used in pairs', 'name': 'knitting_needle'}, {'frequency': 'f', 'id': 628, 'synset': 'knob.n.02', 'synonyms': ['knob'], 'def': 'a round handle often found on a door', 'name': 'knob'}, {'frequency': 'r', 'id': 629, 'synset': 'knocker.n.05', 'synonyms': ['knocker_(on_a_door)', 'doorknocker'], 'def': 'a device (usually metal and ornamental) attached by a hinge to a door', 'name': 'knocker_(on_a_door)'}, {'frequency': 'r', 'id': 630, 'synset': 'koala.n.01', 'synonyms': ['koala', 'koala_bear'], 'def': 'sluggish tailless Australian marsupial with grey furry ears and coat', 'name': 'koala'}, {'frequency': 'r', 'id': 631, 'synset': 'lab_coat.n.01', 'synonyms': ['lab_coat', 'laboratory_coat'], 'def': 'a light coat worn to protect clothing from substances used while working in a laboratory', 'name': 'lab_coat'}, {'frequency': 'f', 'id': 632, 'synset': 'ladder.n.01', 'synonyms': ['ladder'], 'def': 'steps consisting of two parallel members connected by rungs', 'name': 'ladder'}, {'frequency': 'c', 'id': 633, 'synset': 'ladle.n.01', 'synonyms': ['ladle'], 'def': 'a spoon-shaped vessel with a long handle frequently used to transfer liquids', 'name': 'ladle'}, {'frequency': 'r', 'id': 634, 'synset': 'ladybug.n.01', 'synonyms': ['ladybug', 'ladybeetle', 'ladybird_beetle'], 'def': 'small round bright-colored and spotted beetle, typically red and black', 'name': 'ladybug'}, {'frequency': 'c', 'id': 635, 'synset': 'lamb.n.01', 'synonyms': ['lamb_(animal)'], 'def': 'young sheep', 'name': 'lamb_(animal)'}, {'frequency': 'r', 'id': 636, 'synset': 'lamb_chop.n.01', 'synonyms': ['lamb-chop', 'lambchop'], 'def': 'chop cut from a lamb', 'name': 'lamb-chop'}, {'frequency': 'f', 'id': 637, 'synset': 'lamp.n.02', 'synonyms': ['lamp'], 'def': 'a piece of furniture holding one or more electric light bulbs', 'name': 'lamp'}, {'frequency': 'f', 'id': 638, 'synset': 'lamppost.n.01', 'synonyms': ['lamppost'], 'def': 'a metal post supporting an outdoor lamp (such as a streetlight)', 'name': 'lamppost'}, {'frequency': 'f', 'id': 639, 'synset': 'lampshade.n.01', 'synonyms': ['lampshade'], 'def': 'a protective ornamental shade used to screen a light bulb from direct view', 'name': 'lampshade'}, {'frequency': 'c', 'id': 640, 'synset': 'lantern.n.01', 'synonyms': ['lantern'], 'def': 'light in a transparent protective case', 'name': 'lantern'}, {'frequency': 'f', 'id': 641, 'synset': 'lanyard.n.02', 'synonyms': ['lanyard', 'laniard'], 'def': 'a cord worn around the neck to hold a knife or whistle, etc.', 'name': 'lanyard'}, {'frequency': 'f', 'id': 642, 'synset': 'laptop.n.01', 'synonyms': ['laptop_computer', 'notebook_computer'], 'def': 'a portable computer small enough to use in your lap', 'name': 'laptop_computer'}, {'frequency': 'r', 'id': 643, 'synset': 'lasagna.n.01', 'synonyms': ['lasagna', 'lasagne'], 'def': 'baked dish of layers of lasagna pasta with sauce and cheese and meat or vegetables', 'name': 'lasagna'}, {'frequency': 'c', 'id': 644, 'synset': 'latch.n.02', 'synonyms': ['latch'], 'def': 'a bar that can be lowered or slid into a groove to fasten a door or gate', 'name': 'latch'}, {'frequency': 'r', 'id': 645, 'synset': 'lawn_mower.n.01', 'synonyms': ['lawn_mower'], 'def': 'garden tool for mowing grass on lawns', 'name': 'lawn_mower'}, {'frequency': 'r', 'id': 646, 'synset': 'leather.n.01', 'synonyms': ['leather'], 'def': 'an animal skin made smooth and flexible by removing the hair and then tanning', 'name': 'leather'}, {'frequency': 'c', 'id': 647, 'synset': 'legging.n.01', 'synonyms': ['legging_(clothing)', 'leging_(clothing)', 'leg_covering'], 'def': 'a garment covering the leg (usually extending from the knee to the ankle)', 'name': 'legging_(clothing)'}, {'frequency': 'c', 'id': 648, 'synset': 'lego.n.01', 'synonyms': ['Lego', 'Lego_set'], 'def': "a child's plastic construction set for making models from blocks", 'name': 'Lego'}, {'frequency': 'f', 'id': 649, 'synset': 'lemon.n.01', 'synonyms': ['lemon'], 'def': 'yellow oval fruit with juicy acidic flesh', 'name': 'lemon'}, {'frequency': 'r', 'id': 650, 'synset': 'lemonade.n.01', 'synonyms': ['lemonade'], 'def': 'sweetened beverage of diluted lemon juice', 'name': 'lemonade'}, {'frequency': 'f', 'id': 651, 'synset': 'lettuce.n.02', 'synonyms': ['lettuce'], 'def': 'leafy plant commonly eaten in salad or on sandwiches', 'name': 'lettuce'}, {'frequency': 'f', 'id': 652, 'synset': 'license_plate.n.01', 'synonyms': ['license_plate', 'numberplate'], 'def': "a plate mounted on the front and back of car and bearing the car's registration number", 'name': 'license_plate'}, {'frequency': 'f', 'id': 653, 'synset': 'life_buoy.n.01', 'synonyms': ['life_buoy', 'lifesaver', 'life_belt', 'life_ring'], 'def': 'a ring-shaped life preserver used to prevent drowning (NOT a life-jacket or vest)', 'name': 'life_buoy'}, {'frequency': 'f', 'id': 654, 'synset': 'life_jacket.n.01', 'synonyms': ['life_jacket', 'life_vest'], 'def': 'life preserver consisting of a sleeveless jacket of buoyant or inflatable design', 'name': 'life_jacket'}, {'frequency': 'f', 'id': 655, 'synset': 'light_bulb.n.01', 'synonyms': ['lightbulb'], 'def': 'glass bulb or tube shaped electric device that emits light (DO NOT MARK LAMPS AS A WHOLE)', 'name': 'lightbulb'}, {'frequency': 'r', 'id': 656, 'synset': 'lightning_rod.n.02', 'synonyms': ['lightning_rod', 'lightning_conductor'], 'def': 'a metallic conductor that is attached to a high point and leads to the ground', 'name': 'lightning_rod'}, {'frequency': 'c', 'id': 657, 'synset': 'lime.n.06', 'synonyms': ['lime'], 'def': 'the green acidic fruit of any of various lime trees', 'name': 'lime'}, {'frequency': 'r', 'id': 658, 'synset': 'limousine.n.01', 'synonyms': ['limousine'], 'def': 'long luxurious car; usually driven by a chauffeur', 'name': 'limousine'}, {'frequency': 'r', 'id': 659, 'synset': 'linen.n.02', 'synonyms': ['linen_paper'], 'def': 'a high-quality paper made of linen fibers or with a linen finish', 'name': 'linen_paper'}, {'frequency': 'c', 'id': 660, 'synset': 'lion.n.01', 'synonyms': ['lion'], 'def': 'large gregarious predatory cat of Africa and India', 'name': 'lion'}, {'frequency': 'c', 'id': 661, 'synset': 'lip_balm.n.01', 'synonyms': ['lip_balm'], 'def': 'a balm applied to the lips', 'name': 'lip_balm'}, {'frequency': 'c', 'id': 662, 'synset': 'lipstick.n.01', 'synonyms': ['lipstick', 'lip_rouge'], 'def': 'makeup that is used to color the lips', 'name': 'lipstick'}, {'frequency': 'r', 'id': 663, 'synset': 'liquor.n.01', 'synonyms': ['liquor', 'spirits', 'hard_liquor', 'liqueur', 'cordial'], 'def': 'an alcoholic beverage that is distilled rather than fermented', 'name': 'liquor'}, {'frequency': 'r', 'id': 664, 'synset': 'lizard.n.01', 'synonyms': ['lizard'], 'def': 'a reptile with usually two pairs of legs and a tapering tail', 'name': 'lizard'}, {'frequency': 'r', 'id': 665, 'synset': 'loafer.n.02', 'synonyms': ['Loafer_(type_of_shoe)'], 'def': 'a low leather step-in shoe', 'name': 'Loafer_(type_of_shoe)'}, {'frequency': 'f', 'id': 666, 'synset': 'log.n.01', 'synonyms': ['log'], 'def': 'a segment of the trunk of a tree when stripped of branches', 'name': 'log'}, {'frequency': 'c', 'id': 667, 'synset': 'lollipop.n.02', 'synonyms': ['lollipop'], 'def': 'hard candy on a stick', 'name': 'lollipop'}, {'frequency': 'c', 'id': 668, 'synset': 'lotion.n.01', 'synonyms': ['lotion'], 'def': 'any of various cosmetic preparations that are applied to the skin', 'name': 'lotion'}, {'frequency': 'f', 'id': 669, 'synset': 'loudspeaker.n.01', 'synonyms': ['speaker_(stero_equipment)'], 'def': 'electronic device that produces sound often as part of a stereo system', 'name': 'speaker_(stero_equipment)'}, {'frequency': 'c', 'id': 670, 'synset': 'love_seat.n.01', 'synonyms': ['loveseat'], 'def': 'small sofa that seats two people', 'name': 'loveseat'}, {'frequency': 'r', 'id': 671, 'synset': 'machine_gun.n.01', 'synonyms': ['machine_gun'], 'def': 'a rapidly firing automatic gun', 'name': 'machine_gun'}, {'frequency': 'f', 'id': 672, 'synset': 'magazine.n.02', 'synonyms': ['magazine'], 'def': 'a paperback periodic publication', 'name': 'magazine'}, {'frequency': 'f', 'id': 673, 'synset': 'magnet.n.01', 'synonyms': ['magnet'], 'def': 'a device that attracts iron and produces a magnetic field', 'name': 'magnet'}, {'frequency': 'r', 'id': 674, 'synset': 'mail_slot.n.01', 'synonyms': ['mail_slot'], 'def': 'a slot (usually in a door) through which mail can be delivered', 'name': 'mail_slot'}, {'frequency': 'c', 'id': 675, 'synset': 'mailbox.n.01', 'synonyms': ['mailbox_(at_home)', 'letter_box_(at_home)'], 'def': 'a private box for delivery of mail', 'name': 'mailbox_(at_home)'}, {'frequency': 'r', 'id': 676, 'synset': 'mallet.n.01', 'synonyms': ['mallet'], 'def': 'a sports implement with a long handle and a hammer-like head used to hit a ball', 'name': 'mallet'}, {'frequency': 'r', 'id': 677, 'synset': 'mammoth.n.01', 'synonyms': ['mammoth'], 'def': 'any of numerous extinct elephants widely distributed in the Pleistocene', 'name': 'mammoth'}, {'frequency': 'c', 'id': 678, 'synset': 'mandarin.n.05', 'synonyms': ['mandarin_orange'], 'def': 'a somewhat flat reddish-orange loose skinned citrus of China', 'name': 'mandarin_orange'}, {'frequency': 'c', 'id': 679, 'synset': 'manger.n.01', 'synonyms': ['manger', 'trough'], 'def': 'a container (usually in a barn or stable) from which cattle or horses feed', 'name': 'manger'}, {'frequency': 'f', 'id': 680, 'synset': 'manhole.n.01', 'synonyms': ['manhole'], 'def': 'a hole (usually with a flush cover) through which a person can gain access to an underground structure', 'name': 'manhole'}, {'frequency': 'c', 'id': 681, 'synset': 'map.n.01', 'synonyms': ['map'], 'def': "a diagrammatic representation of the earth's surface (or part of it)", 'name': 'map'}, {'frequency': 'c', 'id': 682, 'synset': 'marker.n.03', 'synonyms': ['marker'], 'def': 'a writing implement for making a mark', 'name': 'marker'}, {'frequency': 'r', 'id': 683, 'synset': 'martini.n.01', 'synonyms': ['martini'], 'def': 'a cocktail made of gin (or vodka) with dry vermouth', 'name': 'martini'}, {'frequency': 'r', 'id': 684, 'synset': 'mascot.n.01', 'synonyms': ['mascot'], 'def': 'a person or animal that is adopted by a team or other group as a symbolic figure', 'name': 'mascot'}, {'frequency': 'c', 'id': 685, 'synset': 'mashed_potato.n.01', 'synonyms': ['mashed_potato'], 'def': 'potato that has been peeled and boiled and then mashed', 'name': 'mashed_potato'}, {'frequency': 'r', 'id': 686, 'synset': 'masher.n.02', 'synonyms': ['masher'], 'def': 'a kitchen utensil used for mashing (e.g. potatoes)', 'name': 'masher'}, {'frequency': 'f', 'id': 687, 'synset': 'mask.n.04', 'synonyms': ['mask', 'facemask'], 'def': 'a protective covering worn over the face', 'name': 'mask'}, {'frequency': 'f', 'id': 688, 'synset': 'mast.n.01', 'synonyms': ['mast'], 'def': 'a vertical spar for supporting sails', 'name': 'mast'}, {'frequency': 'c', 'id': 689, 'synset': 'mat.n.03', 'synonyms': ['mat_(gym_equipment)', 'gym_mat'], 'def': 'sports equipment consisting of a piece of thick padding on the floor for gymnastics', 'name': 'mat_(gym_equipment)'}, {'frequency': 'r', 'id': 690, 'synset': 'matchbox.n.01', 'synonyms': ['matchbox'], 'def': 'a box for holding matches', 'name': 'matchbox'}, {'frequency': 'f', 'id': 691, 'synset': 'mattress.n.01', 'synonyms': ['mattress'], 'def': 'a thick pad filled with resilient material used as a bed or part of a bed', 'name': 'mattress'}, {'frequency': 'c', 'id': 692, 'synset': 'measuring_cup.n.01', 'synonyms': ['measuring_cup'], 'def': 'graduated cup used to measure liquid or granular ingredients', 'name': 'measuring_cup'}, {'frequency': 'c', 'id': 693, 'synset': 'measuring_stick.n.01', 'synonyms': ['measuring_stick', 'ruler_(measuring_stick)', 'measuring_rod'], 'def': 'measuring instrument having a sequence of marks at regular intervals', 'name': 'measuring_stick'}, {'frequency': 'c', 'id': 694, 'synset': 'meatball.n.01', 'synonyms': ['meatball'], 'def': 'ground meat formed into a ball and fried or simmered in broth', 'name': 'meatball'}, {'frequency': 'c', 'id': 695, 'synset': 'medicine.n.02', 'synonyms': ['medicine'], 'def': 'something that treats or prevents or alleviates the symptoms of disease', 'name': 'medicine'}, {'frequency': 'r', 'id': 696, 'synset': 'melon.n.01', 'synonyms': ['melon'], 'def': 'fruit of the gourd family having a hard rind and sweet juicy flesh', 'name': 'melon'}, {'frequency': 'f', 'id': 697, 'synset': 'microphone.n.01', 'synonyms': ['microphone'], 'def': 'device for converting sound waves into electrical energy', 'name': 'microphone'}, {'frequency': 'r', 'id': 698, 'synset': 'microscope.n.01', 'synonyms': ['microscope'], 'def': 'magnifier of the image of small objects', 'name': 'microscope'}, {'frequency': 'f', 'id': 699, 'synset': 'microwave.n.02', 'synonyms': ['microwave_oven'], 'def': 'kitchen appliance that cooks food by passing an electromagnetic wave through it', 'name': 'microwave_oven'}, {'frequency': 'r', 'id': 700, 'synset': 'milestone.n.01', 'synonyms': ['milestone', 'milepost'], 'def': 'stone post at side of a road to show distances', 'name': 'milestone'}, {'frequency': 'c', 'id': 701, 'synset': 'milk.n.01', 'synonyms': ['milk'], 'def': 'a white nutritious liquid secreted by mammals and used as food by human beings', 'name': 'milk'}, {'frequency': 'f', 'id': 702, 'synset': 'minivan.n.01', 'synonyms': ['minivan'], 'def': 'a small box-shaped passenger van', 'name': 'minivan'}, {'frequency': 'r', 'id': 703, 'synset': 'mint.n.05', 'synonyms': ['mint_candy'], 'def': 'a candy that is flavored with a mint oil', 'name': 'mint_candy'}, {'frequency': 'f', 'id': 704, 'synset': 'mirror.n.01', 'synonyms': ['mirror'], 'def': 'polished surface that forms images by reflecting light', 'name': 'mirror'}, {'frequency': 'c', 'id': 705, 'synset': 'mitten.n.01', 'synonyms': ['mitten'], 'def': 'glove that encases the thumb separately and the other four fingers together', 'name': 'mitten'}, {'frequency': 'c', 'id': 706, 'synset': 'mixer.n.04', 'synonyms': ['mixer_(kitchen_tool)', 'stand_mixer'], 'def': 'a kitchen utensil that is used for mixing foods', 'name': 'mixer_(kitchen_tool)'}, {'frequency': 'c', 'id': 707, 'synset': 'money.n.03', 'synonyms': ['money'], 'def': 'the official currency issued by a government or national bank', 'name': 'money'}, {'frequency': 'f', 'id': 708, 'synset': 'monitor.n.04', 'synonyms': ['monitor_(computer_equipment) computer_monitor'], 'def': 'a computer monitor', 'name': 'monitor_(computer_equipment) computer_monitor'}, {'frequency': 'c', 'id': 709, 'synset': 'monkey.n.01', 'synonyms': ['monkey'], 'def': 'any of various long-tailed primates', 'name': 'monkey'}, {'frequency': 'f', 'id': 710, 'synset': 'motor.n.01', 'synonyms': ['motor'], 'def': 'machine that converts other forms of energy into mechanical energy and so imparts motion', 'name': 'motor'}, {'frequency': 'f', 'id': 711, 'synset': 'motor_scooter.n.01', 'synonyms': ['motor_scooter', 'scooter'], 'def': 'a wheeled vehicle with small wheels and a low-powered engine', 'name': 'motor_scooter'}, {'frequency': 'r', 'id': 712, 'synset': 'motor_vehicle.n.01', 'synonyms': ['motor_vehicle', 'automotive_vehicle'], 'def': 'a self-propelled wheeled vehicle that does not run on rails', 'name': 'motor_vehicle'}, {'frequency': 'r', 'id': 713, 'synset': 'motorboat.n.01', 'synonyms': ['motorboat', 'powerboat'], 'def': 'a boat propelled by an internal-combustion engine', 'name': 'motorboat'}, {'frequency': 'f', 'id': 714, 'synset': 'motorcycle.n.01', 'synonyms': ['motorcycle'], 'def': 'a motor vehicle with two wheels and a strong frame', 'name': 'motorcycle'}, {'frequency': 'f', 'id': 715, 'synset': 'mound.n.01', 'synonyms': ['mound_(baseball)', "pitcher's_mound"], 'def': '(baseball) the slight elevation on which the pitcher stands', 'name': 'mound_(baseball)'}, {'frequency': 'r', 'id': 716, 'synset': 'mouse.n.01', 'synonyms': ['mouse_(animal_rodent)'], 'def': 'a small rodent with pointed snouts and small ears on elongated bodies with slender usually hairless tails', 'name': 'mouse_(animal_rodent)'}, {'frequency': 'f', 'id': 717, 'synset': 'mouse.n.04', 'synonyms': ['mouse_(computer_equipment)', 'computer_mouse'], 'def': 'a computer input device that controls an on-screen pointer', 'name': 'mouse_(computer_equipment)'}, {'frequency': 'f', 'id': 718, 'synset': 'mousepad.n.01', 'synonyms': ['mousepad'], 'def': 'a small portable pad that provides an operating surface for a computer mouse', 'name': 'mousepad'}, {'frequency': 'c', 'id': 719, 'synset': 'muffin.n.01', 'synonyms': ['muffin'], 'def': 'a sweet quick bread baked in a cup-shaped pan', 'name': 'muffin'}, {'frequency': 'f', 'id': 720, 'synset': 'mug.n.04', 'synonyms': ['mug'], 'def': 'with handle and usually cylindrical', 'name': 'mug'}, {'frequency': 'f', 'id': 721, 'synset': 'mushroom.n.02', 'synonyms': ['mushroom'], 'def': 'a common mushroom', 'name': 'mushroom'}, {'frequency': 'r', 'id': 722, 'synset': 'music_stool.n.01', 'synonyms': ['music_stool', 'piano_stool'], 'def': 'a stool for piano players; usually adjustable in height', 'name': 'music_stool'}, {'frequency': 'r', 'id': 723, 'synset': 'musical_instrument.n.01', 'synonyms': ['musical_instrument', 'instrument_(musical)'], 'def': 'any of various devices or contrivances that can be used to produce musical tones or sounds', 'name': 'musical_instrument'}, {'frequency': 'r', 'id': 724, 'synset': 'nailfile.n.01', 'synonyms': ['nailfile'], 'def': 'a small flat file for shaping the nails', 'name': 'nailfile'}, {'frequency': 'r', 'id': 725, 'synset': 'nameplate.n.01', 'synonyms': ['nameplate'], 'def': 'a plate bearing a name', 'name': 'nameplate'}, {'frequency': 'f', 'id': 726, 'synset': 'napkin.n.01', 'synonyms': ['napkin', 'table_napkin', 'serviette'], 'def': 'a small piece of table linen or paper that is used to wipe the mouth and to cover the lap in order to protect clothing', 'name': 'napkin'}, {'frequency': 'r', 'id': 727, 'synset': 'neckerchief.n.01', 'synonyms': ['neckerchief'], 'def': 'a kerchief worn around the neck', 'name': 'neckerchief'}, {'frequency': 'f', 'id': 728, 'synset': 'necklace.n.01', 'synonyms': ['necklace'], 'def': 'jewelry consisting of a cord or chain (often bearing gems) worn about the neck as an ornament', 'name': 'necklace'}, {'frequency': 'f', 'id': 729, 'synset': 'necktie.n.01', 'synonyms': ['necktie', 'tie_(necktie)'], 'def': 'neckwear consisting of a long narrow piece of material worn under a collar and tied in knot at the front', 'name': 'necktie'}, {'frequency': 'r', 'id': 730, 'synset': 'needle.n.03', 'synonyms': ['needle'], 'def': 'a sharp pointed implement (usually metal)', 'name': 'needle'}, {'frequency': 'c', 'id': 731, 'synset': 'nest.n.01', 'synonyms': ['nest'], 'def': 'a structure in which animals lay eggs or give birth to their young', 'name': 'nest'}, {'frequency': 'r', 'id': 732, 'synset': 'newsstand.n.01', 'synonyms': ['newsstand'], 'def': 'a stall where newspapers and other periodicals are sold', 'name': 'newsstand'}, {'frequency': 'c', 'id': 733, 'synset': 'nightwear.n.01', 'synonyms': ['nightshirt', 'nightwear', 'sleepwear', 'nightclothes'], 'def': 'garments designed to be worn in bed', 'name': 'nightshirt'}, {'frequency': 'r', 'id': 734, 'synset': 'nosebag.n.01', 'synonyms': ['nosebag_(for_animals)', 'feedbag'], 'def': 'a canvas bag that is used to feed an animal (such as a horse); covers the muzzle and fastens at the top of the head', 'name': 'nosebag_(for_animals)'}, {'frequency': 'r', 'id': 735, 'synset': 'noseband.n.01', 'synonyms': ['noseband_(for_animals)', 'nosepiece_(for_animals)'], 'def': "a strap that is the part of a bridle that goes over the animal's nose", 'name': 'noseband_(for_animals)'}, {'frequency': 'f', 'id': 736, 'synset': 'notebook.n.01', 'synonyms': ['notebook'], 'def': 'a book with blank pages for recording notes or memoranda', 'name': 'notebook'}, {'frequency': 'c', 'id': 737, 'synset': 'notepad.n.01', 'synonyms': ['notepad'], 'def': 'a pad of paper for keeping notes', 'name': 'notepad'}, {'frequency': 'c', 'id': 738, 'synset': 'nut.n.03', 'synonyms': ['nut'], 'def': 'a small metal block (usually square or hexagonal) with internal screw thread to be fitted onto a bolt', 'name': 'nut'}, {'frequency': 'r', 'id': 739, 'synset': 'nutcracker.n.01', 'synonyms': ['nutcracker'], 'def': 'a hand tool used to crack nuts open', 'name': 'nutcracker'}, {'frequency': 'c', 'id': 740, 'synset': 'oar.n.01', 'synonyms': ['oar'], 'def': 'an implement used to propel or steer a boat', 'name': 'oar'}, {'frequency': 'r', 'id': 741, 'synset': 'octopus.n.01', 'synonyms': ['octopus_(food)'], 'def': 'tentacles of octopus prepared as food', 'name': 'octopus_(food)'}, {'frequency': 'r', 'id': 742, 'synset': 'octopus.n.02', 'synonyms': ['octopus_(animal)'], 'def': 'bottom-living cephalopod having a soft oval body with eight long tentacles', 'name': 'octopus_(animal)'}, {'frequency': 'c', 'id': 743, 'synset': 'oil_lamp.n.01', 'synonyms': ['oil_lamp', 'kerosene_lamp', 'kerosine_lamp'], 'def': 'a lamp that burns oil (as kerosine) for light', 'name': 'oil_lamp'}, {'frequency': 'c', 'id': 744, 'synset': 'olive_oil.n.01', 'synonyms': ['olive_oil'], 'def': 'oil from olives', 'name': 'olive_oil'}, {'frequency': 'r', 'id': 745, 'synset': 'omelet.n.01', 'synonyms': ['omelet', 'omelette'], 'def': 'beaten eggs cooked until just set; may be folded around e.g. ham or cheese or jelly', 'name': 'omelet'}, {'frequency': 'f', 'id': 746, 'synset': 'onion.n.01', 'synonyms': ['onion'], 'def': 'the bulb of an onion plant', 'name': 'onion'}, {'frequency': 'f', 'id': 747, 'synset': 'orange.n.01', 'synonyms': ['orange_(fruit)'], 'def': 'orange (FRUIT of an orange tree)', 'name': 'orange_(fruit)'}, {'frequency': 'c', 'id': 748, 'synset': 'orange_juice.n.01', 'synonyms': ['orange_juice'], 'def': 'bottled or freshly squeezed juice of oranges', 'name': 'orange_juice'}, {'frequency': 'r', 'id': 749, 'synset': 'oregano.n.01', 'synonyms': ['oregano', 'marjoram'], 'def': 'aromatic Eurasian perennial herb used in cooking and baking', 'name': 'oregano'}, {'frequency': 'c', 'id': 750, 'synset': 'ostrich.n.02', 'synonyms': ['ostrich'], 'def': 'fast-running African flightless bird with two-toed feet; largest living bird', 'name': 'ostrich'}, {'frequency': 'c', 'id': 751, 'synset': 'ottoman.n.03', 'synonyms': ['ottoman', 'pouf', 'pouffe', 'hassock'], 'def': 'thick cushion used as a seat', 'name': 'ottoman'}, {'frequency': 'c', 'id': 752, 'synset': 'overall.n.01', 'synonyms': ['overalls_(clothing)'], 'def': 'work clothing consisting of denim trousers usually with a bib and shoulder straps', 'name': 'overalls_(clothing)'}, {'frequency': 'c', 'id': 753, 'synset': 'owl.n.01', 'synonyms': ['owl'], 'def': 'nocturnal bird of prey with hawk-like beak and claws and large head with front-facing eyes', 'name': 'owl'}, {'frequency': 'c', 'id': 754, 'synset': 'packet.n.03', 'synonyms': ['packet'], 'def': 'a small package or bundle', 'name': 'packet'}, {'frequency': 'r', 'id': 755, 'synset': 'pad.n.03', 'synonyms': ['inkpad', 'inking_pad', 'stamp_pad'], 'def': 'absorbent material saturated with ink used to transfer ink evenly to a rubber stamp', 'name': 'inkpad'}, {'frequency': 'c', 'id': 756, 'synset': 'pad.n.04', 'synonyms': ['pad'], 'def': 'a flat mass of soft material used for protection, stuffing, or comfort', 'name': 'pad'}, {'frequency': 'c', 'id': 757, 'synset': 'paddle.n.04', 'synonyms': ['paddle', 'boat_paddle'], 'def': 'a short light oar used without an oarlock to propel a canoe or small boat', 'name': 'paddle'}, {'frequency': 'c', 'id': 758, 'synset': 'padlock.n.01', 'synonyms': ['padlock'], 'def': 'a detachable, portable lock', 'name': 'padlock'}, {'frequency': 'r', 'id': 759, 'synset': 'paintbox.n.01', 'synonyms': ['paintbox'], 'def': "a box containing a collection of cubes or tubes of artists' paint", 'name': 'paintbox'}, {'frequency': 'c', 'id': 760, 'synset': 'paintbrush.n.01', 'synonyms': ['paintbrush'], 'def': 'a brush used as an applicator to apply paint', 'name': 'paintbrush'}, {'frequency': 'f', 'id': 761, 'synset': 'painting.n.01', 'synonyms': ['painting'], 'def': 'graphic art consisting of an artistic composition made by applying paints to a surface', 'name': 'painting'}, {'frequency': 'c', 'id': 762, 'synset': 'pajama.n.02', 'synonyms': ['pajamas', 'pyjamas'], 'def': 'loose-fitting nightclothes worn for sleeping or lounging', 'name': 'pajamas'}, {'frequency': 'c', 'id': 763, 'synset': 'palette.n.02', 'synonyms': ['palette', 'pallet'], 'def': 'board that provides a flat surface on which artists mix paints and the range of colors used', 'name': 'palette'}, {'frequency': 'f', 'id': 764, 'synset': 'pan.n.01', 'synonyms': ['pan_(for_cooking)', 'cooking_pan'], 'def': 'cooking utensil consisting of a wide metal vessel', 'name': 'pan_(for_cooking)'}, {'frequency': 'r', 'id': 765, 'synset': 'pan.n.03', 'synonyms': ['pan_(metal_container)'], 'def': 'shallow container made of metal', 'name': 'pan_(metal_container)'}, {'frequency': 'c', 'id': 766, 'synset': 'pancake.n.01', 'synonyms': ['pancake'], 'def': 'a flat cake of thin batter fried on both sides on a griddle', 'name': 'pancake'}, {'frequency': 'r', 'id': 767, 'synset': 'pantyhose.n.01', 'synonyms': ['pantyhose'], 'def': "a woman's tights consisting of underpants and stockings", 'name': 'pantyhose'}, {'frequency': 'r', 'id': 768, 'synset': 'papaya.n.02', 'synonyms': ['papaya'], 'def': 'large oval melon-like tropical fruit with yellowish flesh', 'name': 'papaya'}, {'frequency': 'r', 'id': 769, 'synset': 'paper_clip.n.01', 'synonyms': ['paperclip'], 'def': 'a wire or plastic clip for holding sheets of paper together', 'name': 'paperclip'}, {'frequency': 'f', 'id': 770, 'synset': 'paper_plate.n.01', 'synonyms': ['paper_plate'], 'def': 'a disposable plate made of cardboard', 'name': 'paper_plate'}, {'frequency': 'f', 'id': 771, 'synset': 'paper_towel.n.01', 'synonyms': ['paper_towel'], 'def': 'a disposable towel made of absorbent paper', 'name': 'paper_towel'}, {'frequency': 'r', 'id': 772, 'synset': 'paperback_book.n.01', 'synonyms': ['paperback_book', 'paper-back_book', 'softback_book', 'soft-cover_book'], 'def': 'a book with paper covers', 'name': 'paperback_book'}, {'frequency': 'r', 'id': 773, 'synset': 'paperweight.n.01', 'synonyms': ['paperweight'], 'def': 'a weight used to hold down a stack of papers', 'name': 'paperweight'}, {'frequency': 'c', 'id': 774, 'synset': 'parachute.n.01', 'synonyms': ['parachute'], 'def': 'rescue equipment consisting of a device that fills with air and retards your fall', 'name': 'parachute'}, {'frequency': 'r', 'id': 775, 'synset': 'parakeet.n.01', 'synonyms': ['parakeet', 'parrakeet', 'parroket', 'paraquet', 'paroquet', 'parroquet'], 'def': 'any of numerous small slender long-tailed parrots', 'name': 'parakeet'}, {'frequency': 'c', 'id': 776, 'synset': 'parasail.n.01', 'synonyms': ['parasail_(sports)'], 'def': 'parachute that will lift a person up into the air when it is towed by a motorboat or a car', 'name': 'parasail_(sports)'}, {'frequency': 'r', 'id': 777, 'synset': 'parchment.n.01', 'synonyms': ['parchment'], 'def': 'a superior paper resembling sheepskin', 'name': 'parchment'}, {'frequency': 'r', 'id': 778, 'synset': 'parka.n.01', 'synonyms': ['parka', 'anorak'], 'def': "a kind of heavy jacket (`windcheater' is a British term)", 'name': 'parka'}, {'frequency': 'f', 'id': 779, 'synset': 'parking_meter.n.01', 'synonyms': ['parking_meter'], 'def': 'a coin-operated timer located next to a parking space', 'name': 'parking_meter'}, {'frequency': 'c', 'id': 780, 'synset': 'parrot.n.01', 'synonyms': ['parrot'], 'def': 'usually brightly colored tropical birds with short hooked beaks and the ability to mimic sounds', 'name': 'parrot'}, {'frequency': 'c', 'id': 781, 'synset': 'passenger_car.n.01', 'synonyms': ['passenger_car_(part_of_a_train)', 'coach_(part_of_a_train)'], 'def': 'a railcar where passengers ride', 'name': 'passenger_car_(part_of_a_train)'}, {'frequency': 'r', 'id': 782, 'synset': 'passenger_ship.n.01', 'synonyms': ['passenger_ship'], 'def': 'a ship built to carry passengers', 'name': 'passenger_ship'}, {'frequency': 'r', 'id': 783, 'synset': 'passport.n.02', 'synonyms': ['passport'], 'def': 'a document issued by a country to a citizen allowing that person to travel abroad and re-enter the home country', 'name': 'passport'}, {'frequency': 'f', 'id': 784, 'synset': 'pastry.n.02', 'synonyms': ['pastry'], 'def': 'any of various baked foods made of dough or batter', 'name': 'pastry'}, {'frequency': 'r', 'id': 785, 'synset': 'patty.n.01', 'synonyms': ['patty_(food)'], 'def': 'small flat mass of chopped food', 'name': 'patty_(food)'}, {'frequency': 'c', 'id': 786, 'synset': 'pea.n.01', 'synonyms': ['pea_(food)'], 'def': 'seed of a pea plant used for food', 'name': 'pea_(food)'}, {'frequency': 'c', 'id': 787, 'synset': 'peach.n.03', 'synonyms': ['peach'], 'def': 'downy juicy fruit with sweet yellowish or whitish flesh', 'name': 'peach'}, {'frequency': 'c', 'id': 788, 'synset': 'peanut_butter.n.01', 'synonyms': ['peanut_butter'], 'def': 'a spread made from ground peanuts', 'name': 'peanut_butter'}, {'frequency': 'c', 'id': 789, 'synset': 'pear.n.01', 'synonyms': ['pear'], 'def': 'sweet juicy gritty-textured fruit available in many varieties', 'name': 'pear'}, {'frequency': 'r', 'id': 790, 'synset': 'peeler.n.03', 'synonyms': ['peeler_(tool_for_fruit_and_vegetables)'], 'def': 'a device for peeling vegetables or fruits', 'name': 'peeler_(tool_for_fruit_and_vegetables)'}, {'frequency': 'r', 'id': 791, 'synset': 'pegboard.n.01', 'synonyms': ['pegboard'], 'def': 'a board perforated with regularly spaced holes into which pegs can be fitted', 'name': 'pegboard'}, {'frequency': 'c', 'id': 792, 'synset': 'pelican.n.01', 'synonyms': ['pelican'], 'def': 'large long-winged warm-water seabird having a large bill with a distensible pouch for fish', 'name': 'pelican'}, {'frequency': 'f', 'id': 793, 'synset': 'pen.n.01', 'synonyms': ['pen'], 'def': 'a writing implement with a point from which ink flows', 'name': 'pen'}, {'frequency': 'c', 'id': 794, 'synset': 'pencil.n.01', 'synonyms': ['pencil'], 'def': 'a thin cylindrical pointed writing implement made of wood and graphite', 'name': 'pencil'}, {'frequency': 'r', 'id': 795, 'synset': 'pencil_box.n.01', 'synonyms': ['pencil_box', 'pencil_case'], 'def': 'a box for holding pencils', 'name': 'pencil_box'}, {'frequency': 'r', 'id': 796, 'synset': 'pencil_sharpener.n.01', 'synonyms': ['pencil_sharpener'], 'def': 'a rotary implement for sharpening the point on pencils', 'name': 'pencil_sharpener'}, {'frequency': 'r', 'id': 797, 'synset': 'pendulum.n.01', 'synonyms': ['pendulum'], 'def': 'an apparatus consisting of an object mounted so that it swings freely under the influence of gravity', 'name': 'pendulum'}, {'frequency': 'c', 'id': 798, 'synset': 'penguin.n.01', 'synonyms': ['penguin'], 'def': 'short-legged flightless birds of cold southern regions having webbed feet and wings modified as flippers', 'name': 'penguin'}, {'frequency': 'r', 'id': 799, 'synset': 'pennant.n.02', 'synonyms': ['pennant'], 'def': 'a flag longer than it is wide (and often tapering)', 'name': 'pennant'}, {'frequency': 'r', 'id': 800, 'synset': 'penny.n.02', 'synonyms': ['penny_(coin)'], 'def': 'a coin worth one-hundredth of the value of the basic unit', 'name': 'penny_(coin)'}, {'frequency': 'c', 'id': 801, 'synset': 'pepper.n.03', 'synonyms': ['pepper', 'peppercorn'], 'def': 'pungent seasoning from the berry of the common pepper plant; whole or ground', 'name': 'pepper'}, {'frequency': 'c', 'id': 802, 'synset': 'pepper_mill.n.01', 'synonyms': ['pepper_mill', 'pepper_grinder'], 'def': 'a mill for grinding pepper', 'name': 'pepper_mill'}, {'frequency': 'c', 'id': 803, 'synset': 'perfume.n.02', 'synonyms': ['perfume'], 'def': 'a toiletry that emits and diffuses a fragrant odor', 'name': 'perfume'}, {'frequency': 'r', 'id': 804, 'synset': 'persimmon.n.02', 'synonyms': ['persimmon'], 'def': 'orange fruit resembling a plum; edible when fully ripe', 'name': 'persimmon'}, {'frequency': 'f', 'id': 805, 'synset': 'person.n.01', 'synonyms': ['baby', 'child', 'boy', 'girl', 'man', 'woman', 'person', 'human'], 'def': 'a human being', 'name': 'baby'}, {'frequency': 'r', 'id': 806, 'synset': 'pet.n.01', 'synonyms': ['pet'], 'def': 'a domesticated animal kept for companionship or amusement', 'name': 'pet'}, {'frequency': 'r', 'id': 807, 'synset': 'petfood.n.01', 'synonyms': ['petfood', 'pet-food'], 'def': 'food prepared for animal pets', 'name': 'petfood'}, {'frequency': 'r', 'id': 808, 'synset': 'pew.n.01', 'synonyms': ['pew_(church_bench)', 'church_bench'], 'def': 'long bench with backs; used in church by the congregation', 'name': 'pew_(church_bench)'}, {'frequency': 'r', 'id': 809, 'synset': 'phonebook.n.01', 'synonyms': ['phonebook', 'telephone_book', 'telephone_directory'], 'def': 'a directory containing an alphabetical list of telephone subscribers and their telephone numbers', 'name': 'phonebook'}, {'frequency': 'c', 'id': 810, 'synset': 'phonograph_record.n.01', 'synonyms': ['phonograph_record', 'phonograph_recording', 'record_(phonograph_recording)'], 'def': 'sound recording consisting of a typically black disk with a continuous groove', 'name': 'phonograph_record'}, {'frequency': 'c', 'id': 811, 'synset': 'piano.n.01', 'synonyms': ['piano'], 'def': 'a keyboard instrument that is played by depressing keys that cause hammers to strike tuned strings and produce sounds', 'name': 'piano'}, {'frequency': 'f', 'id': 812, 'synset': 'pickle.n.01', 'synonyms': ['pickle'], 'def': 'vegetables (especially cucumbers) preserved in brine or vinegar', 'name': 'pickle'}, {'frequency': 'f', 'id': 813, 'synset': 'pickup.n.01', 'synonyms': ['pickup_truck'], 'def': 'a light truck with an open body and low sides and a tailboard', 'name': 'pickup_truck'}, {'frequency': 'c', 'id': 814, 'synset': 'pie.n.01', 'synonyms': ['pie'], 'def': 'dish baked in pastry-lined pan often with a pastry top', 'name': 'pie'}, {'frequency': 'c', 'id': 815, 'synset': 'pigeon.n.01', 'synonyms': ['pigeon'], 'def': 'wild and domesticated birds having a heavy body and short legs', 'name': 'pigeon'}, {'frequency': 'r', 'id': 816, 'synset': 'piggy_bank.n.01', 'synonyms': ['piggy_bank', 'penny_bank'], 'def': "a child's coin bank (often shaped like a pig)", 'name': 'piggy_bank'}, {'frequency': 'f', 'id': 817, 'synset': 'pillow.n.01', 'synonyms': ['pillow'], 'def': 'a cushion to support the head of a sleeping person', 'name': 'pillow'}, {'frequency': 'r', 'id': 818, 'synset': 'pin.n.09', 'synonyms': ['pin_(non_jewelry)'], 'def': 'a small slender (often pointed) piece of wood or metal used to support or fasten or attach things', 'name': 'pin_(non_jewelry)'}, {'frequency': 'f', 'id': 819, 'synset': 'pineapple.n.02', 'synonyms': ['pineapple'], 'def': 'large sweet fleshy tropical fruit with a tuft of stiff leaves', 'name': 'pineapple'}, {'frequency': 'c', 'id': 820, 'synset': 'pinecone.n.01', 'synonyms': ['pinecone'], 'def': 'the seed-producing cone of a pine tree', 'name': 'pinecone'}, {'frequency': 'r', 'id': 821, 'synset': 'ping-pong_ball.n.01', 'synonyms': ['ping-pong_ball'], 'def': 'light hollow ball used in playing table tennis', 'name': 'ping-pong_ball'}, {'frequency': 'r', 'id': 822, 'synset': 'pinwheel.n.03', 'synonyms': ['pinwheel'], 'def': 'a toy consisting of vanes of colored paper or plastic that is pinned to a stick and spins when it is pointed into the wind', 'name': 'pinwheel'}, {'frequency': 'r', 'id': 823, 'synset': 'pipe.n.01', 'synonyms': ['tobacco_pipe'], 'def': 'a tube with a small bowl at one end; used for smoking tobacco', 'name': 'tobacco_pipe'}, {'frequency': 'f', 'id': 824, 'synset': 'pipe.n.02', 'synonyms': ['pipe', 'piping'], 'def': 'a long tube made of metal or plastic that is used to carry water or oil or gas etc.', 'name': 'pipe'}, {'frequency': 'r', 'id': 825, 'synset': 'pistol.n.01', 'synonyms': ['pistol', 'handgun'], 'def': 'a firearm that is held and fired with one hand', 'name': 'pistol'}, {'frequency': 'r', 'id': 826, 'synset': 'pita.n.01', 'synonyms': ['pita_(bread)', 'pocket_bread'], 'def': 'usually small round bread that can open into a pocket for filling', 'name': 'pita_(bread)'}, {'frequency': 'f', 'id': 827, 'synset': 'pitcher.n.02', 'synonyms': ['pitcher_(vessel_for_liquid)', 'ewer'], 'def': 'an open vessel with a handle and a spout for pouring', 'name': 'pitcher_(vessel_for_liquid)'}, {'frequency': 'r', 'id': 828, 'synset': 'pitchfork.n.01', 'synonyms': ['pitchfork'], 'def': 'a long-handled hand tool with sharp widely spaced prongs for lifting and pitching hay', 'name': 'pitchfork'}, {'frequency': 'f', 'id': 829, 'synset': 'pizza.n.01', 'synonyms': ['pizza'], 'def': 'Italian open pie made of thin bread dough spread with a spiced mixture of e.g. tomato sauce and cheese', 'name': 'pizza'}, {'frequency': 'f', 'id': 830, 'synset': 'place_mat.n.01', 'synonyms': ['place_mat'], 'def': 'a mat placed on a table for an individual place setting', 'name': 'place_mat'}, {'frequency': 'f', 'id': 831, 'synset': 'plate.n.04', 'synonyms': ['plate'], 'def': 'dish on which food is served or from which food is eaten', 'name': 'plate'}, {'frequency': 'c', 'id': 832, 'synset': 'platter.n.01', 'synonyms': ['platter'], 'def': 'a large shallow dish used for serving food', 'name': 'platter'}, {'frequency': 'r', 'id': 833, 'synset': 'playing_card.n.01', 'synonyms': ['playing_card'], 'def': 'one of a pack of cards that are used to play card games', 'name': 'playing_card'}, {'frequency': 'r', 'id': 834, 'synset': 'playpen.n.01', 'synonyms': ['playpen'], 'def': 'a portable enclosure in which babies may be left to play', 'name': 'playpen'}, {'frequency': 'c', 'id': 835, 'synset': 'pliers.n.01', 'synonyms': ['pliers', 'plyers'], 'def': 'a gripping hand tool with two hinged arms and (usually) serrated jaws', 'name': 'pliers'}, {'frequency': 'r', 'id': 836, 'synset': 'plow.n.01', 'synonyms': ['plow_(farm_equipment)', 'plough_(farm_equipment)'], 'def': 'a farm tool having one or more heavy blades to break the soil and cut a furrow prior to sowing', 'name': 'plow_(farm_equipment)'}, {'frequency': 'r', 'id': 837, 'synset': 'pocket_watch.n.01', 'synonyms': ['pocket_watch'], 'def': 'a watch that is carried in a small watch pocket', 'name': 'pocket_watch'}, {'frequency': 'c', 'id': 838, 'synset': 'pocketknife.n.01', 'synonyms': ['pocketknife'], 'def': 'a knife with a blade that folds into the handle; suitable for carrying in the pocket', 'name': 'pocketknife'}, {'frequency': 'c', 'id': 839, 'synset': 'poker.n.01', 'synonyms': ['poker_(fire_stirring_tool)', 'stove_poker', 'fire_hook'], 'def': 'fire iron consisting of a metal rod with a handle; used to stir a fire', 'name': 'poker_(fire_stirring_tool)'}, {'frequency': 'f', 'id': 840, 'synset': 'pole.n.01', 'synonyms': ['pole', 'post'], 'def': 'a long (usually round) rod of wood or metal or plastic', 'name': 'pole'}, {'frequency': 'r', 'id': 841, 'synset': 'police_van.n.01', 'synonyms': ['police_van', 'police_wagon', 'paddy_wagon', 'patrol_wagon'], 'def': 'van used by police to transport prisoners', 'name': 'police_van'}, {'frequency': 'f', 'id': 842, 'synset': 'polo_shirt.n.01', 'synonyms': ['polo_shirt', 'sport_shirt'], 'def': 'a shirt with short sleeves designed for comfort and casual wear', 'name': 'polo_shirt'}, {'frequency': 'r', 'id': 843, 'synset': 'poncho.n.01', 'synonyms': ['poncho'], 'def': 'a blanket-like cloak with a hole in the center for the head', 'name': 'poncho'}, {'frequency': 'c', 'id': 844, 'synset': 'pony.n.05', 'synonyms': ['pony'], 'def': 'any of various breeds of small gentle horses usually less than five feet high at the shoulder', 'name': 'pony'}, {'frequency': 'r', 'id': 845, 'synset': 'pool_table.n.01', 'synonyms': ['pool_table', 'billiard_table', 'snooker_table'], 'def': 'game equipment consisting of a heavy table on which pool is played', 'name': 'pool_table'}, {'frequency': 'f', 'id': 846, 'synset': 'pop.n.02', 'synonyms': ['pop_(soda)', 'soda_(pop)', 'tonic', 'soft_drink'], 'def': 'a sweet drink containing carbonated water and flavoring', 'name': 'pop_(soda)'}, {'frequency': 'r', 'id': 847, 'synset': 'portrait.n.02', 'synonyms': ['portrait', 'portrayal'], 'def': 'any likeness of a person, in any medium', 'name': 'portrait'}, {'frequency': 'c', 'id': 848, 'synset': 'postbox.n.01', 'synonyms': ['postbox_(public)', 'mailbox_(public)'], 'def': 'public box for deposit of mail', 'name': 'postbox_(public)'}, {'frequency': 'c', 'id': 849, 'synset': 'postcard.n.01', 'synonyms': ['postcard', 'postal_card', 'mailing-card'], 'def': 'a card for sending messages by post without an envelope', 'name': 'postcard'}, {'frequency': 'f', 'id': 850, 'synset': 'poster.n.01', 'synonyms': ['poster', 'placard'], 'def': 'a sign posted in a public place as an advertisement', 'name': 'poster'}, {'frequency': 'f', 'id': 851, 'synset': 'pot.n.01', 'synonyms': ['pot'], 'def': 'metal or earthenware cooking vessel that is usually round and deep; often has a handle and lid', 'name': 'pot'}, {'frequency': 'f', 'id': 852, 'synset': 'pot.n.04', 'synonyms': ['flowerpot'], 'def': 'a container in which plants are cultivated', 'name': 'flowerpot'}, {'frequency': 'f', 'id': 853, 'synset': 'potato.n.01', 'synonyms': ['potato'], 'def': 'an edible tuber native to South America', 'name': 'potato'}, {'frequency': 'c', 'id': 854, 'synset': 'potholder.n.01', 'synonyms': ['potholder'], 'def': 'an insulated pad for holding hot pots', 'name': 'potholder'}, {'frequency': 'c', 'id': 855, 'synset': 'pottery.n.01', 'synonyms': ['pottery', 'clayware'], 'def': 'ceramic ware made from clay and baked in a kiln', 'name': 'pottery'}, {'frequency': 'c', 'id': 856, 'synset': 'pouch.n.01', 'synonyms': ['pouch'], 'def': 'a small or medium size container for holding or carrying things', 'name': 'pouch'}, {'frequency': 'r', 'id': 857, 'synset': 'power_shovel.n.01', 'synonyms': ['power_shovel', 'excavator', 'digger'], 'def': 'a machine for excavating', 'name': 'power_shovel'}, {'frequency': 'c', 'id': 858, 'synset': 'prawn.n.01', 'synonyms': ['prawn', 'shrimp'], 'def': 'any of various edible decapod crustaceans', 'name': 'prawn'}, {'frequency': 'f', 'id': 859, 'synset': 'printer.n.03', 'synonyms': ['printer', 'printing_machine'], 'def': 'a machine that prints', 'name': 'printer'}, {'frequency': 'c', 'id': 860, 'synset': 'projectile.n.01', 'synonyms': ['projectile_(weapon)', 'missile'], 'def': 'a weapon that is forcibly thrown or projected at a targets', 'name': 'projectile_(weapon)'}, {'frequency': 'c', 'id': 861, 'synset': 'projector.n.02', 'synonyms': ['projector'], 'def': 'an optical instrument that projects an enlarged image onto a screen', 'name': 'projector'}, {'frequency': 'f', 'id': 862, 'synset': 'propeller.n.01', 'synonyms': ['propeller', 'propellor'], 'def': 'a mechanical device that rotates to push against air or water', 'name': 'propeller'}, {'frequency': 'r', 'id': 863, 'synset': 'prune.n.01', 'synonyms': ['prune'], 'def': 'dried plum', 'name': 'prune'}, {'frequency': 'r', 'id': 864, 'synset': 'pudding.n.01', 'synonyms': ['pudding'], 'def': 'any of various soft thick unsweetened baked dishes', 'name': 'pudding'}, {'frequency': 'r', 'id': 865, 'synset': 'puffer.n.02', 'synonyms': ['puffer_(fish)', 'pufferfish', 'blowfish', 'globefish'], 'def': 'fishes whose elongated spiny body can inflate itself with water or air to form a globe', 'name': 'puffer_(fish)'}, {'frequency': 'r', 'id': 866, 'synset': 'puffin.n.01', 'synonyms': ['puffin'], 'def': 'seabirds having short necks and brightly colored compressed bills', 'name': 'puffin'}, {'frequency': 'r', 'id': 867, 'synset': 'pug.n.01', 'synonyms': ['pug-dog'], 'def': 'small compact smooth-coated breed of Asiatic origin having a tightly curled tail and broad flat wrinkled muzzle', 'name': 'pug-dog'}, {'frequency': 'c', 'id': 868, 'synset': 'pumpkin.n.02', 'synonyms': ['pumpkin'], 'def': 'usually large pulpy deep-yellow round fruit of the squash family maturing in late summer or early autumn', 'name': 'pumpkin'}, {'frequency': 'r', 'id': 869, 'synset': 'punch.n.03', 'synonyms': ['puncher'], 'def': 'a tool for making holes or indentations', 'name': 'puncher'}, {'frequency': 'r', 'id': 870, 'synset': 'puppet.n.01', 'synonyms': ['puppet', 'marionette'], 'def': 'a small figure of a person operated from above with strings by a puppeteer', 'name': 'puppet'}, {'frequency': 'r', 'id': 871, 'synset': 'puppy.n.01', 'synonyms': ['puppy'], 'def': 'a young dog', 'name': 'puppy'}, {'frequency': 'r', 'id': 872, 'synset': 'quesadilla.n.01', 'synonyms': ['quesadilla'], 'def': 'a tortilla that is filled with cheese and heated', 'name': 'quesadilla'}, {'frequency': 'r', 'id': 873, 'synset': 'quiche.n.02', 'synonyms': ['quiche'], 'def': 'a tart filled with rich unsweetened custard; often contains other ingredients (as cheese or ham or seafood or vegetables)', 'name': 'quiche'}, {'frequency': 'f', 'id': 874, 'synset': 'quilt.n.01', 'synonyms': ['quilt', 'comforter'], 'def': 'bedding made of two layers of cloth filled with stuffing and stitched together', 'name': 'quilt'}, {'frequency': 'c', 'id': 875, 'synset': 'rabbit.n.01', 'synonyms': ['rabbit'], 'def': 'any of various burrowing animals of the family Leporidae having long ears and short tails', 'name': 'rabbit'}, {'frequency': 'r', 'id': 876, 'synset': 'racer.n.02', 'synonyms': ['race_car', 'racing_car'], 'def': 'a fast car that competes in races', 'name': 'race_car'}, {'frequency': 'c', 'id': 877, 'synset': 'racket.n.04', 'synonyms': ['racket', 'racquet'], 'def': 'a sports implement used to strike a ball in various games', 'name': 'racket'}, {'frequency': 'r', 'id': 878, 'synset': 'radar.n.01', 'synonyms': ['radar'], 'def': 'measuring instrument in which the echo of a pulse of microwave radiation is used to detect and locate distant objects', 'name': 'radar'}, {'frequency': 'c', 'id': 879, 'synset': 'radiator.n.03', 'synonyms': ['radiator'], 'def': 'a mechanism consisting of a metal honeycomb through which hot fluids circulate', 'name': 'radiator'}, {'frequency': 'c', 'id': 880, 'synset': 'radio_receiver.n.01', 'synonyms': ['radio_receiver', 'radio_set', 'radio', 'tuner_(radio)'], 'def': 'an electronic receiver that detects and demodulates and amplifies transmitted radio signals', 'name': 'radio_receiver'}, {'frequency': 'c', 'id': 881, 'synset': 'radish.n.03', 'synonyms': ['radish', 'daikon'], 'def': 'pungent edible root of any of various cultivated radish plants', 'name': 'radish'}, {'frequency': 'c', 'id': 882, 'synset': 'raft.n.01', 'synonyms': ['raft'], 'def': 'a flat float (usually made of logs or planks) that can be used for transport or as a platform for swimmers', 'name': 'raft'}, {'frequency': 'r', 'id': 883, 'synset': 'rag_doll.n.01', 'synonyms': ['rag_doll'], 'def': 'a cloth doll that is stuffed and (usually) painted', 'name': 'rag_doll'}, {'frequency': 'c', 'id': 884, 'synset': 'raincoat.n.01', 'synonyms': ['raincoat', 'waterproof_jacket'], 'def': 'a water-resistant coat', 'name': 'raincoat'}, {'frequency': 'c', 'id': 885, 'synset': 'ram.n.05', 'synonyms': ['ram_(animal)'], 'def': 'uncastrated adult male sheep', 'name': 'ram_(animal)'}, {'frequency': 'c', 'id': 886, 'synset': 'raspberry.n.02', 'synonyms': ['raspberry'], 'def': 'red or black edible aggregate berries usually smaller than the related blackberries', 'name': 'raspberry'}, {'frequency': 'r', 'id': 887, 'synset': 'rat.n.01', 'synonyms': ['rat'], 'def': 'any of various long-tailed rodents similar to but larger than a mouse', 'name': 'rat'}, {'frequency': 'c', 'id': 888, 'synset': 'razorblade.n.01', 'synonyms': ['razorblade'], 'def': 'a blade that has very sharp edge', 'name': 'razorblade'}, {'frequency': 'c', 'id': 889, 'synset': 'reamer.n.01', 'synonyms': ['reamer_(juicer)', 'juicer', 'juice_reamer'], 'def': 'a squeezer with a conical ridged center that is used for squeezing juice from citrus fruit', 'name': 'reamer_(juicer)'}, {'frequency': 'f', 'id': 890, 'synset': 'rearview_mirror.n.01', 'synonyms': ['rearview_mirror'], 'def': 'car mirror that reflects the view out of the rear window', 'name': 'rearview_mirror'}, {'frequency': 'c', 'id': 891, 'synset': 'receipt.n.02', 'synonyms': ['receipt'], 'def': 'an acknowledgment (usually tangible) that payment has been made', 'name': 'receipt'}, {'frequency': 'c', 'id': 892, 'synset': 'recliner.n.01', 'synonyms': ['recliner', 'reclining_chair', 'lounger_(chair)'], 'def': 'an armchair whose back can be lowered and foot can be raised to allow the sitter to recline in it', 'name': 'recliner'}, {'frequency': 'r', 'id': 893, 'synset': 'record_player.n.01', 'synonyms': ['record_player', 'phonograph_(record_player)', 'turntable'], 'def': 'machine in which rotating records cause a stylus to vibrate and the vibrations are amplified acoustically or electronically', 'name': 'record_player'}, {'frequency': 'r', 'id': 894, 'synset': 'red_cabbage.n.02', 'synonyms': ['red_cabbage'], 'def': 'compact head of purplish-red leaves', 'name': 'red_cabbage'}, {'frequency': 'f', 'id': 895, 'synset': 'reflector.n.01', 'synonyms': ['reflector'], 'def': 'device that reflects light, radiation, etc.', 'name': 'reflector'}, {'frequency': 'f', 'id': 896, 'synset': 'remote_control.n.01', 'synonyms': ['remote_control'], 'def': 'a device that can be used to control a machine or apparatus from a distance', 'name': 'remote_control'}, {'frequency': 'c', 'id': 897, 'synset': 'rhinoceros.n.01', 'synonyms': ['rhinoceros'], 'def': 'massive powerful herbivorous odd-toed ungulate of southeast Asia and Africa having very thick skin and one or two horns on the snout', 'name': 'rhinoceros'}, {'frequency': 'r', 'id': 898, 'synset': 'rib.n.03', 'synonyms': ['rib_(food)'], 'def': 'cut of meat including one or more ribs', 'name': 'rib_(food)'}, {'frequency': 'r', 'id': 899, 'synset': 'rifle.n.01', 'synonyms': ['rifle'], 'def': 'a shoulder firearm with a long barrel', 'name': 'rifle'}, {'frequency': 'f', 'id': 900, 'synset': 'ring.n.08', 'synonyms': ['ring'], 'def': 'jewelry consisting of a circlet of precious metal (often set with jewels) worn on the finger', 'name': 'ring'}, {'frequency': 'r', 'id': 901, 'synset': 'river_boat.n.01', 'synonyms': ['river_boat'], 'def': 'a boat used on rivers or to ply a river', 'name': 'river_boat'}, {'frequency': 'r', 'id': 902, 'synset': 'road_map.n.02', 'synonyms': ['road_map'], 'def': '(NOT A ROAD) a MAP showing roads (for automobile travel)', 'name': 'road_map'}, {'frequency': 'c', 'id': 903, 'synset': 'robe.n.01', 'synonyms': ['robe'], 'def': 'any loose flowing garment', 'name': 'robe'}, {'frequency': 'c', 'id': 904, 'synset': 'rocking_chair.n.01', 'synonyms': ['rocking_chair'], 'def': 'a chair mounted on rockers', 'name': 'rocking_chair'}, {'frequency': 'r', 'id': 905, 'synset': 'roller_skate.n.01', 'synonyms': ['roller_skate'], 'def': 'a shoe with pairs of rollers (small hard wheels) fixed to the sole', 'name': 'roller_skate'}, {'frequency': 'r', 'id': 906, 'synset': 'rollerblade.n.01', 'synonyms': ['Rollerblade'], 'def': 'an in-line variant of a roller skate', 'name': 'Rollerblade'}, {'frequency': 'c', 'id': 907, 'synset': 'rolling_pin.n.01', 'synonyms': ['rolling_pin'], 'def': 'utensil consisting of a cylinder (usually of wood) with a handle at each end; used to roll out dough', 'name': 'rolling_pin'}, {'frequency': 'r', 'id': 908, 'synset': 'root_beer.n.01', 'synonyms': ['root_beer'], 'def': 'carbonated drink containing extracts of roots and herbs', 'name': 'root_beer'}, {'frequency': 'c', 'id': 909, 'synset': 'router.n.02', 'synonyms': ['router_(computer_equipment)'], 'def': 'a device that forwards data packets between computer networks', 'name': 'router_(computer_equipment)'}, {'frequency': 'f', 'id': 910, 'synset': 'rubber_band.n.01', 'synonyms': ['rubber_band', 'elastic_band'], 'def': 'a narrow band of elastic rubber used to hold things (such as papers) together', 'name': 'rubber_band'}, {'frequency': 'c', 'id': 911, 'synset': 'runner.n.08', 'synonyms': ['runner_(carpet)'], 'def': 'a long narrow carpet', 'name': 'runner_(carpet)'}, {'frequency': 'f', 'id': 912, 'synset': 'sack.n.01', 'synonyms': ['plastic_bag', 'paper_bag'], 'def': "a bag made of paper or plastic for holding customer's purchases", 'name': 'plastic_bag'}, {'frequency': 'f', 'id': 913, 'synset': 'saddle.n.01', 'synonyms': ['saddle_(on_an_animal)'], 'def': 'a seat for the rider of a horse or camel', 'name': 'saddle_(on_an_animal)'}, {'frequency': 'f', 'id': 914, 'synset': 'saddle_blanket.n.01', 'synonyms': ['saddle_blanket', 'saddlecloth', 'horse_blanket'], 'def': 'stable gear consisting of a blanket placed under the saddle', 'name': 'saddle_blanket'}, {'frequency': 'c', 'id': 915, 'synset': 'saddlebag.n.01', 'synonyms': ['saddlebag'], 'def': 'a large bag (or pair of bags) hung over a saddle', 'name': 'saddlebag'}, {'frequency': 'r', 'id': 916, 'synset': 'safety_pin.n.01', 'synonyms': ['safety_pin'], 'def': 'a pin in the form of a clasp; has a guard so the point of the pin will not stick the user', 'name': 'safety_pin'}, {'frequency': 'c', 'id': 917, 'synset': 'sail.n.01', 'synonyms': ['sail'], 'def': 'a large piece of fabric by means of which wind is used to propel a sailing vessel', 'name': 'sail'}, {'frequency': 'c', 'id': 918, 'synset': 'salad.n.01', 'synonyms': ['salad'], 'def': 'food mixtures either arranged on a plate or tossed and served with a moist dressing; usually consisting of or including greens', 'name': 'salad'}, {'frequency': 'r', 'id': 919, 'synset': 'salad_plate.n.01', 'synonyms': ['salad_plate', 'salad_bowl'], 'def': 'a plate or bowl for individual servings of salad', 'name': 'salad_plate'}, {'frequency': 'r', 'id': 920, 'synset': 'salami.n.01', 'synonyms': ['salami'], 'def': 'highly seasoned fatty sausage of pork and beef usually dried', 'name': 'salami'}, {'frequency': 'r', 'id': 921, 'synset': 'salmon.n.01', 'synonyms': ['salmon_(fish)'], 'def': 'any of various large food and game fishes of northern waters', 'name': 'salmon_(fish)'}, {'frequency': 'r', 'id': 922, 'synset': 'salmon.n.03', 'synonyms': ['salmon_(food)'], 'def': 'flesh of any of various marine or freshwater fish of the family Salmonidae', 'name': 'salmon_(food)'}, {'frequency': 'r', 'id': 923, 'synset': 'salsa.n.01', 'synonyms': ['salsa'], 'def': 'spicy sauce of tomatoes and onions and chili peppers to accompany Mexican foods', 'name': 'salsa'}, {'frequency': 'f', 'id': 924, 'synset': 'saltshaker.n.01', 'synonyms': ['saltshaker'], 'def': 'a shaker with a perforated top for sprinkling salt', 'name': 'saltshaker'}, {'frequency': 'f', 'id': 925, 'synset': 'sandal.n.01', 'synonyms': ['sandal_(type_of_shoe)'], 'def': 'a shoe consisting of a sole fastened by straps to the foot', 'name': 'sandal_(type_of_shoe)'}, {'frequency': 'f', 'id': 926, 'synset': 'sandwich.n.01', 'synonyms': ['sandwich'], 'def': 'two (or more) slices of bread with a filling between them', 'name': 'sandwich'}, {'frequency': 'r', 'id': 927, 'synset': 'satchel.n.01', 'synonyms': ['satchel'], 'def': 'luggage consisting of a small case with a flat bottom and (usually) a shoulder strap', 'name': 'satchel'}, {'frequency': 'r', 'id': 928, 'synset': 'saucepan.n.01', 'synonyms': ['saucepan'], 'def': 'a deep pan with a handle; used for stewing or boiling', 'name': 'saucepan'}, {'frequency': 'f', 'id': 929, 'synset': 'saucer.n.02', 'synonyms': ['saucer'], 'def': 'a small shallow dish for holding a cup at the table', 'name': 'saucer'}, {'frequency': 'f', 'id': 930, 'synset': 'sausage.n.01', 'synonyms': ['sausage'], 'def': 'highly seasoned minced meat stuffed in casings', 'name': 'sausage'}, {'frequency': 'r', 'id': 931, 'synset': 'sawhorse.n.01', 'synonyms': ['sawhorse', 'sawbuck'], 'def': 'a framework for holding wood that is being sawed', 'name': 'sawhorse'}, {'frequency': 'r', 'id': 932, 'synset': 'sax.n.02', 'synonyms': ['saxophone'], 'def': "a wind instrument with a `J'-shaped form typically made of brass", 'name': 'saxophone'}, {'frequency': 'f', 'id': 933, 'synset': 'scale.n.07', 'synonyms': ['scale_(measuring_instrument)'], 'def': 'a measuring instrument for weighing; shows amount of mass', 'name': 'scale_(measuring_instrument)'}, {'frequency': 'r', 'id': 934, 'synset': 'scarecrow.n.01', 'synonyms': ['scarecrow', 'strawman'], 'def': 'an effigy in the shape of a man to frighten birds away from seeds', 'name': 'scarecrow'}, {'frequency': 'f', 'id': 935, 'synset': 'scarf.n.01', 'synonyms': ['scarf'], 'def': 'a garment worn around the head or neck or shoulders for warmth or decoration', 'name': 'scarf'}, {'frequency': 'c', 'id': 936, 'synset': 'school_bus.n.01', 'synonyms': ['school_bus'], 'def': 'a bus used to transport children to or from school', 'name': 'school_bus'}, {'frequency': 'f', 'id': 937, 'synset': 'scissors.n.01', 'synonyms': ['scissors'], 'def': 'a tool having two crossed pivoting blades with looped handles', 'name': 'scissors'}, {'frequency': 'c', 'id': 938, 'synset': 'scoreboard.n.01', 'synonyms': ['scoreboard'], 'def': 'a large board for displaying the score of a contest (and some other information)', 'name': 'scoreboard'}, {'frequency': 'c', 'id': 939, 'synset': 'scrambled_eggs.n.01', 'synonyms': ['scrambled_eggs'], 'def': 'eggs beaten and cooked to a soft firm consistency while stirring', 'name': 'scrambled_eggs'}, {'frequency': 'r', 'id': 940, 'synset': 'scraper.n.01', 'synonyms': ['scraper'], 'def': 'any of various hand tools for scraping', 'name': 'scraper'}, {'frequency': 'r', 'id': 941, 'synset': 'scratcher.n.03', 'synonyms': ['scratcher'], 'def': 'a device used for scratching', 'name': 'scratcher'}, {'frequency': 'c', 'id': 942, 'synset': 'screwdriver.n.01', 'synonyms': ['screwdriver'], 'def': 'a hand tool for driving screws; has a tip that fits into the head of a screw', 'name': 'screwdriver'}, {'frequency': 'c', 'id': 943, 'synset': 'scrub_brush.n.01', 'synonyms': ['scrubbing_brush'], 'def': 'a brush with short stiff bristles for heavy cleaning', 'name': 'scrubbing_brush'}, {'frequency': 'c', 'id': 944, 'synset': 'sculpture.n.01', 'synonyms': ['sculpture'], 'def': 'a three-dimensional work of art', 'name': 'sculpture'}, {'frequency': 'r', 'id': 945, 'synset': 'seabird.n.01', 'synonyms': ['seabird', 'seafowl'], 'def': 'a bird that frequents coastal waters and the open ocean: gulls; pelicans; gannets; cormorants; albatrosses; petrels; etc.', 'name': 'seabird'}, {'frequency': 'r', 'id': 946, 'synset': 'seahorse.n.02', 'synonyms': ['seahorse'], 'def': 'small fish with horse-like heads bent sharply downward and curled tails', 'name': 'seahorse'}, {'frequency': 'r', 'id': 947, 'synset': 'seaplane.n.01', 'synonyms': ['seaplane', 'hydroplane'], 'def': 'an airplane that can land on or take off from water', 'name': 'seaplane'}, {'frequency': 'c', 'id': 948, 'synset': 'seashell.n.01', 'synonyms': ['seashell'], 'def': 'the shell of a marine organism', 'name': 'seashell'}, {'frequency': 'r', 'id': 949, 'synset': 'seedling.n.01', 'synonyms': ['seedling'], 'def': 'young plant or tree grown from a seed', 'name': 'seedling'}, {'frequency': 'c', 'id': 950, 'synset': 'serving_dish.n.01', 'synonyms': ['serving_dish'], 'def': 'a dish used for serving food', 'name': 'serving_dish'}, {'frequency': 'r', 'id': 951, 'synset': 'sewing_machine.n.01', 'synonyms': ['sewing_machine'], 'def': 'a textile machine used as a home appliance for sewing', 'name': 'sewing_machine'}, {'frequency': 'r', 'id': 952, 'synset': 'shaker.n.03', 'synonyms': ['shaker'], 'def': 'a container in which something can be shaken', 'name': 'shaker'}, {'frequency': 'c', 'id': 953, 'synset': 'shampoo.n.01', 'synonyms': ['shampoo'], 'def': 'cleansing agent consisting of soaps or detergents used for washing the hair', 'name': 'shampoo'}, {'frequency': 'r', 'id': 954, 'synset': 'shark.n.01', 'synonyms': ['shark'], 'def': 'typically large carnivorous fishes with sharpe teeth', 'name': 'shark'}, {'frequency': 'r', 'id': 955, 'synset': 'sharpener.n.01', 'synonyms': ['sharpener'], 'def': 'any implement that is used to make something (an edge or a point) sharper', 'name': 'sharpener'}, {'frequency': 'r', 'id': 956, 'synset': 'sharpie.n.03', 'synonyms': ['Sharpie'], 'def': 'a pen with indelible ink that will write on any surface', 'name': 'Sharpie'}, {'frequency': 'r', 'id': 957, 'synset': 'shaver.n.03', 'synonyms': ['shaver_(electric)', 'electric_shaver', 'electric_razor'], 'def': 'a razor powered by an electric motor', 'name': 'shaver_(electric)'}, {'frequency': 'c', 'id': 958, 'synset': 'shaving_cream.n.01', 'synonyms': ['shaving_cream', 'shaving_soap'], 'def': 'toiletry consisting that forms a rich lather for softening the beard before shaving', 'name': 'shaving_cream'}, {'frequency': 'r', 'id': 959, 'synset': 'shawl.n.01', 'synonyms': ['shawl'], 'def': 'cloak consisting of an oblong piece of cloth used to cover the head and shoulders', 'name': 'shawl'}, {'frequency': 'r', 'id': 960, 'synset': 'shears.n.01', 'synonyms': ['shears'], 'def': 'large scissors with strong blades', 'name': 'shears'}, {'frequency': 'f', 'id': 961, 'synset': 'sheep.n.01', 'synonyms': ['sheep'], 'def': 'woolly usually horned ruminant mammal related to the goat', 'name': 'sheep'}, {'frequency': 'r', 'id': 962, 'synset': 'shepherd_dog.n.01', 'synonyms': ['shepherd_dog', 'sheepdog'], 'def': 'any of various usually long-haired breeds of dog reared to herd and guard sheep', 'name': 'shepherd_dog'}, {'frequency': 'r', 'id': 963, 'synset': 'sherbert.n.01', 'synonyms': ['sherbert', 'sherbet'], 'def': 'a frozen dessert made primarily of fruit juice and sugar', 'name': 'sherbert'}, {'frequency': 'r', 'id': 964, 'synset': 'shield.n.02', 'synonyms': ['shield'], 'def': 'armor carried on the arm to intercept blows', 'name': 'shield'}, {'frequency': 'f', 'id': 965, 'synset': 'shirt.n.01', 'synonyms': ['shirt'], 'def': 'a garment worn on the upper half of the body', 'name': 'shirt'}, {'frequency': 'f', 'id': 966, 'synset': 'shoe.n.01', 'synonyms': ['shoe', 'sneaker_(type_of_shoe)', 'tennis_shoe'], 'def': 'common footwear covering the foot', 'name': 'shoe'}, {'frequency': 'c', 'id': 967, 'synset': 'shopping_bag.n.01', 'synonyms': ['shopping_bag'], 'def': 'a bag made of plastic or strong paper (often with handles); used to transport goods after shopping', 'name': 'shopping_bag'}, {'frequency': 'c', 'id': 968, 'synset': 'shopping_cart.n.01', 'synonyms': ['shopping_cart'], 'def': 'a handcart that holds groceries or other goods while shopping', 'name': 'shopping_cart'}, {'frequency': 'f', 'id': 969, 'synset': 'short_pants.n.01', 'synonyms': ['short_pants', 'shorts_(clothing)', 'trunks_(clothing)'], 'def': 'trousers that end at or above the knee', 'name': 'short_pants'}, {'frequency': 'r', 'id': 970, 'synset': 'shot_glass.n.01', 'synonyms': ['shot_glass'], 'def': 'a small glass adequate to hold a single swallow of whiskey', 'name': 'shot_glass'}, {'frequency': 'c', 'id': 971, 'synset': 'shoulder_bag.n.01', 'synonyms': ['shoulder_bag'], 'def': 'a large handbag that can be carried by a strap looped over the shoulder', 'name': 'shoulder_bag'}, {'frequency': 'c', 'id': 972, 'synset': 'shovel.n.01', 'synonyms': ['shovel'], 'def': 'a hand tool for lifting loose material such as snow, dirt, etc.', 'name': 'shovel'}, {'frequency': 'f', 'id': 973, 'synset': 'shower.n.01', 'synonyms': ['shower_head'], 'def': 'a plumbing fixture that sprays water over you', 'name': 'shower_head'}, {'frequency': 'f', 'id': 974, 'synset': 'shower_curtain.n.01', 'synonyms': ['shower_curtain'], 'def': 'a curtain that keeps water from splashing out of the shower area', 'name': 'shower_curtain'}, {'frequency': 'r', 'id': 975, 'synset': 'shredder.n.01', 'synonyms': ['shredder_(for_paper)'], 'def': 'a device that shreds documents', 'name': 'shredder_(for_paper)'}, {'frequency': 'r', 'id': 976, 'synset': 'sieve.n.01', 'synonyms': ['sieve', 'screen_(sieve)'], 'def': 'a strainer for separating lumps from powdered material or grading particles', 'name': 'sieve'}, {'frequency': 'f', 'id': 977, 'synset': 'signboard.n.01', 'synonyms': ['signboard'], 'def': 'structure displaying a board on which advertisements can be posted', 'name': 'signboard'}, {'frequency': 'c', 'id': 978, 'synset': 'silo.n.01', 'synonyms': ['silo'], 'def': 'a cylindrical tower used for storing goods', 'name': 'silo'}, {'frequency': 'f', 'id': 979, 'synset': 'sink.n.01', 'synonyms': ['sink'], 'def': 'plumbing fixture consisting of a water basin fixed to a wall or floor and having a drainpipe', 'name': 'sink'}, {'frequency': 'f', 'id': 980, 'synset': 'skateboard.n.01', 'synonyms': ['skateboard'], 'def': 'a board with wheels that is ridden in a standing or crouching position and propelled by foot', 'name': 'skateboard'}, {'frequency': 'c', 'id': 981, 'synset': 'skewer.n.01', 'synonyms': ['skewer'], 'def': 'a long pin for holding meat in position while it is being roasted', 'name': 'skewer'}, {'frequency': 'f', 'id': 982, 'synset': 'ski.n.01', 'synonyms': ['ski'], 'def': 'sports equipment for skiing on snow', 'name': 'ski'}, {'frequency': 'f', 'id': 983, 'synset': 'ski_boot.n.01', 'synonyms': ['ski_boot'], 'def': 'a stiff boot that is fastened to a ski with a ski binding', 'name': 'ski_boot'}, {'frequency': 'f', 'id': 984, 'synset': 'ski_parka.n.01', 'synonyms': ['ski_parka', 'ski_jacket'], 'def': 'a parka to be worn while skiing', 'name': 'ski_parka'}, {'frequency': 'f', 'id': 985, 'synset': 'ski_pole.n.01', 'synonyms': ['ski_pole'], 'def': 'a pole with metal points used as an aid in skiing', 'name': 'ski_pole'}, {'frequency': 'f', 'id': 986, 'synset': 'skirt.n.02', 'synonyms': ['skirt'], 'def': 'a garment hanging from the waist; worn mainly by girls and women', 'name': 'skirt'}, {'frequency': 'c', 'id': 987, 'synset': 'sled.n.01', 'synonyms': ['sled', 'sledge', 'sleigh'], 'def': 'a vehicle or flat object for transportation over snow by sliding or pulled by dogs, etc.', 'name': 'sled'}, {'frequency': 'c', 'id': 988, 'synset': 'sleeping_bag.n.01', 'synonyms': ['sleeping_bag'], 'def': 'large padded bag designed to be slept in outdoors', 'name': 'sleeping_bag'}, {'frequency': 'r', 'id': 989, 'synset': 'sling.n.05', 'synonyms': ['sling_(bandage)', 'triangular_bandage'], 'def': 'bandage to support an injured forearm; slung over the shoulder or neck', 'name': 'sling_(bandage)'}, {'frequency': 'c', 'id': 990, 'synset': 'slipper.n.01', 'synonyms': ['slipper_(footwear)', 'carpet_slipper_(footwear)'], 'def': 'low footwear that can be slipped on and off easily; usually worn indoors', 'name': 'slipper_(footwear)'}, {'frequency': 'r', 'id': 991, 'synset': 'smoothie.n.02', 'synonyms': ['smoothie'], 'def': 'a thick smooth drink consisting of fresh fruit pureed with ice cream or yoghurt or milk', 'name': 'smoothie'}, {'frequency': 'r', 'id': 992, 'synset': 'snake.n.01', 'synonyms': ['snake', 'serpent'], 'def': 'limbless scaly elongate reptile; some are venomous', 'name': 'snake'}, {'frequency': 'f', 'id': 993, 'synset': 'snowboard.n.01', 'synonyms': ['snowboard'], 'def': 'a board that resembles a broad ski or a small surfboard; used in a standing position to slide down snow-covered slopes', 'name': 'snowboard'}, {'frequency': 'c', 'id': 994, 'synset': 'snowman.n.01', 'synonyms': ['snowman'], 'def': 'a figure of a person made of packed snow', 'name': 'snowman'}, {'frequency': 'c', 'id': 995, 'synset': 'snowmobile.n.01', 'synonyms': ['snowmobile'], 'def': 'tracked vehicle for travel on snow having skis in front', 'name': 'snowmobile'}, {'frequency': 'f', 'id': 996, 'synset': 'soap.n.01', 'synonyms': ['soap'], 'def': 'a cleansing agent made from the salts of vegetable or animal fats', 'name': 'soap'}, {'frequency': 'f', 'id': 997, 'synset': 'soccer_ball.n.01', 'synonyms': ['soccer_ball'], 'def': "an inflated ball used in playing soccer (called `football' outside of the United States)", 'name': 'soccer_ball'}, {'frequency': 'f', 'id': 998, 'synset': 'sock.n.01', 'synonyms': ['sock'], 'def': 'cloth covering for the foot; worn inside the shoe; reaches to between the ankle and the knee', 'name': 'sock'}, {'frequency': 'r', 'id': 999, 'synset': 'soda_fountain.n.02', 'synonyms': ['soda_fountain'], 'def': 'an apparatus for dispensing soda water', 'name': 'soda_fountain'}, {'frequency': 'r', 'id': 1000, 'synset': 'soda_water.n.01', 'synonyms': ['carbonated_water', 'club_soda', 'seltzer', 'sparkling_water'], 'def': 'effervescent beverage artificially charged with carbon dioxide', 'name': 'carbonated_water'}, {'frequency': 'f', 'id': 1001, 'synset': 'sofa.n.01', 'synonyms': ['sofa', 'couch', 'lounge'], 'def': 'an upholstered seat for more than one person', 'name': 'sofa'}, {'frequency': 'r', 'id': 1002, 'synset': 'softball.n.01', 'synonyms': ['softball'], 'def': 'ball used in playing softball', 'name': 'softball'}, {'frequency': 'c', 'id': 1003, 'synset': 'solar_array.n.01', 'synonyms': ['solar_array', 'solar_battery', 'solar_panel'], 'def': 'electrical device consisting of a large array of connected solar cells', 'name': 'solar_array'}, {'frequency': 'r', 'id': 1004, 'synset': 'sombrero.n.02', 'synonyms': ['sombrero'], 'def': 'a straw hat with a tall crown and broad brim; worn in American southwest and in Mexico', 'name': 'sombrero'}, {'frequency': 'c', 'id': 1005, 'synset': 'soup.n.01', 'synonyms': ['soup'], 'def': 'liquid food especially of meat or fish or vegetable stock often containing pieces of solid food', 'name': 'soup'}, {'frequency': 'r', 'id': 1006, 'synset': 'soup_bowl.n.01', 'synonyms': ['soup_bowl'], 'def': 'a bowl for serving soup', 'name': 'soup_bowl'}, {'frequency': 'c', 'id': 1007, 'synset': 'soupspoon.n.01', 'synonyms': ['soupspoon'], 'def': 'a spoon with a rounded bowl for eating soup', 'name': 'soupspoon'}, {'frequency': 'c', 'id': 1008, 'synset': 'sour_cream.n.01', 'synonyms': ['sour_cream', 'soured_cream'], 'def': 'soured light cream', 'name': 'sour_cream'}, {'frequency': 'r', 'id': 1009, 'synset': 'soya_milk.n.01', 'synonyms': ['soya_milk', 'soybean_milk', 'soymilk'], 'def': 'a milk substitute containing soybean flour and water; used in some infant formulas and in making tofu', 'name': 'soya_milk'}, {'frequency': 'r', 'id': 1010, 'synset': 'space_shuttle.n.01', 'synonyms': ['space_shuttle'], 'def': "a reusable spacecraft with wings for a controlled descent through the Earth's atmosphere", 'name': 'space_shuttle'}, {'frequency': 'r', 'id': 1011, 'synset': 'sparkler.n.02', 'synonyms': ['sparkler_(fireworks)'], 'def': 'a firework that burns slowly and throws out a shower of sparks', 'name': 'sparkler_(fireworks)'}, {'frequency': 'f', 'id': 1012, 'synset': 'spatula.n.02', 'synonyms': ['spatula'], 'def': 'a hand tool with a thin flexible blade used to mix or spread soft substances', 'name': 'spatula'}, {'frequency': 'r', 'id': 1013, 'synset': 'spear.n.01', 'synonyms': ['spear', 'lance'], 'def': 'a long pointed rod used as a tool or weapon', 'name': 'spear'}, {'frequency': 'f', 'id': 1014, 'synset': 'spectacles.n.01', 'synonyms': ['spectacles', 'specs', 'eyeglasses', 'glasses'], 'def': 'optical instrument consisting of a frame that holds a pair of lenses for correcting defective vision', 'name': 'spectacles'}, {'frequency': 'c', 'id': 1015, 'synset': 'spice_rack.n.01', 'synonyms': ['spice_rack'], 'def': 'a rack for displaying containers filled with spices', 'name': 'spice_rack'}, {'frequency': 'r', 'id': 1016, 'synset': 'spider.n.01', 'synonyms': ['spider'], 'def': 'predatory arachnid with eight legs, two poison fangs, two feelers, and usually two silk-spinning organs at the back end of the body', 'name': 'spider'}, {'frequency': 'c', 'id': 1017, 'synset': 'sponge.n.01', 'synonyms': ['sponge'], 'def': 'a porous mass usable to absorb water typically used for cleaning', 'name': 'sponge'}, {'frequency': 'f', 'id': 1018, 'synset': 'spoon.n.01', 'synonyms': ['spoon'], 'def': 'a piece of cutlery with a shallow bowl-shaped container and a handle', 'name': 'spoon'}, {'frequency': 'c', 'id': 1019, 'synset': 'sportswear.n.01', 'synonyms': ['sportswear', 'athletic_wear', 'activewear'], 'def': 'attire worn for sport or for casual wear', 'name': 'sportswear'}, {'frequency': 'c', 'id': 1020, 'synset': 'spotlight.n.02', 'synonyms': ['spotlight'], 'def': 'a lamp that produces a strong beam of light to illuminate a restricted area; used to focus attention of a stage performer', 'name': 'spotlight'}, {'frequency': 'r', 'id': 1021, 'synset': 'squirrel.n.01', 'synonyms': ['squirrel'], 'def': 'a kind of arboreal rodent having a long bushy tail', 'name': 'squirrel'}, {'frequency': 'c', 'id': 1022, 'synset': 'stapler.n.01', 'synonyms': ['stapler_(stapling_machine)'], 'def': 'a machine that inserts staples into sheets of paper in order to fasten them together', 'name': 'stapler_(stapling_machine)'}, {'frequency': 'r', 'id': 1023, 'synset': 'starfish.n.01', 'synonyms': ['starfish', 'sea_star'], 'def': 'echinoderms characterized by five arms extending from a central disk', 'name': 'starfish'}, {'frequency': 'f', 'id': 1024, 'synset': 'statue.n.01', 'synonyms': ['statue_(sculpture)'], 'def': 'a sculpture representing a human or animal', 'name': 'statue_(sculpture)'}, {'frequency': 'c', 'id': 1025, 'synset': 'steak.n.01', 'synonyms': ['steak_(food)'], 'def': 'a slice of meat cut from the fleshy part of an animal or large fish', 'name': 'steak_(food)'}, {'frequency': 'r', 'id': 1026, 'synset': 'steak_knife.n.01', 'synonyms': ['steak_knife'], 'def': 'a sharp table knife used in eating steak', 'name': 'steak_knife'}, {'frequency': 'r', 'id': 1027, 'synset': 'steamer.n.02', 'synonyms': ['steamer_(kitchen_appliance)'], 'def': 'a cooking utensil that can be used to cook food by steaming it', 'name': 'steamer_(kitchen_appliance)'}, {'frequency': 'f', 'id': 1028, 'synset': 'steering_wheel.n.01', 'synonyms': ['steering_wheel'], 'def': 'a handwheel that is used for steering', 'name': 'steering_wheel'}, {'frequency': 'r', 'id': 1029, 'synset': 'stencil.n.01', 'synonyms': ['stencil'], 'def': 'a sheet of material (metal, plastic, etc.) that has been perforated with a pattern; ink or paint can pass through the perforations to create the printed pattern on the surface below', 'name': 'stencil'}, {'frequency': 'r', 'id': 1030, 'synset': 'step_ladder.n.01', 'synonyms': ['stepladder'], 'def': 'a folding portable ladder hinged at the top', 'name': 'stepladder'}, {'frequency': 'c', 'id': 1031, 'synset': 'step_stool.n.01', 'synonyms': ['step_stool'], 'def': 'a stool that has one or two steps that fold under the seat', 'name': 'step_stool'}, {'frequency': 'c', 'id': 1032, 'synset': 'stereo.n.01', 'synonyms': ['stereo_(sound_system)'], 'def': 'electronic device for playing audio', 'name': 'stereo_(sound_system)'}, {'frequency': 'r', 'id': 1033, 'synset': 'stew.n.02', 'synonyms': ['stew'], 'def': 'food prepared by stewing especially meat or fish with vegetables', 'name': 'stew'}, {'frequency': 'r', 'id': 1034, 'synset': 'stirrer.n.02', 'synonyms': ['stirrer'], 'def': 'an implement used for stirring', 'name': 'stirrer'}, {'frequency': 'f', 'id': 1035, 'synset': 'stirrup.n.01', 'synonyms': ['stirrup'], 'def': "support consisting of metal loops into which rider's feet go", 'name': 'stirrup'}, {'frequency': 'c', 'id': 1036, 'synset': 'stocking.n.01', 'synonyms': ['stockings_(leg_wear)'], 'def': 'close-fitting hosiery to cover the foot and leg; come in matched pairs', 'name': 'stockings_(leg_wear)'}, {'frequency': 'f', 'id': 1037, 'synset': 'stool.n.01', 'synonyms': ['stool'], 'def': 'a simple seat without a back or arms', 'name': 'stool'}, {'frequency': 'f', 'id': 1038, 'synset': 'stop_sign.n.01', 'synonyms': ['stop_sign'], 'def': 'a traffic sign to notify drivers that they must come to a complete stop', 'name': 'stop_sign'}, {'frequency': 'f', 'id': 1039, 'synset': 'stoplight.n.01', 'synonyms': ['brake_light'], 'def': 'a red light on the rear of a motor vehicle that signals when the brakes are applied', 'name': 'brake_light'}, {'frequency': 'f', 'id': 1040, 'synset': 'stove.n.01', 'synonyms': ['stove', 'kitchen_stove', 'range_(kitchen_appliance)', 'kitchen_range', 'cooking_stove'], 'def': 'a kitchen appliance used for cooking food', 'name': 'stove'}, {'frequency': 'c', 'id': 1041, 'synset': 'strainer.n.01', 'synonyms': ['strainer'], 'def': 'a filter to retain larger pieces while smaller pieces and liquids pass through', 'name': 'strainer'}, {'frequency': 'f', 'id': 1042, 'synset': 'strap.n.01', 'synonyms': ['strap'], 'def': 'an elongated strip of material for binding things together or holding', 'name': 'strap'}, {'frequency': 'f', 'id': 1043, 'synset': 'straw.n.04', 'synonyms': ['straw_(for_drinking)', 'drinking_straw'], 'def': 'a thin paper or plastic tube used to suck liquids into the mouth', 'name': 'straw_(for_drinking)'}, {'frequency': 'f', 'id': 1044, 'synset': 'strawberry.n.01', 'synonyms': ['strawberry'], 'def': 'sweet fleshy red fruit', 'name': 'strawberry'}, {'frequency': 'f', 'id': 1045, 'synset': 'street_sign.n.01', 'synonyms': ['street_sign'], 'def': 'a sign visible from the street', 'name': 'street_sign'}, {'frequency': 'f', 'id': 1046, 'synset': 'streetlight.n.01', 'synonyms': ['streetlight', 'street_lamp'], 'def': 'a lamp supported on a lamppost; for illuminating a street', 'name': 'streetlight'}, {'frequency': 'r', 'id': 1047, 'synset': 'string_cheese.n.01', 'synonyms': ['string_cheese'], 'def': 'cheese formed in long strings twisted together', 'name': 'string_cheese'}, {'frequency': 'r', 'id': 1048, 'synset': 'stylus.n.02', 'synonyms': ['stylus'], 'def': 'a pointed tool for writing or drawing or engraving', 'name': 'stylus'}, {'frequency': 'r', 'id': 1049, 'synset': 'subwoofer.n.01', 'synonyms': ['subwoofer'], 'def': 'a loudspeaker that is designed to reproduce very low bass frequencies', 'name': 'subwoofer'}, {'frequency': 'r', 'id': 1050, 'synset': 'sugar_bowl.n.01', 'synonyms': ['sugar_bowl'], 'def': 'a dish in which sugar is served', 'name': 'sugar_bowl'}, {'frequency': 'r', 'id': 1051, 'synset': 'sugarcane.n.01', 'synonyms': ['sugarcane_(plant)'], 'def': 'juicy canes whose sap is a source of molasses and commercial sugar; fresh canes are sometimes chewed for the juice', 'name': 'sugarcane_(plant)'}, {'frequency': 'c', 'id': 1052, 'synset': 'suit.n.01', 'synonyms': ['suit_(clothing)'], 'def': 'a set of garments (usually including a jacket and trousers or skirt) for outerwear all of the same fabric and color', 'name': 'suit_(clothing)'}, {'frequency': 'c', 'id': 1053, 'synset': 'sunflower.n.01', 'synonyms': ['sunflower'], 'def': 'any plant of the genus Helianthus having large flower heads with dark disk florets and showy yellow rays', 'name': 'sunflower'}, {'frequency': 'f', 'id': 1054, 'synset': 'sunglasses.n.01', 'synonyms': ['sunglasses'], 'def': 'spectacles that are darkened or polarized to protect the eyes from the glare of the sun', 'name': 'sunglasses'}, {'frequency': 'c', 'id': 1055, 'synset': 'sunhat.n.01', 'synonyms': ['sunhat'], 'def': 'a hat with a broad brim that protects the face from direct exposure to the sun', 'name': 'sunhat'}, {'frequency': 'r', 'id': 1056, 'synset': 'sunscreen.n.01', 'synonyms': ['sunscreen', 'sunblock'], 'def': 'a cream spread on the skin; contains a chemical to filter out ultraviolet light and so protect from sunburn', 'name': 'sunscreen'}, {'frequency': 'f', 'id': 1057, 'synset': 'surfboard.n.01', 'synonyms': ['surfboard'], 'def': 'a narrow buoyant board for riding surf', 'name': 'surfboard'}, {'frequency': 'c', 'id': 1058, 'synset': 'sushi.n.01', 'synonyms': ['sushi'], 'def': 'rice (with raw fish) wrapped in seaweed', 'name': 'sushi'}, {'frequency': 'c', 'id': 1059, 'synset': 'swab.n.02', 'synonyms': ['mop'], 'def': 'cleaning implement consisting of absorbent material fastened to a handle; for cleaning floors', 'name': 'mop'}, {'frequency': 'c', 'id': 1060, 'synset': 'sweat_pants.n.01', 'synonyms': ['sweat_pants'], 'def': 'loose-fitting trousers with elastic cuffs; worn by athletes', 'name': 'sweat_pants'}, {'frequency': 'c', 'id': 1061, 'synset': 'sweatband.n.02', 'synonyms': ['sweatband'], 'def': 'a band of material tied around the forehead or wrist to absorb sweat', 'name': 'sweatband'}, {'frequency': 'f', 'id': 1062, 'synset': 'sweater.n.01', 'synonyms': ['sweater'], 'def': 'a crocheted or knitted garment covering the upper part of the body', 'name': 'sweater'}, {'frequency': 'f', 'id': 1063, 'synset': 'sweatshirt.n.01', 'synonyms': ['sweatshirt'], 'def': 'cotton knit pullover with long sleeves worn during athletic activity', 'name': 'sweatshirt'}, {'frequency': 'c', 'id': 1064, 'synset': 'sweet_potato.n.02', 'synonyms': ['sweet_potato'], 'def': 'the edible tuberous root of the sweet potato vine', 'name': 'sweet_potato'}, {'frequency': 'f', 'id': 1065, 'synset': 'swimsuit.n.01', 'synonyms': ['swimsuit', 'swimwear', 'bathing_suit', 'swimming_costume', 'bathing_costume', 'swimming_trunks', 'bathing_trunks'], 'def': 'garment worn for swimming', 'name': 'swimsuit'}, {'frequency': 'c', 'id': 1066, 'synset': 'sword.n.01', 'synonyms': ['sword'], 'def': 'a cutting or thrusting weapon that has a long metal blade', 'name': 'sword'}, {'frequency': 'r', 'id': 1067, 'synset': 'syringe.n.01', 'synonyms': ['syringe'], 'def': 'a medical instrument used to inject or withdraw fluids', 'name': 'syringe'}, {'frequency': 'r', 'id': 1068, 'synset': 'tabasco.n.02', 'synonyms': ['Tabasco_sauce'], 'def': 'very spicy sauce (trade name Tabasco) made from fully-aged red peppers', 'name': 'Tabasco_sauce'}, {'frequency': 'r', 'id': 1069, 'synset': 'table-tennis_table.n.01', 'synonyms': ['table-tennis_table', 'ping-pong_table'], 'def': 'a table used for playing table tennis', 'name': 'table-tennis_table'}, {'frequency': 'f', 'id': 1070, 'synset': 'table.n.02', 'synonyms': ['table'], 'def': 'a piece of furniture having a smooth flat top that is usually supported by one or more vertical legs', 'name': 'table'}, {'frequency': 'c', 'id': 1071, 'synset': 'table_lamp.n.01', 'synonyms': ['table_lamp'], 'def': 'a lamp that sits on a table', 'name': 'table_lamp'}, {'frequency': 'f', 'id': 1072, 'synset': 'tablecloth.n.01', 'synonyms': ['tablecloth'], 'def': 'a covering spread over a dining table', 'name': 'tablecloth'}, {'frequency': 'r', 'id': 1073, 'synset': 'tachometer.n.01', 'synonyms': ['tachometer'], 'def': 'measuring instrument for indicating speed of rotation', 'name': 'tachometer'}, {'frequency': 'r', 'id': 1074, 'synset': 'taco.n.02', 'synonyms': ['taco'], 'def': 'a small tortilla cupped around a filling', 'name': 'taco'}, {'frequency': 'f', 'id': 1075, 'synset': 'tag.n.02', 'synonyms': ['tag'], 'def': 'a label associated with something for the purpose of identification or information', 'name': 'tag'}, {'frequency': 'f', 'id': 1076, 'synset': 'taillight.n.01', 'synonyms': ['taillight', 'rear_light'], 'def': 'lamp (usually red) mounted at the rear of a motor vehicle', 'name': 'taillight'}, {'frequency': 'r', 'id': 1077, 'synset': 'tambourine.n.01', 'synonyms': ['tambourine'], 'def': 'a shallow drum with a single drumhead and with metallic disks in the sides', 'name': 'tambourine'}, {'frequency': 'r', 'id': 1078, 'synset': 'tank.n.01', 'synonyms': ['army_tank', 'armored_combat_vehicle', 'armoured_combat_vehicle'], 'def': 'an enclosed armored military vehicle; has a cannon and moves on caterpillar treads', 'name': 'army_tank'}, {'frequency': 'c', 'id': 1079, 'synset': 'tank.n.02', 'synonyms': ['tank_(storage_vessel)', 'storage_tank'], 'def': 'a large (usually metallic) vessel for holding gases or liquids', 'name': 'tank_(storage_vessel)'}, {'frequency': 'f', 'id': 1080, 'synset': 'tank_top.n.01', 'synonyms': ['tank_top_(clothing)'], 'def': 'a tight-fitting sleeveless shirt with wide shoulder straps and low neck and no front opening', 'name': 'tank_top_(clothing)'}, {'frequency': 'c', 'id': 1081, 'synset': 'tape.n.01', 'synonyms': ['tape_(sticky_cloth_or_paper)'], 'def': 'a long thin piece of cloth or paper as used for binding or fastening', 'name': 'tape_(sticky_cloth_or_paper)'}, {'frequency': 'c', 'id': 1082, 'synset': 'tape.n.04', 'synonyms': ['tape_measure', 'measuring_tape'], 'def': 'measuring instrument consisting of a narrow strip (cloth or metal) marked in inches or centimeters and used for measuring lengths', 'name': 'tape_measure'}, {'frequency': 'c', 'id': 1083, 'synset': 'tapestry.n.02', 'synonyms': ['tapestry'], 'def': 'a heavy textile with a woven design; used for curtains and upholstery', 'name': 'tapestry'}, {'frequency': 'f', 'id': 1084, 'synset': 'tarpaulin.n.01', 'synonyms': ['tarp'], 'def': 'waterproofed canvas', 'name': 'tarp'}, {'frequency': 'c', 'id': 1085, 'synset': 'tartan.n.01', 'synonyms': ['tartan', 'plaid'], 'def': 'a cloth having a crisscross design', 'name': 'tartan'}, {'frequency': 'c', 'id': 1086, 'synset': 'tassel.n.01', 'synonyms': ['tassel'], 'def': 'adornment consisting of a bunch of cords fastened at one end', 'name': 'tassel'}, {'frequency': 'r', 'id': 1087, 'synset': 'tea_bag.n.01', 'synonyms': ['tea_bag'], 'def': 'a measured amount of tea in a bag for an individual serving of tea', 'name': 'tea_bag'}, {'frequency': 'c', 'id': 1088, 'synset': 'teacup.n.02', 'synonyms': ['teacup'], 'def': 'a cup from which tea is drunk', 'name': 'teacup'}, {'frequency': 'c', 'id': 1089, 'synset': 'teakettle.n.01', 'synonyms': ['teakettle'], 'def': 'kettle for boiling water to make tea', 'name': 'teakettle'}, {'frequency': 'c', 'id': 1090, 'synset': 'teapot.n.01', 'synonyms': ['teapot'], 'def': 'pot for brewing tea; usually has a spout and handle', 'name': 'teapot'}, {'frequency': 'f', 'id': 1091, 'synset': 'teddy.n.01', 'synonyms': ['teddy_bear'], 'def': "plaything consisting of a child's toy bear (usually plush and stuffed with soft materials)", 'name': 'teddy_bear'}, {'frequency': 'f', 'id': 1092, 'synset': 'telephone.n.01', 'synonyms': ['telephone', 'phone', 'telephone_set'], 'def': 'electronic device for communicating by voice over long distances', 'name': 'telephone'}, {'frequency': 'c', 'id': 1093, 'synset': 'telephone_booth.n.01', 'synonyms': ['telephone_booth', 'phone_booth', 'call_box', 'telephone_box', 'telephone_kiosk'], 'def': 'booth for using a telephone', 'name': 'telephone_booth'}, {'frequency': 'f', 'id': 1094, 'synset': 'telephone_pole.n.01', 'synonyms': ['telephone_pole', 'telegraph_pole', 'telegraph_post'], 'def': 'tall pole supporting telephone wires', 'name': 'telephone_pole'}, {'frequency': 'r', 'id': 1095, 'synset': 'telephoto_lens.n.01', 'synonyms': ['telephoto_lens', 'zoom_lens'], 'def': 'a camera lens that magnifies the image', 'name': 'telephoto_lens'}, {'frequency': 'c', 'id': 1096, 'synset': 'television_camera.n.01', 'synonyms': ['television_camera', 'tv_camera'], 'def': 'television equipment for capturing and recording video', 'name': 'television_camera'}, {'frequency': 'f', 'id': 1097, 'synset': 'television_receiver.n.01', 'synonyms': ['television_set', 'tv', 'tv_set'], 'def': 'an electronic device that receives television signals and displays them on a screen', 'name': 'television_set'}, {'frequency': 'f', 'id': 1098, 'synset': 'tennis_ball.n.01', 'synonyms': ['tennis_ball'], 'def': 'ball about the size of a fist used in playing tennis', 'name': 'tennis_ball'}, {'frequency': 'f', 'id': 1099, 'synset': 'tennis_racket.n.01', 'synonyms': ['tennis_racket'], 'def': 'a racket used to play tennis', 'name': 'tennis_racket'}, {'frequency': 'r', 'id': 1100, 'synset': 'tequila.n.01', 'synonyms': ['tequila'], 'def': 'Mexican liquor made from fermented juices of an agave plant', 'name': 'tequila'}, {'frequency': 'c', 'id': 1101, 'synset': 'thermometer.n.01', 'synonyms': ['thermometer'], 'def': 'measuring instrument for measuring temperature', 'name': 'thermometer'}, {'frequency': 'c', 'id': 1102, 'synset': 'thermos.n.01', 'synonyms': ['thermos_bottle'], 'def': 'vacuum flask that preserves temperature of hot or cold drinks', 'name': 'thermos_bottle'}, {'frequency': 'c', 'id': 1103, 'synset': 'thermostat.n.01', 'synonyms': ['thermostat'], 'def': 'a regulator for automatically regulating temperature by starting or stopping the supply of heat', 'name': 'thermostat'}, {'frequency': 'r', 'id': 1104, 'synset': 'thimble.n.02', 'synonyms': ['thimble'], 'def': 'a small metal cap to protect the finger while sewing; can be used as a small container', 'name': 'thimble'}, {'frequency': 'c', 'id': 1105, 'synset': 'thread.n.01', 'synonyms': ['thread', 'yarn'], 'def': 'a fine cord of twisted fibers (of cotton or silk or wool or nylon etc.) used in sewing and weaving', 'name': 'thread'}, {'frequency': 'c', 'id': 1106, 'synset': 'thumbtack.n.01', 'synonyms': ['thumbtack', 'drawing_pin', 'pushpin'], 'def': 'a tack for attaching papers to a bulletin board or drawing board', 'name': 'thumbtack'}, {'frequency': 'c', 'id': 1107, 'synset': 'tiara.n.01', 'synonyms': ['tiara'], 'def': 'a jeweled headdress worn by women on formal occasions', 'name': 'tiara'}, {'frequency': 'c', 'id': 1108, 'synset': 'tiger.n.02', 'synonyms': ['tiger'], 'def': 'large feline of forests in most of Asia having a tawny coat with black stripes', 'name': 'tiger'}, {'frequency': 'c', 'id': 1109, 'synset': 'tights.n.01', 'synonyms': ['tights_(clothing)', 'leotards'], 'def': 'skintight knit hose covering the body from the waist to the feet worn by acrobats and dancers and as stockings by women and girls', 'name': 'tights_(clothing)'}, {'frequency': 'c', 'id': 1110, 'synset': 'timer.n.01', 'synonyms': ['timer', 'stopwatch'], 'def': 'a timepiece that measures a time interval and signals its end', 'name': 'timer'}, {'frequency': 'f', 'id': 1111, 'synset': 'tinfoil.n.01', 'synonyms': ['tinfoil'], 'def': 'foil made of tin or an alloy of tin and lead', 'name': 'tinfoil'}, {'frequency': 'r', 'id': 1112, 'synset': 'tinsel.n.01', 'synonyms': ['tinsel'], 'def': 'a showy decoration that is basically valueless', 'name': 'tinsel'}, {'frequency': 'f', 'id': 1113, 'synset': 'tissue.n.02', 'synonyms': ['tissue_paper'], 'def': 'a soft thin (usually translucent) paper', 'name': 'tissue_paper'}, {'frequency': 'c', 'id': 1114, 'synset': 'toast.n.01', 'synonyms': ['toast_(food)'], 'def': 'slice of bread that has been toasted', 'name': 'toast_(food)'}, {'frequency': 'f', 'id': 1115, 'synset': 'toaster.n.02', 'synonyms': ['toaster'], 'def': 'a kitchen appliance (usually electric) for toasting bread', 'name': 'toaster'}, {'frequency': 'c', 'id': 1116, 'synset': 'toaster_oven.n.01', 'synonyms': ['toaster_oven'], 'def': 'kitchen appliance consisting of a small electric oven for toasting or warming food', 'name': 'toaster_oven'}, {'frequency': 'f', 'id': 1117, 'synset': 'toilet.n.02', 'synonyms': ['toilet'], 'def': 'a plumbing fixture for defecation and urination', 'name': 'toilet'}, {'frequency': 'f', 'id': 1118, 'synset': 'toilet_tissue.n.01', 'synonyms': ['toilet_tissue', 'toilet_paper', 'bathroom_tissue'], 'def': 'a soft thin absorbent paper for use in toilets', 'name': 'toilet_tissue'}, {'frequency': 'f', 'id': 1119, 'synset': 'tomato.n.01', 'synonyms': ['tomato'], 'def': 'mildly acid red or yellow pulpy fruit eaten as a vegetable', 'name': 'tomato'}, {'frequency': 'c', 'id': 1120, 'synset': 'tongs.n.01', 'synonyms': ['tongs'], 'def': 'any of various devices for taking hold of objects; usually have two hinged legs with handles above and pointed hooks below', 'name': 'tongs'}, {'frequency': 'c', 'id': 1121, 'synset': 'toolbox.n.01', 'synonyms': ['toolbox'], 'def': 'a box or chest or cabinet for holding hand tools', 'name': 'toolbox'}, {'frequency': 'f', 'id': 1122, 'synset': 'toothbrush.n.01', 'synonyms': ['toothbrush'], 'def': 'small brush; has long handle; used to clean teeth', 'name': 'toothbrush'}, {'frequency': 'f', 'id': 1123, 'synset': 'toothpaste.n.01', 'synonyms': ['toothpaste'], 'def': 'a dentifrice in the form of a paste', 'name': 'toothpaste'}, {'frequency': 'c', 'id': 1124, 'synset': 'toothpick.n.01', 'synonyms': ['toothpick'], 'def': 'pick consisting of a small strip of wood or plastic; used to pick food from between the teeth', 'name': 'toothpick'}, {'frequency': 'c', 'id': 1125, 'synset': 'top.n.09', 'synonyms': ['cover'], 'def': 'covering for a hole (especially a hole in the top of a container)', 'name': 'cover'}, {'frequency': 'c', 'id': 1126, 'synset': 'tortilla.n.01', 'synonyms': ['tortilla'], 'def': 'thin unleavened pancake made from cornmeal or wheat flour', 'name': 'tortilla'}, {'frequency': 'c', 'id': 1127, 'synset': 'tow_truck.n.01', 'synonyms': ['tow_truck'], 'def': 'a truck equipped to hoist and pull wrecked cars (or to remove cars from no-parking zones)', 'name': 'tow_truck'}, {'frequency': 'f', 'id': 1128, 'synset': 'towel.n.01', 'synonyms': ['towel'], 'def': 'a rectangular piece of absorbent cloth (or paper) for drying or wiping', 'name': 'towel'}, {'frequency': 'f', 'id': 1129, 'synset': 'towel_rack.n.01', 'synonyms': ['towel_rack', 'towel_rail', 'towel_bar'], 'def': 'a rack consisting of one or more bars on which towels can be hung', 'name': 'towel_rack'}, {'frequency': 'f', 'id': 1130, 'synset': 'toy.n.03', 'synonyms': ['toy'], 'def': 'a device regarded as providing amusement', 'name': 'toy'}, {'frequency': 'c', 'id': 1131, 'synset': 'tractor.n.01', 'synonyms': ['tractor_(farm_equipment)'], 'def': 'a wheeled vehicle with large wheels; used in farming and other applications', 'name': 'tractor_(farm_equipment)'}, {'frequency': 'f', 'id': 1132, 'synset': 'traffic_light.n.01', 'synonyms': ['traffic_light'], 'def': 'a device to control vehicle traffic often consisting of three or more lights', 'name': 'traffic_light'}, {'frequency': 'r', 'id': 1133, 'synset': 'trail_bike.n.01', 'synonyms': ['dirt_bike'], 'def': 'a lightweight motorcycle equipped with rugged tires and suspension for off-road use', 'name': 'dirt_bike'}, {'frequency': 'c', 'id': 1134, 'synset': 'trailer_truck.n.01', 'synonyms': ['trailer_truck', 'tractor_trailer', 'trucking_rig', 'articulated_lorry', 'semi_truck'], 'def': 'a truck consisting of a tractor and trailer together', 'name': 'trailer_truck'}, {'frequency': 'f', 'id': 1135, 'synset': 'train.n.01', 'synonyms': ['train_(railroad_vehicle)', 'railroad_train'], 'def': 'public or private transport provided by a line of railway cars coupled together and drawn by a locomotive', 'name': 'train_(railroad_vehicle)'}, {'frequency': 'r', 'id': 1136, 'synset': 'trampoline.n.01', 'synonyms': ['trampoline'], 'def': 'gymnastic apparatus consisting of a strong canvas sheet attached with springs to a metal frame', 'name': 'trampoline'}, {'frequency': 'f', 'id': 1137, 'synset': 'tray.n.01', 'synonyms': ['tray'], 'def': 'an open receptacle for holding or displaying or serving articles or food', 'name': 'tray'}, {'frequency': 'r', 'id': 1138, 'synset': 'tree_house.n.01', 'synonyms': ['tree_house'], 'def': '(NOT A TREE) a PLAYHOUSE built in the branches of a tree', 'name': 'tree_house'}, {'frequency': 'r', 'id': 1139, 'synset': 'trench_coat.n.01', 'synonyms': ['trench_coat'], 'def': 'a military style raincoat; belted with deep pockets', 'name': 'trench_coat'}, {'frequency': 'r', 'id': 1140, 'synset': 'triangle.n.05', 'synonyms': ['triangle_(musical_instrument)'], 'def': 'a percussion instrument consisting of a metal bar bent in the shape of an open triangle', 'name': 'triangle_(musical_instrument)'}, {'frequency': 'r', 'id': 1141, 'synset': 'tricycle.n.01', 'synonyms': ['tricycle'], 'def': 'a vehicle with three wheels that is moved by foot pedals', 'name': 'tricycle'}, {'frequency': 'c', 'id': 1142, 'synset': 'tripod.n.01', 'synonyms': ['tripod'], 'def': 'a three-legged rack used for support', 'name': 'tripod'}, {'frequency': 'f', 'id': 1143, 'synset': 'trouser.n.01', 'synonyms': ['trousers', 'pants_(clothing)'], 'def': 'a garment extending from the waist to the knee or ankle, covering each leg separately', 'name': 'trousers'}, {'frequency': 'f', 'id': 1144, 'synset': 'truck.n.01', 'synonyms': ['truck'], 'def': 'an automotive vehicle suitable for hauling', 'name': 'truck'}, {'frequency': 'r', 'id': 1145, 'synset': 'truffle.n.03', 'synonyms': ['truffle_(chocolate)', 'chocolate_truffle'], 'def': 'creamy chocolate candy', 'name': 'truffle_(chocolate)'}, {'frequency': 'c', 'id': 1146, 'synset': 'trunk.n.02', 'synonyms': ['trunk'], 'def': 'luggage consisting of a large strong case used when traveling or for storage', 'name': 'trunk'}, {'frequency': 'r', 'id': 1147, 'synset': 'tub.n.02', 'synonyms': ['vat'], 'def': 'a large open vessel for holding or storing liquids', 'name': 'vat'}, {'frequency': 'c', 'id': 1148, 'synset': 'turban.n.01', 'synonyms': ['turban'], 'def': 'a traditional headdress consisting of a long scarf wrapped around the head', 'name': 'turban'}, {'frequency': 'r', 'id': 1149, 'synset': 'turkey.n.01', 'synonyms': ['turkey_(bird)'], 'def': 'large gallinaceous bird with fan-shaped tail; widely domesticated for food', 'name': 'turkey_(bird)'}, {'frequency': 'c', 'id': 1150, 'synset': 'turkey.n.04', 'synonyms': ['turkey_(food)'], 'def': 'flesh of large domesticated fowl usually roasted', 'name': 'turkey_(food)'}, {'frequency': 'r', 'id': 1151, 'synset': 'turnip.n.01', 'synonyms': ['turnip'], 'def': 'widely cultivated plant having a large fleshy edible white or yellow root', 'name': 'turnip'}, {'frequency': 'c', 'id': 1152, 'synset': 'turtle.n.02', 'synonyms': ['turtle'], 'def': 'any of various aquatic and land reptiles having a bony shell and flipper-like limbs for swimming', 'name': 'turtle'}, {'frequency': 'r', 'id': 1153, 'synset': 'turtleneck.n.01', 'synonyms': ['turtleneck_(clothing)', 'polo-neck'], 'def': 'a sweater or jersey with a high close-fitting collar', 'name': 'turtleneck_(clothing)'}, {'frequency': 'r', 'id': 1154, 'synset': 'typewriter.n.01', 'synonyms': ['typewriter'], 'def': 'hand-operated character printer for printing written messages one character at a time', 'name': 'typewriter'}, {'frequency': 'f', 'id': 1155, 'synset': 'umbrella.n.01', 'synonyms': ['umbrella'], 'def': 'a lightweight handheld collapsible canopy', 'name': 'umbrella'}, {'frequency': 'c', 'id': 1156, 'synset': 'underwear.n.01', 'synonyms': ['underwear', 'underclothes', 'underclothing', 'underpants'], 'def': 'undergarment worn next to the skin and under the outer garments', 'name': 'underwear'}, {'frequency': 'r', 'id': 1157, 'synset': 'unicycle.n.01', 'synonyms': ['unicycle'], 'def': 'a vehicle with a single wheel that is driven by pedals', 'name': 'unicycle'}, {'frequency': 'c', 'id': 1158, 'synset': 'urinal.n.01', 'synonyms': ['urinal'], 'def': 'a plumbing fixture (usually attached to the wall) used by men to urinate', 'name': 'urinal'}, {'frequency': 'r', 'id': 1159, 'synset': 'urn.n.01', 'synonyms': ['urn'], 'def': 'a large vase that usually has a pedestal or feet', 'name': 'urn'}, {'frequency': 'c', 'id': 1160, 'synset': 'vacuum.n.04', 'synonyms': ['vacuum_cleaner'], 'def': 'an electrical home appliance that cleans by suction', 'name': 'vacuum_cleaner'}, {'frequency': 'c', 'id': 1161, 'synset': 'valve.n.03', 'synonyms': ['valve'], 'def': 'control consisting of a mechanical device for controlling the flow of a fluid', 'name': 'valve'}, {'frequency': 'f', 'id': 1162, 'synset': 'vase.n.01', 'synonyms': ['vase'], 'def': 'an open jar of glass or porcelain used as an ornament or to hold flowers', 'name': 'vase'}, {'frequency': 'c', 'id': 1163, 'synset': 'vending_machine.n.01', 'synonyms': ['vending_machine'], 'def': 'a slot machine for selling goods', 'name': 'vending_machine'}, {'frequency': 'f', 'id': 1164, 'synset': 'vent.n.01', 'synonyms': ['vent', 'blowhole', 'air_vent'], 'def': 'a hole for the escape of gas or air', 'name': 'vent'}, {'frequency': 'c', 'id': 1165, 'synset': 'videotape.n.01', 'synonyms': ['videotape'], 'def': 'a video recording made on magnetic tape', 'name': 'videotape'}, {'frequency': 'r', 'id': 1166, 'synset': 'vinegar.n.01', 'synonyms': ['vinegar'], 'def': 'sour-tasting liquid produced usually by oxidation of the alcohol in wine or cider and used as a condiment or food preservative', 'name': 'vinegar'}, {'frequency': 'r', 'id': 1167, 'synset': 'violin.n.01', 'synonyms': ['violin', 'fiddle'], 'def': 'bowed stringed instrument that is the highest member of the violin family', 'name': 'violin'}, {'frequency': 'r', 'id': 1168, 'synset': 'vodka.n.01', 'synonyms': ['vodka'], 'def': 'unaged colorless liquor originating in Russia', 'name': 'vodka'}, {'frequency': 'r', 'id': 1169, 'synset': 'volleyball.n.02', 'synonyms': ['volleyball'], 'def': 'an inflated ball used in playing volleyball', 'name': 'volleyball'}, {'frequency': 'r', 'id': 1170, 'synset': 'vulture.n.01', 'synonyms': ['vulture'], 'def': 'any of various large birds of prey having naked heads and weak claws and feeding chiefly on carrion', 'name': 'vulture'}, {'frequency': 'c', 'id': 1171, 'synset': 'waffle.n.01', 'synonyms': ['waffle'], 'def': 'pancake batter baked in a waffle iron', 'name': 'waffle'}, {'frequency': 'r', 'id': 1172, 'synset': 'waffle_iron.n.01', 'synonyms': ['waffle_iron'], 'def': 'a kitchen appliance for baking waffles', 'name': 'waffle_iron'}, {'frequency': 'c', 'id': 1173, 'synset': 'wagon.n.01', 'synonyms': ['wagon'], 'def': 'any of various kinds of wheeled vehicles drawn by an animal or a tractor', 'name': 'wagon'}, {'frequency': 'c', 'id': 1174, 'synset': 'wagon_wheel.n.01', 'synonyms': ['wagon_wheel'], 'def': 'a wheel of a wagon', 'name': 'wagon_wheel'}, {'frequency': 'c', 'id': 1175, 'synset': 'walking_stick.n.01', 'synonyms': ['walking_stick'], 'def': 'a stick carried in the hand for support in walking', 'name': 'walking_stick'}, {'frequency': 'c', 'id': 1176, 'synset': 'wall_clock.n.01', 'synonyms': ['wall_clock'], 'def': 'a clock mounted on a wall', 'name': 'wall_clock'}, {'frequency': 'f', 'id': 1177, 'synset': 'wall_socket.n.01', 'synonyms': ['wall_socket', 'wall_plug', 'electric_outlet', 'electrical_outlet', 'outlet', 'electric_receptacle'], 'def': 'receptacle providing a place in a wiring system where current can be taken to run electrical devices', 'name': 'wall_socket'}, {'frequency': 'c', 'id': 1178, 'synset': 'wallet.n.01', 'synonyms': ['wallet', 'billfold'], 'def': 'a pocket-size case for holding papers and paper money', 'name': 'wallet'}, {'frequency': 'r', 'id': 1179, 'synset': 'walrus.n.01', 'synonyms': ['walrus'], 'def': 'either of two large northern marine mammals having ivory tusks and tough hide over thick blubber', 'name': 'walrus'}, {'frequency': 'r', 'id': 1180, 'synset': 'wardrobe.n.01', 'synonyms': ['wardrobe'], 'def': 'a tall piece of furniture that provides storage space for clothes; has a door and rails or hooks for hanging clothes', 'name': 'wardrobe'}, {'frequency': 'r', 'id': 1181, 'synset': 'wasabi.n.02', 'synonyms': ['wasabi'], 'def': 'the thick green root of the wasabi plant that the Japanese use in cooking and that tastes like strong horseradish', 'name': 'wasabi'}, {'frequency': 'c', 'id': 1182, 'synset': 'washer.n.03', 'synonyms': ['automatic_washer', 'washing_machine'], 'def': 'a home appliance for washing clothes and linens automatically', 'name': 'automatic_washer'}, {'frequency': 'f', 'id': 1183, 'synset': 'watch.n.01', 'synonyms': ['watch', 'wristwatch'], 'def': 'a small, portable timepiece', 'name': 'watch'}, {'frequency': 'f', 'id': 1184, 'synset': 'water_bottle.n.01', 'synonyms': ['water_bottle'], 'def': 'a bottle for holding water', 'name': 'water_bottle'}, {'frequency': 'c', 'id': 1185, 'synset': 'water_cooler.n.01', 'synonyms': ['water_cooler'], 'def': 'a device for cooling and dispensing drinking water', 'name': 'water_cooler'}, {'frequency': 'c', 'id': 1186, 'synset': 'water_faucet.n.01', 'synonyms': ['water_faucet', 'water_tap', 'tap_(water_faucet)'], 'def': 'a faucet for drawing water from a pipe or cask', 'name': 'water_faucet'}, {'frequency': 'r', 'id': 1187, 'synset': 'water_filter.n.01', 'synonyms': ['water_filter'], 'def': 'a filter to remove impurities from the water supply', 'name': 'water_filter'}, {'frequency': 'r', 'id': 1188, 'synset': 'water_heater.n.01', 'synonyms': ['water_heater', 'hot-water_heater'], 'def': 'a heater and storage tank to supply heated water', 'name': 'water_heater'}, {'frequency': 'r', 'id': 1189, 'synset': 'water_jug.n.01', 'synonyms': ['water_jug'], 'def': 'a jug that holds water', 'name': 'water_jug'}, {'frequency': 'r', 'id': 1190, 'synset': 'water_pistol.n.01', 'synonyms': ['water_gun', 'squirt_gun'], 'def': 'plaything consisting of a toy pistol that squirts water', 'name': 'water_gun'}, {'frequency': 'c', 'id': 1191, 'synset': 'water_scooter.n.01', 'synonyms': ['water_scooter', 'sea_scooter', 'jet_ski'], 'def': 'a motorboat resembling a motor scooter (NOT A SURFBOARD OR WATER SKI)', 'name': 'water_scooter'}, {'frequency': 'c', 'id': 1192, 'synset': 'water_ski.n.01', 'synonyms': ['water_ski'], 'def': 'broad ski for skimming over water towed by a speedboat (DO NOT MARK WATER)', 'name': 'water_ski'}, {'frequency': 'c', 'id': 1193, 'synset': 'water_tower.n.01', 'synonyms': ['water_tower'], 'def': 'a large reservoir for water', 'name': 'water_tower'}, {'frequency': 'c', 'id': 1194, 'synset': 'watering_can.n.01', 'synonyms': ['watering_can'], 'def': 'a container with a handle and a spout with a perforated nozzle; used to sprinkle water over plants', 'name': 'watering_can'}, {'frequency': 'c', 'id': 1195, 'synset': 'watermelon.n.02', 'synonyms': ['watermelon'], 'def': 'large oblong or roundish melon with a hard green rind and sweet watery red or occasionally yellowish pulp', 'name': 'watermelon'}, {'frequency': 'f', 'id': 1196, 'synset': 'weathervane.n.01', 'synonyms': ['weathervane', 'vane_(weathervane)', 'wind_vane'], 'def': 'mechanical device attached to an elevated structure; rotates freely to show the direction of the wind', 'name': 'weathervane'}, {'frequency': 'c', 'id': 1197, 'synset': 'webcam.n.01', 'synonyms': ['webcam'], 'def': 'a digital camera designed to take digital photographs and transmit them over the internet', 'name': 'webcam'}, {'frequency': 'c', 'id': 1198, 'synset': 'wedding_cake.n.01', 'synonyms': ['wedding_cake', 'bridecake'], 'def': 'a rich cake with two or more tiers and covered with frosting and decorations; served at a wedding reception', 'name': 'wedding_cake'}, {'frequency': 'c', 'id': 1199, 'synset': 'wedding_ring.n.01', 'synonyms': ['wedding_ring', 'wedding_band'], 'def': 'a ring given to the bride and/or groom at the wedding', 'name': 'wedding_ring'}, {'frequency': 'f', 'id': 1200, 'synset': 'wet_suit.n.01', 'synonyms': ['wet_suit'], 'def': 'a close-fitting garment made of a permeable material; worn in cold water to retain body heat', 'name': 'wet_suit'}, {'frequency': 'f', 'id': 1201, 'synset': 'wheel.n.01', 'synonyms': ['wheel'], 'def': 'a circular frame with spokes (or a solid disc) that can rotate on a shaft or axle', 'name': 'wheel'}, {'frequency': 'c', 'id': 1202, 'synset': 'wheelchair.n.01', 'synonyms': ['wheelchair'], 'def': 'a movable chair mounted on large wheels', 'name': 'wheelchair'}, {'frequency': 'c', 'id': 1203, 'synset': 'whipped_cream.n.01', 'synonyms': ['whipped_cream'], 'def': 'cream that has been beaten until light and fluffy', 'name': 'whipped_cream'}, {'frequency': 'r', 'id': 1204, 'synset': 'whiskey.n.01', 'synonyms': ['whiskey'], 'def': 'a liquor made from fermented mash of grain', 'name': 'whiskey'}, {'frequency': 'r', 'id': 1205, 'synset': 'whistle.n.03', 'synonyms': ['whistle'], 'def': 'a small wind instrument that produces a whistling sound by blowing into it', 'name': 'whistle'}, {'frequency': 'r', 'id': 1206, 'synset': 'wick.n.02', 'synonyms': ['wick'], 'def': 'a loosely woven cord in a candle or oil lamp that is lit on fire', 'name': 'wick'}, {'frequency': 'c', 'id': 1207, 'synset': 'wig.n.01', 'synonyms': ['wig'], 'def': 'hairpiece covering the head and made of real or synthetic hair', 'name': 'wig'}, {'frequency': 'c', 'id': 1208, 'synset': 'wind_chime.n.01', 'synonyms': ['wind_chime'], 'def': 'a decorative arrangement of pieces of metal or glass or pottery that hang together loosely so the wind can cause them to tinkle', 'name': 'wind_chime'}, {'frequency': 'c', 'id': 1209, 'synset': 'windmill.n.01', 'synonyms': ['windmill'], 'def': 'a mill that is powered by the wind', 'name': 'windmill'}, {'frequency': 'c', 'id': 1210, 'synset': 'window_box.n.01', 'synonyms': ['window_box_(for_plants)'], 'def': 'a container for growing plants on a windowsill', 'name': 'window_box_(for_plants)'}, {'frequency': 'f', 'id': 1211, 'synset': 'windshield_wiper.n.01', 'synonyms': ['windshield_wiper', 'windscreen_wiper', 'wiper_(for_windshield/screen)'], 'def': 'a mechanical device that cleans the windshield', 'name': 'windshield_wiper'}, {'frequency': 'c', 'id': 1212, 'synset': 'windsock.n.01', 'synonyms': ['windsock', 'air_sock', 'air-sleeve', 'wind_sleeve', 'wind_cone'], 'def': 'a truncated cloth cone mounted on a mast/pole; shows wind direction', 'name': 'windsock'}, {'frequency': 'f', 'id': 1213, 'synset': 'wine_bottle.n.01', 'synonyms': ['wine_bottle'], 'def': 'a bottle for holding wine', 'name': 'wine_bottle'}, {'frequency': 'r', 'id': 1214, 'synset': 'wine_bucket.n.01', 'synonyms': ['wine_bucket', 'wine_cooler'], 'def': 'a bucket of ice used to chill a bottle of wine', 'name': 'wine_bucket'}, {'frequency': 'f', 'id': 1215, 'synset': 'wineglass.n.01', 'synonyms': ['wineglass'], 'def': 'a glass that has a stem and in which wine is served', 'name': 'wineglass'}, {'frequency': 'r', 'id': 1216, 'synset': 'wing_chair.n.01', 'synonyms': ['wing_chair'], 'def': 'easy chair having wings on each side of a high back', 'name': 'wing_chair'}, {'frequency': 'c', 'id': 1217, 'synset': 'winker.n.02', 'synonyms': ['blinder_(for_horses)'], 'def': 'blinds that prevent a horse from seeing something on either side', 'name': 'blinder_(for_horses)'}, {'frequency': 'c', 'id': 1218, 'synset': 'wok.n.01', 'synonyms': ['wok'], 'def': 'pan with a convex bottom; used for frying in Chinese cooking', 'name': 'wok'}, {'frequency': 'r', 'id': 1219, 'synset': 'wolf.n.01', 'synonyms': ['wolf'], 'def': 'a wild carnivorous mammal of the dog family, living and hunting in packs', 'name': 'wolf'}, {'frequency': 'c', 'id': 1220, 'synset': 'wooden_spoon.n.02', 'synonyms': ['wooden_spoon'], 'def': 'a spoon made of wood', 'name': 'wooden_spoon'}, {'frequency': 'c', 'id': 1221, 'synset': 'wreath.n.01', 'synonyms': ['wreath'], 'def': 'an arrangement of flowers, leaves, or stems fastened in a ring', 'name': 'wreath'}, {'frequency': 'c', 'id': 1222, 'synset': 'wrench.n.03', 'synonyms': ['wrench', 'spanner'], 'def': 'a hand tool that is used to hold or twist a nut or bolt', 'name': 'wrench'}, {'frequency': 'c', 'id': 1223, 'synset': 'wristband.n.01', 'synonyms': ['wristband'], 'def': 'band consisting of a part of a sleeve that covers the wrist', 'name': 'wristband'}, {'frequency': 'f', 'id': 1224, 'synset': 'wristlet.n.01', 'synonyms': ['wristlet', 'wrist_band'], 'def': 'a band or bracelet worn around the wrist', 'name': 'wristlet'}, {'frequency': 'r', 'id': 1225, 'synset': 'yacht.n.01', 'synonyms': ['yacht'], 'def': 'an expensive vessel propelled by sail or power and used for cruising or racing', 'name': 'yacht'}, {'frequency': 'r', 'id': 1226, 'synset': 'yak.n.02', 'synonyms': ['yak'], 'def': 'large long-haired wild ox of Tibet often domesticated', 'name': 'yak'}, {'frequency': 'c', 'id': 1227, 'synset': 'yogurt.n.01', 'synonyms': ['yogurt', 'yoghurt', 'yoghourt'], 'def': 'a custard-like food made from curdled milk', 'name': 'yogurt'}, {'frequency': 'r', 'id': 1228, 'synset': 'yoke.n.07', 'synonyms': ['yoke_(animal_equipment)'], 'def': 'gear joining two animals at the neck; NOT egg yolk', 'name': 'yoke_(animal_equipment)'}, {'frequency': 'f', 'id': 1229, 'synset': 'zebra.n.01', 'synonyms': ['zebra'], 'def': 'any of several fleet black-and-white striped African equines', 'name': 'zebra'}, {'frequency': 'c', 'id': 1230, 'synset': 'zucchini.n.02', 'synonyms': ['zucchini', 'courgette'], 'def': 'small cucumber-shaped vegetable marrow; typically dark green', 'name': 'zucchini'}] # noqa # fmt: on ================================================ FILE: detectron2/detectron2/data/datasets/lvis_v1_categories.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # Autogen with # with open("lvis_v1_val.json", "r") as f: # a = json.load(f) # c = a["categories"] # for x in c: # del x["image_count"] # del x["instance_count"] # LVIS_CATEGORIES = repr(c) + " # noqa" # with open("/tmp/lvis_categories.py", "wt") as f: # f.write(f"LVIS_CATEGORIES = {LVIS_CATEGORIES}") # Then paste the contents of that file below # fmt: off LVIS_CATEGORIES = [{'frequency': 'c', 'synset': 'aerosol.n.02', 'synonyms': ['aerosol_can', 'spray_can'], 'id': 1, 'def': 'a dispenser that holds a substance under pressure', 'name': 'aerosol_can'}, {'frequency': 'f', 'synset': 'air_conditioner.n.01', 'synonyms': ['air_conditioner'], 'id': 2, 'def': 'a machine that keeps air cool and dry', 'name': 'air_conditioner'}, {'frequency': 'f', 'synset': 'airplane.n.01', 'synonyms': ['airplane', 'aeroplane'], 'id': 3, 'def': 'an aircraft that has a fixed wing and is powered by propellers or jets', 'name': 'airplane'}, {'frequency': 'f', 'synset': 'alarm_clock.n.01', 'synonyms': ['alarm_clock'], 'id': 4, 'def': 'a clock that wakes a sleeper at some preset time', 'name': 'alarm_clock'}, {'frequency': 'c', 'synset': 'alcohol.n.01', 'synonyms': ['alcohol', 'alcoholic_beverage'], 'id': 5, 'def': 'a liquor or brew containing alcohol as the active agent', 'name': 'alcohol'}, {'frequency': 'c', 'synset': 'alligator.n.02', 'synonyms': ['alligator', 'gator'], 'id': 6, 'def': 'amphibious reptiles related to crocodiles but with shorter broader snouts', 'name': 'alligator'}, {'frequency': 'c', 'synset': 'almond.n.02', 'synonyms': ['almond'], 'id': 7, 'def': 'oval-shaped edible seed of the almond tree', 'name': 'almond'}, {'frequency': 'c', 'synset': 'ambulance.n.01', 'synonyms': ['ambulance'], 'id': 8, 'def': 'a vehicle that takes people to and from hospitals', 'name': 'ambulance'}, {'frequency': 'c', 'synset': 'amplifier.n.01', 'synonyms': ['amplifier'], 'id': 9, 'def': 'electronic equipment that increases strength of signals', 'name': 'amplifier'}, {'frequency': 'c', 'synset': 'anklet.n.03', 'synonyms': ['anklet', 'ankle_bracelet'], 'id': 10, 'def': 'an ornament worn around the ankle', 'name': 'anklet'}, {'frequency': 'f', 'synset': 'antenna.n.01', 'synonyms': ['antenna', 'aerial', 'transmitting_aerial'], 'id': 11, 'def': 'an electrical device that sends or receives radio or television signals', 'name': 'antenna'}, {'frequency': 'f', 'synset': 'apple.n.01', 'synonyms': ['apple'], 'id': 12, 'def': 'fruit with red or yellow or green skin and sweet to tart crisp whitish flesh', 'name': 'apple'}, {'frequency': 'r', 'synset': 'applesauce.n.01', 'synonyms': ['applesauce'], 'id': 13, 'def': 'puree of stewed apples usually sweetened and spiced', 'name': 'applesauce'}, {'frequency': 'r', 'synset': 'apricot.n.02', 'synonyms': ['apricot'], 'id': 14, 'def': 'downy yellow to rosy-colored fruit resembling a small peach', 'name': 'apricot'}, {'frequency': 'f', 'synset': 'apron.n.01', 'synonyms': ['apron'], 'id': 15, 'def': 'a garment of cloth that is tied about the waist and worn to protect clothing', 'name': 'apron'}, {'frequency': 'c', 'synset': 'aquarium.n.01', 'synonyms': ['aquarium', 'fish_tank'], 'id': 16, 'def': 'a tank/pool/bowl filled with water for keeping live fish and underwater animals', 'name': 'aquarium'}, {'frequency': 'r', 'synset': 'arctic.n.02', 'synonyms': ['arctic_(type_of_shoe)', 'galosh', 'golosh', 'rubber_(type_of_shoe)', 'gumshoe'], 'id': 17, 'def': 'a waterproof overshoe that protects shoes from water or snow', 'name': 'arctic_(type_of_shoe)'}, {'frequency': 'c', 'synset': 'armband.n.02', 'synonyms': ['armband'], 'id': 18, 'def': 'a band worn around the upper arm', 'name': 'armband'}, {'frequency': 'f', 'synset': 'armchair.n.01', 'synonyms': ['armchair'], 'id': 19, 'def': 'chair with a support on each side for arms', 'name': 'armchair'}, {'frequency': 'r', 'synset': 'armoire.n.01', 'synonyms': ['armoire'], 'id': 20, 'def': 'a large wardrobe or cabinet', 'name': 'armoire'}, {'frequency': 'r', 'synset': 'armor.n.01', 'synonyms': ['armor', 'armour'], 'id': 21, 'def': 'protective covering made of metal and used in combat', 'name': 'armor'}, {'frequency': 'c', 'synset': 'artichoke.n.02', 'synonyms': ['artichoke'], 'id': 22, 'def': 'a thistlelike flower head with edible fleshy leaves and heart', 'name': 'artichoke'}, {'frequency': 'f', 'synset': 'ashcan.n.01', 'synonyms': ['trash_can', 'garbage_can', 'wastebin', 'dustbin', 'trash_barrel', 'trash_bin'], 'id': 23, 'def': 'a bin that holds rubbish until it is collected', 'name': 'trash_can'}, {'frequency': 'c', 'synset': 'ashtray.n.01', 'synonyms': ['ashtray'], 'id': 24, 'def': "a receptacle for the ash from smokers' cigars or cigarettes", 'name': 'ashtray'}, {'frequency': 'c', 'synset': 'asparagus.n.02', 'synonyms': ['asparagus'], 'id': 25, 'def': 'edible young shoots of the asparagus plant', 'name': 'asparagus'}, {'frequency': 'c', 'synset': 'atomizer.n.01', 'synonyms': ['atomizer', 'atomiser', 'spray', 'sprayer', 'nebulizer', 'nebuliser'], 'id': 26, 'def': 'a dispenser that turns a liquid (such as perfume) into a fine mist', 'name': 'atomizer'}, {'frequency': 'f', 'synset': 'avocado.n.01', 'synonyms': ['avocado'], 'id': 27, 'def': 'a pear-shaped fruit with green or blackish skin and rich yellowish pulp enclosing a single large seed', 'name': 'avocado'}, {'frequency': 'c', 'synset': 'award.n.02', 'synonyms': ['award', 'accolade'], 'id': 28, 'def': 'a tangible symbol signifying approval or distinction', 'name': 'award'}, {'frequency': 'f', 'synset': 'awning.n.01', 'synonyms': ['awning'], 'id': 29, 'def': 'a canopy made of canvas to shelter people or things from rain or sun', 'name': 'awning'}, {'frequency': 'r', 'synset': 'ax.n.01', 'synonyms': ['ax', 'axe'], 'id': 30, 'def': 'an edge tool with a heavy bladed head mounted across a handle', 'name': 'ax'}, {'frequency': 'r', 'synset': 'baboon.n.01', 'synonyms': ['baboon'], 'id': 31, 'def': 'large terrestrial monkeys having doglike muzzles', 'name': 'baboon'}, {'frequency': 'f', 'synset': 'baby_buggy.n.01', 'synonyms': ['baby_buggy', 'baby_carriage', 'perambulator', 'pram', 'stroller'], 'id': 32, 'def': 'a small vehicle with four wheels in which a baby or child is pushed around', 'name': 'baby_buggy'}, {'frequency': 'c', 'synset': 'backboard.n.01', 'synonyms': ['basketball_backboard'], 'id': 33, 'def': 'a raised vertical board with basket attached; used to play basketball', 'name': 'basketball_backboard'}, {'frequency': 'f', 'synset': 'backpack.n.01', 'synonyms': ['backpack', 'knapsack', 'packsack', 'rucksack', 'haversack'], 'id': 34, 'def': 'a bag carried by a strap on your back or shoulder', 'name': 'backpack'}, {'frequency': 'f', 'synset': 'bag.n.04', 'synonyms': ['handbag', 'purse', 'pocketbook'], 'id': 35, 'def': 'a container used for carrying money and small personal items or accessories', 'name': 'handbag'}, {'frequency': 'f', 'synset': 'bag.n.06', 'synonyms': ['suitcase', 'baggage', 'luggage'], 'id': 36, 'def': 'cases used to carry belongings when traveling', 'name': 'suitcase'}, {'frequency': 'c', 'synset': 'bagel.n.01', 'synonyms': ['bagel', 'beigel'], 'id': 37, 'def': 'glazed yeast-raised doughnut-shaped roll with hard crust', 'name': 'bagel'}, {'frequency': 'r', 'synset': 'bagpipe.n.01', 'synonyms': ['bagpipe'], 'id': 38, 'def': 'a tubular wind instrument; the player blows air into a bag and squeezes it out', 'name': 'bagpipe'}, {'frequency': 'r', 'synset': 'baguet.n.01', 'synonyms': ['baguet', 'baguette'], 'id': 39, 'def': 'narrow French stick loaf', 'name': 'baguet'}, {'frequency': 'r', 'synset': 'bait.n.02', 'synonyms': ['bait', 'lure'], 'id': 40, 'def': 'something used to lure fish or other animals into danger so they can be trapped or killed', 'name': 'bait'}, {'frequency': 'f', 'synset': 'ball.n.06', 'synonyms': ['ball'], 'id': 41, 'def': 'a spherical object used as a plaything', 'name': 'ball'}, {'frequency': 'r', 'synset': 'ballet_skirt.n.01', 'synonyms': ['ballet_skirt', 'tutu'], 'id': 42, 'def': 'very short skirt worn by ballerinas', 'name': 'ballet_skirt'}, {'frequency': 'f', 'synset': 'balloon.n.01', 'synonyms': ['balloon'], 'id': 43, 'def': 'large tough nonrigid bag filled with gas or heated air', 'name': 'balloon'}, {'frequency': 'c', 'synset': 'bamboo.n.02', 'synonyms': ['bamboo'], 'id': 44, 'def': 'woody tropical grass having hollow woody stems', 'name': 'bamboo'}, {'frequency': 'f', 'synset': 'banana.n.02', 'synonyms': ['banana'], 'id': 45, 'def': 'elongated crescent-shaped yellow fruit with soft sweet flesh', 'name': 'banana'}, {'frequency': 'c', 'synset': 'band_aid.n.01', 'synonyms': ['Band_Aid'], 'id': 46, 'def': 'trade name for an adhesive bandage to cover small cuts or blisters', 'name': 'Band_Aid'}, {'frequency': 'c', 'synset': 'bandage.n.01', 'synonyms': ['bandage'], 'id': 47, 'def': 'a piece of soft material that covers and protects an injured part of the body', 'name': 'bandage'}, {'frequency': 'f', 'synset': 'bandanna.n.01', 'synonyms': ['bandanna', 'bandana'], 'id': 48, 'def': 'large and brightly colored handkerchief; often used as a neckerchief', 'name': 'bandanna'}, {'frequency': 'r', 'synset': 'banjo.n.01', 'synonyms': ['banjo'], 'id': 49, 'def': 'a stringed instrument of the guitar family with a long neck and circular body', 'name': 'banjo'}, {'frequency': 'f', 'synset': 'banner.n.01', 'synonyms': ['banner', 'streamer'], 'id': 50, 'def': 'long strip of cloth or paper used for decoration or advertising', 'name': 'banner'}, {'frequency': 'r', 'synset': 'barbell.n.01', 'synonyms': ['barbell'], 'id': 51, 'def': 'a bar to which heavy discs are attached at each end; used in weightlifting', 'name': 'barbell'}, {'frequency': 'r', 'synset': 'barge.n.01', 'synonyms': ['barge'], 'id': 52, 'def': 'a flatbottom boat for carrying heavy loads (especially on canals)', 'name': 'barge'}, {'frequency': 'f', 'synset': 'barrel.n.02', 'synonyms': ['barrel', 'cask'], 'id': 53, 'def': 'a cylindrical container that holds liquids', 'name': 'barrel'}, {'frequency': 'c', 'synset': 'barrette.n.01', 'synonyms': ['barrette'], 'id': 54, 'def': "a pin for holding women's hair in place", 'name': 'barrette'}, {'frequency': 'c', 'synset': 'barrow.n.03', 'synonyms': ['barrow', 'garden_cart', 'lawn_cart', 'wheelbarrow'], 'id': 55, 'def': 'a cart for carrying small loads; has handles and one or more wheels', 'name': 'barrow'}, {'frequency': 'f', 'synset': 'base.n.03', 'synonyms': ['baseball_base'], 'id': 56, 'def': 'a place that the runner must touch before scoring', 'name': 'baseball_base'}, {'frequency': 'f', 'synset': 'baseball.n.02', 'synonyms': ['baseball'], 'id': 57, 'def': 'a ball used in playing baseball', 'name': 'baseball'}, {'frequency': 'f', 'synset': 'baseball_bat.n.01', 'synonyms': ['baseball_bat'], 'id': 58, 'def': 'an implement used in baseball by the batter', 'name': 'baseball_bat'}, {'frequency': 'f', 'synset': 'baseball_cap.n.01', 'synonyms': ['baseball_cap', 'jockey_cap', 'golf_cap'], 'id': 59, 'def': 'a cap with a bill', 'name': 'baseball_cap'}, {'frequency': 'f', 'synset': 'baseball_glove.n.01', 'synonyms': ['baseball_glove', 'baseball_mitt'], 'id': 60, 'def': 'the handwear used by fielders in playing baseball', 'name': 'baseball_glove'}, {'frequency': 'f', 'synset': 'basket.n.01', 'synonyms': ['basket', 'handbasket'], 'id': 61, 'def': 'a container that is usually woven and has handles', 'name': 'basket'}, {'frequency': 'c', 'synset': 'basketball.n.02', 'synonyms': ['basketball'], 'id': 62, 'def': 'an inflated ball used in playing basketball', 'name': 'basketball'}, {'frequency': 'r', 'synset': 'bass_horn.n.01', 'synonyms': ['bass_horn', 'sousaphone', 'tuba'], 'id': 63, 'def': 'the lowest brass wind instrument', 'name': 'bass_horn'}, {'frequency': 'c', 'synset': 'bat.n.01', 'synonyms': ['bat_(animal)'], 'id': 64, 'def': 'nocturnal mouselike mammal with forelimbs modified to form membranous wings', 'name': 'bat_(animal)'}, {'frequency': 'f', 'synset': 'bath_mat.n.01', 'synonyms': ['bath_mat'], 'id': 65, 'def': 'a heavy towel or mat to stand on while drying yourself after a bath', 'name': 'bath_mat'}, {'frequency': 'f', 'synset': 'bath_towel.n.01', 'synonyms': ['bath_towel'], 'id': 66, 'def': 'a large towel; to dry yourself after a bath', 'name': 'bath_towel'}, {'frequency': 'c', 'synset': 'bathrobe.n.01', 'synonyms': ['bathrobe'], 'id': 67, 'def': 'a loose-fitting robe of towelling; worn after a bath or swim', 'name': 'bathrobe'}, {'frequency': 'f', 'synset': 'bathtub.n.01', 'synonyms': ['bathtub', 'bathing_tub'], 'id': 68, 'def': 'a large open container that you fill with water and use to wash the body', 'name': 'bathtub'}, {'frequency': 'r', 'synset': 'batter.n.02', 'synonyms': ['batter_(food)'], 'id': 69, 'def': 'a liquid or semiliquid mixture, as of flour, eggs, and milk, used in cooking', 'name': 'batter_(food)'}, {'frequency': 'c', 'synset': 'battery.n.02', 'synonyms': ['battery'], 'id': 70, 'def': 'a portable device that produces electricity', 'name': 'battery'}, {'frequency': 'r', 'synset': 'beach_ball.n.01', 'synonyms': ['beachball'], 'id': 71, 'def': 'large and light ball; for play at the seaside', 'name': 'beachball'}, {'frequency': 'c', 'synset': 'bead.n.01', 'synonyms': ['bead'], 'id': 72, 'def': 'a small ball with a hole through the middle used for ornamentation, jewellery, etc.', 'name': 'bead'}, {'frequency': 'c', 'synset': 'bean_curd.n.01', 'synonyms': ['bean_curd', 'tofu'], 'id': 73, 'def': 'cheeselike food made of curdled soybean milk', 'name': 'bean_curd'}, {'frequency': 'c', 'synset': 'beanbag.n.01', 'synonyms': ['beanbag'], 'id': 74, 'def': 'a bag filled with dried beans or similar items; used in games or to sit on', 'name': 'beanbag'}, {'frequency': 'f', 'synset': 'beanie.n.01', 'synonyms': ['beanie', 'beany'], 'id': 75, 'def': 'a small skullcap; formerly worn by schoolboys and college freshmen', 'name': 'beanie'}, {'frequency': 'f', 'synset': 'bear.n.01', 'synonyms': ['bear'], 'id': 76, 'def': 'large carnivorous or omnivorous mammals with shaggy coats and claws', 'name': 'bear'}, {'frequency': 'f', 'synset': 'bed.n.01', 'synonyms': ['bed'], 'id': 77, 'def': 'a piece of furniture that provides a place to sleep', 'name': 'bed'}, {'frequency': 'r', 'synset': 'bedpan.n.01', 'synonyms': ['bedpan'], 'id': 78, 'def': 'a shallow vessel used by a bedridden patient for defecation and urination', 'name': 'bedpan'}, {'frequency': 'f', 'synset': 'bedspread.n.01', 'synonyms': ['bedspread', 'bedcover', 'bed_covering', 'counterpane', 'spread'], 'id': 79, 'def': 'decorative cover for a bed', 'name': 'bedspread'}, {'frequency': 'f', 'synset': 'beef.n.01', 'synonyms': ['cow'], 'id': 80, 'def': 'cattle/cow', 'name': 'cow'}, {'frequency': 'f', 'synset': 'beef.n.02', 'synonyms': ['beef_(food)', 'boeuf_(food)'], 'id': 81, 'def': 'meat from an adult domestic bovine', 'name': 'beef_(food)'}, {'frequency': 'r', 'synset': 'beeper.n.01', 'synonyms': ['beeper', 'pager'], 'id': 82, 'def': 'an device that beeps when the person carrying it is being paged', 'name': 'beeper'}, {'frequency': 'f', 'synset': 'beer_bottle.n.01', 'synonyms': ['beer_bottle'], 'id': 83, 'def': 'a bottle that holds beer', 'name': 'beer_bottle'}, {'frequency': 'c', 'synset': 'beer_can.n.01', 'synonyms': ['beer_can'], 'id': 84, 'def': 'a can that holds beer', 'name': 'beer_can'}, {'frequency': 'r', 'synset': 'beetle.n.01', 'synonyms': ['beetle'], 'id': 85, 'def': 'insect with hard wing covers', 'name': 'beetle'}, {'frequency': 'f', 'synset': 'bell.n.01', 'synonyms': ['bell'], 'id': 86, 'def': 'a hollow device made of metal that makes a ringing sound when struck', 'name': 'bell'}, {'frequency': 'f', 'synset': 'bell_pepper.n.02', 'synonyms': ['bell_pepper', 'capsicum'], 'id': 87, 'def': 'large bell-shaped sweet pepper in green or red or yellow or orange or black varieties', 'name': 'bell_pepper'}, {'frequency': 'f', 'synset': 'belt.n.02', 'synonyms': ['belt'], 'id': 88, 'def': 'a band to tie or buckle around the body (usually at the waist)', 'name': 'belt'}, {'frequency': 'f', 'synset': 'belt_buckle.n.01', 'synonyms': ['belt_buckle'], 'id': 89, 'def': 'the buckle used to fasten a belt', 'name': 'belt_buckle'}, {'frequency': 'f', 'synset': 'bench.n.01', 'synonyms': ['bench'], 'id': 90, 'def': 'a long seat for more than one person', 'name': 'bench'}, {'frequency': 'c', 'synset': 'beret.n.01', 'synonyms': ['beret'], 'id': 91, 'def': 'a cap with no brim or bill; made of soft cloth', 'name': 'beret'}, {'frequency': 'c', 'synset': 'bib.n.02', 'synonyms': ['bib'], 'id': 92, 'def': 'a napkin tied under the chin of a child while eating', 'name': 'bib'}, {'frequency': 'r', 'synset': 'bible.n.01', 'synonyms': ['Bible'], 'id': 93, 'def': 'the sacred writings of the Christian religions', 'name': 'Bible'}, {'frequency': 'f', 'synset': 'bicycle.n.01', 'synonyms': ['bicycle', 'bike_(bicycle)'], 'id': 94, 'def': 'a wheeled vehicle that has two wheels and is moved by foot pedals', 'name': 'bicycle'}, {'frequency': 'f', 'synset': 'bill.n.09', 'synonyms': ['visor', 'vizor'], 'id': 95, 'def': 'a brim that projects to the front to shade the eyes', 'name': 'visor'}, {'frequency': 'f', 'synset': 'billboard.n.01', 'synonyms': ['billboard'], 'id': 96, 'def': 'large outdoor signboard', 'name': 'billboard'}, {'frequency': 'c', 'synset': 'binder.n.03', 'synonyms': ['binder', 'ring-binder'], 'id': 97, 'def': 'holds loose papers or magazines', 'name': 'binder'}, {'frequency': 'c', 'synset': 'binoculars.n.01', 'synonyms': ['binoculars', 'field_glasses', 'opera_glasses'], 'id': 98, 'def': 'an optical instrument designed for simultaneous use by both eyes', 'name': 'binoculars'}, {'frequency': 'f', 'synset': 'bird.n.01', 'synonyms': ['bird'], 'id': 99, 'def': 'animal characterized by feathers and wings', 'name': 'bird'}, {'frequency': 'c', 'synset': 'bird_feeder.n.01', 'synonyms': ['birdfeeder'], 'id': 100, 'def': 'an outdoor device that supplies food for wild birds', 'name': 'birdfeeder'}, {'frequency': 'c', 'synset': 'birdbath.n.01', 'synonyms': ['birdbath'], 'id': 101, 'def': 'an ornamental basin (usually in a garden) for birds to bathe in', 'name': 'birdbath'}, {'frequency': 'c', 'synset': 'birdcage.n.01', 'synonyms': ['birdcage'], 'id': 102, 'def': 'a cage in which a bird can be kept', 'name': 'birdcage'}, {'frequency': 'c', 'synset': 'birdhouse.n.01', 'synonyms': ['birdhouse'], 'id': 103, 'def': 'a shelter for birds', 'name': 'birdhouse'}, {'frequency': 'f', 'synset': 'birthday_cake.n.01', 'synonyms': ['birthday_cake'], 'id': 104, 'def': 'decorated cake served at a birthday party', 'name': 'birthday_cake'}, {'frequency': 'r', 'synset': 'birthday_card.n.01', 'synonyms': ['birthday_card'], 'id': 105, 'def': 'a card expressing a birthday greeting', 'name': 'birthday_card'}, {'frequency': 'r', 'synset': 'black_flag.n.01', 'synonyms': ['pirate_flag'], 'id': 106, 'def': 'a flag usually bearing a white skull and crossbones on a black background', 'name': 'pirate_flag'}, {'frequency': 'c', 'synset': 'black_sheep.n.02', 'synonyms': ['black_sheep'], 'id': 107, 'def': 'sheep with a black coat', 'name': 'black_sheep'}, {'frequency': 'c', 'synset': 'blackberry.n.01', 'synonyms': ['blackberry'], 'id': 108, 'def': 'large sweet black or very dark purple edible aggregate fruit', 'name': 'blackberry'}, {'frequency': 'f', 'synset': 'blackboard.n.01', 'synonyms': ['blackboard', 'chalkboard'], 'id': 109, 'def': 'sheet of slate; for writing with chalk', 'name': 'blackboard'}, {'frequency': 'f', 'synset': 'blanket.n.01', 'synonyms': ['blanket'], 'id': 110, 'def': 'bedding that keeps a person warm in bed', 'name': 'blanket'}, {'frequency': 'c', 'synset': 'blazer.n.01', 'synonyms': ['blazer', 'sport_jacket', 'sport_coat', 'sports_jacket', 'sports_coat'], 'id': 111, 'def': 'lightweight jacket; often striped in the colors of a club or school', 'name': 'blazer'}, {'frequency': 'f', 'synset': 'blender.n.01', 'synonyms': ['blender', 'liquidizer', 'liquidiser'], 'id': 112, 'def': 'an electrically powered mixer that mix or chop or liquefy foods', 'name': 'blender'}, {'frequency': 'r', 'synset': 'blimp.n.02', 'synonyms': ['blimp'], 'id': 113, 'def': 'a small nonrigid airship used for observation or as a barrage balloon', 'name': 'blimp'}, {'frequency': 'f', 'synset': 'blinker.n.01', 'synonyms': ['blinker', 'flasher'], 'id': 114, 'def': 'a light that flashes on and off; used as a signal or to send messages', 'name': 'blinker'}, {'frequency': 'f', 'synset': 'blouse.n.01', 'synonyms': ['blouse'], 'id': 115, 'def': 'a top worn by women', 'name': 'blouse'}, {'frequency': 'f', 'synset': 'blueberry.n.02', 'synonyms': ['blueberry'], 'id': 116, 'def': 'sweet edible dark-blue berries of blueberry plants', 'name': 'blueberry'}, {'frequency': 'r', 'synset': 'board.n.09', 'synonyms': ['gameboard'], 'id': 117, 'def': 'a flat portable surface (usually rectangular) designed for board games', 'name': 'gameboard'}, {'frequency': 'f', 'synset': 'boat.n.01', 'synonyms': ['boat', 'ship_(boat)'], 'id': 118, 'def': 'a vessel for travel on water', 'name': 'boat'}, {'frequency': 'r', 'synset': 'bob.n.05', 'synonyms': ['bob', 'bobber', 'bobfloat'], 'id': 119, 'def': 'a small float usually made of cork; attached to a fishing line', 'name': 'bob'}, {'frequency': 'c', 'synset': 'bobbin.n.01', 'synonyms': ['bobbin', 'spool', 'reel'], 'id': 120, 'def': 'a thing around which thread/tape/film or other flexible materials can be wound', 'name': 'bobbin'}, {'frequency': 'c', 'synset': 'bobby_pin.n.01', 'synonyms': ['bobby_pin', 'hairgrip'], 'id': 121, 'def': 'a flat wire hairpin used to hold bobbed hair in place', 'name': 'bobby_pin'}, {'frequency': 'c', 'synset': 'boiled_egg.n.01', 'synonyms': ['boiled_egg', 'coddled_egg'], 'id': 122, 'def': 'egg cooked briefly in the shell in gently boiling water', 'name': 'boiled_egg'}, {'frequency': 'r', 'synset': 'bolo_tie.n.01', 'synonyms': ['bolo_tie', 'bolo', 'bola_tie', 'bola'], 'id': 123, 'def': 'a cord fastened around the neck with an ornamental clasp and worn as a necktie', 'name': 'bolo_tie'}, {'frequency': 'c', 'synset': 'bolt.n.03', 'synonyms': ['deadbolt'], 'id': 124, 'def': 'the part of a lock that is engaged or withdrawn with a key', 'name': 'deadbolt'}, {'frequency': 'f', 'synset': 'bolt.n.06', 'synonyms': ['bolt'], 'id': 125, 'def': 'a screw that screws into a nut to form a fastener', 'name': 'bolt'}, {'frequency': 'r', 'synset': 'bonnet.n.01', 'synonyms': ['bonnet'], 'id': 126, 'def': 'a hat tied under the chin', 'name': 'bonnet'}, {'frequency': 'f', 'synset': 'book.n.01', 'synonyms': ['book'], 'id': 127, 'def': 'a written work or composition that has been published', 'name': 'book'}, {'frequency': 'c', 'synset': 'bookcase.n.01', 'synonyms': ['bookcase'], 'id': 128, 'def': 'a piece of furniture with shelves for storing books', 'name': 'bookcase'}, {'frequency': 'c', 'synset': 'booklet.n.01', 'synonyms': ['booklet', 'brochure', 'leaflet', 'pamphlet'], 'id': 129, 'def': 'a small book usually having a paper cover', 'name': 'booklet'}, {'frequency': 'r', 'synset': 'bookmark.n.01', 'synonyms': ['bookmark', 'bookmarker'], 'id': 130, 'def': 'a marker (a piece of paper or ribbon) placed between the pages of a book', 'name': 'bookmark'}, {'frequency': 'r', 'synset': 'boom.n.04', 'synonyms': ['boom_microphone', 'microphone_boom'], 'id': 131, 'def': 'a pole carrying an overhead microphone projected over a film or tv set', 'name': 'boom_microphone'}, {'frequency': 'f', 'synset': 'boot.n.01', 'synonyms': ['boot'], 'id': 132, 'def': 'footwear that covers the whole foot and lower leg', 'name': 'boot'}, {'frequency': 'f', 'synset': 'bottle.n.01', 'synonyms': ['bottle'], 'id': 133, 'def': 'a glass or plastic vessel used for storing drinks or other liquids', 'name': 'bottle'}, {'frequency': 'c', 'synset': 'bottle_opener.n.01', 'synonyms': ['bottle_opener'], 'id': 134, 'def': 'an opener for removing caps or corks from bottles', 'name': 'bottle_opener'}, {'frequency': 'c', 'synset': 'bouquet.n.01', 'synonyms': ['bouquet'], 'id': 135, 'def': 'an arrangement of flowers that is usually given as a present', 'name': 'bouquet'}, {'frequency': 'r', 'synset': 'bow.n.04', 'synonyms': ['bow_(weapon)'], 'id': 136, 'def': 'a weapon for shooting arrows', 'name': 'bow_(weapon)'}, {'frequency': 'f', 'synset': 'bow.n.08', 'synonyms': ['bow_(decorative_ribbons)'], 'id': 137, 'def': 'a decorative interlacing of ribbons', 'name': 'bow_(decorative_ribbons)'}, {'frequency': 'f', 'synset': 'bow_tie.n.01', 'synonyms': ['bow-tie', 'bowtie'], 'id': 138, 'def': "a man's tie that ties in a bow", 'name': 'bow-tie'}, {'frequency': 'f', 'synset': 'bowl.n.03', 'synonyms': ['bowl'], 'id': 139, 'def': 'a dish that is round and open at the top for serving foods', 'name': 'bowl'}, {'frequency': 'r', 'synset': 'bowl.n.08', 'synonyms': ['pipe_bowl'], 'id': 140, 'def': 'a small round container that is open at the top for holding tobacco', 'name': 'pipe_bowl'}, {'frequency': 'c', 'synset': 'bowler_hat.n.01', 'synonyms': ['bowler_hat', 'bowler', 'derby_hat', 'derby', 'plug_hat'], 'id': 141, 'def': 'a felt hat that is round and hard with a narrow brim', 'name': 'bowler_hat'}, {'frequency': 'r', 'synset': 'bowling_ball.n.01', 'synonyms': ['bowling_ball'], 'id': 142, 'def': 'a large ball with finger holes used in the sport of bowling', 'name': 'bowling_ball'}, {'frequency': 'f', 'synset': 'box.n.01', 'synonyms': ['box'], 'id': 143, 'def': 'a (usually rectangular) container; may have a lid', 'name': 'box'}, {'frequency': 'r', 'synset': 'boxing_glove.n.01', 'synonyms': ['boxing_glove'], 'id': 144, 'def': 'large glove coverings the fists of a fighter worn for the sport of boxing', 'name': 'boxing_glove'}, {'frequency': 'c', 'synset': 'brace.n.06', 'synonyms': ['suspenders'], 'id': 145, 'def': 'elastic straps that hold trousers up (usually used in the plural)', 'name': 'suspenders'}, {'frequency': 'f', 'synset': 'bracelet.n.02', 'synonyms': ['bracelet', 'bangle'], 'id': 146, 'def': 'jewelry worn around the wrist for decoration', 'name': 'bracelet'}, {'frequency': 'r', 'synset': 'brass.n.07', 'synonyms': ['brass_plaque'], 'id': 147, 'def': 'a memorial made of brass', 'name': 'brass_plaque'}, {'frequency': 'c', 'synset': 'brassiere.n.01', 'synonyms': ['brassiere', 'bra', 'bandeau'], 'id': 148, 'def': 'an undergarment worn by women to support their breasts', 'name': 'brassiere'}, {'frequency': 'c', 'synset': 'bread-bin.n.01', 'synonyms': ['bread-bin', 'breadbox'], 'id': 149, 'def': 'a container used to keep bread or cake in', 'name': 'bread-bin'}, {'frequency': 'f', 'synset': 'bread.n.01', 'synonyms': ['bread'], 'id': 150, 'def': 'food made from dough of flour or meal and usually raised with yeast or baking powder and then baked', 'name': 'bread'}, {'frequency': 'r', 'synset': 'breechcloth.n.01', 'synonyms': ['breechcloth', 'breechclout', 'loincloth'], 'id': 151, 'def': 'a garment that provides covering for the loins', 'name': 'breechcloth'}, {'frequency': 'f', 'synset': 'bridal_gown.n.01', 'synonyms': ['bridal_gown', 'wedding_gown', 'wedding_dress'], 'id': 152, 'def': 'a gown worn by the bride at a wedding', 'name': 'bridal_gown'}, {'frequency': 'c', 'synset': 'briefcase.n.01', 'synonyms': ['briefcase'], 'id': 153, 'def': 'a case with a handle; for carrying papers or files or books', 'name': 'briefcase'}, {'frequency': 'f', 'synset': 'broccoli.n.01', 'synonyms': ['broccoli'], 'id': 154, 'def': 'plant with dense clusters of tight green flower buds', 'name': 'broccoli'}, {'frequency': 'r', 'synset': 'brooch.n.01', 'synonyms': ['broach'], 'id': 155, 'def': 'a decorative pin worn by women', 'name': 'broach'}, {'frequency': 'c', 'synset': 'broom.n.01', 'synonyms': ['broom'], 'id': 156, 'def': 'bundle of straws or twigs attached to a long handle; used for cleaning', 'name': 'broom'}, {'frequency': 'c', 'synset': 'brownie.n.03', 'synonyms': ['brownie'], 'id': 157, 'def': 'square or bar of very rich chocolate cake usually with nuts', 'name': 'brownie'}, {'frequency': 'c', 'synset': 'brussels_sprouts.n.01', 'synonyms': ['brussels_sprouts'], 'id': 158, 'def': 'the small edible cabbage-like buds growing along a stalk', 'name': 'brussels_sprouts'}, {'frequency': 'r', 'synset': 'bubble_gum.n.01', 'synonyms': ['bubble_gum'], 'id': 159, 'def': 'a kind of chewing gum that can be blown into bubbles', 'name': 'bubble_gum'}, {'frequency': 'f', 'synset': 'bucket.n.01', 'synonyms': ['bucket', 'pail'], 'id': 160, 'def': 'a roughly cylindrical vessel that is open at the top', 'name': 'bucket'}, {'frequency': 'r', 'synset': 'buggy.n.01', 'synonyms': ['horse_buggy'], 'id': 161, 'def': 'a small lightweight carriage; drawn by a single horse', 'name': 'horse_buggy'}, {'frequency': 'c', 'synset': 'bull.n.11', 'synonyms': ['horned_cow'], 'id': 162, 'def': 'a cow with horns', 'name': 'bull'}, {'frequency': 'c', 'synset': 'bulldog.n.01', 'synonyms': ['bulldog'], 'id': 163, 'def': 'a thickset short-haired dog with a large head and strong undershot lower jaw', 'name': 'bulldog'}, {'frequency': 'r', 'synset': 'bulldozer.n.01', 'synonyms': ['bulldozer', 'dozer'], 'id': 164, 'def': 'large powerful tractor; a large blade in front flattens areas of ground', 'name': 'bulldozer'}, {'frequency': 'c', 'synset': 'bullet_train.n.01', 'synonyms': ['bullet_train'], 'id': 165, 'def': 'a high-speed passenger train', 'name': 'bullet_train'}, {'frequency': 'c', 'synset': 'bulletin_board.n.02', 'synonyms': ['bulletin_board', 'notice_board'], 'id': 166, 'def': 'a board that hangs on a wall; displays announcements', 'name': 'bulletin_board'}, {'frequency': 'r', 'synset': 'bulletproof_vest.n.01', 'synonyms': ['bulletproof_vest'], 'id': 167, 'def': 'a vest capable of resisting the impact of a bullet', 'name': 'bulletproof_vest'}, {'frequency': 'c', 'synset': 'bullhorn.n.01', 'synonyms': ['bullhorn', 'megaphone'], 'id': 168, 'def': 'a portable loudspeaker with built-in microphone and amplifier', 'name': 'bullhorn'}, {'frequency': 'f', 'synset': 'bun.n.01', 'synonyms': ['bun', 'roll'], 'id': 169, 'def': 'small rounded bread either plain or sweet', 'name': 'bun'}, {'frequency': 'c', 'synset': 'bunk_bed.n.01', 'synonyms': ['bunk_bed'], 'id': 170, 'def': 'beds built one above the other', 'name': 'bunk_bed'}, {'frequency': 'f', 'synset': 'buoy.n.01', 'synonyms': ['buoy'], 'id': 171, 'def': 'a float attached by rope to the seabed to mark channels in a harbor or underwater hazards', 'name': 'buoy'}, {'frequency': 'r', 'synset': 'burrito.n.01', 'synonyms': ['burrito'], 'id': 172, 'def': 'a flour tortilla folded around a filling', 'name': 'burrito'}, {'frequency': 'f', 'synset': 'bus.n.01', 'synonyms': ['bus_(vehicle)', 'autobus', 'charabanc', 'double-decker', 'motorbus', 'motorcoach'], 'id': 173, 'def': 'a vehicle carrying many passengers; used for public transport', 'name': 'bus_(vehicle)'}, {'frequency': 'c', 'synset': 'business_card.n.01', 'synonyms': ['business_card'], 'id': 174, 'def': "a card on which are printed the person's name and business affiliation", 'name': 'business_card'}, {'frequency': 'f', 'synset': 'butter.n.01', 'synonyms': ['butter'], 'id': 175, 'def': 'an edible emulsion of fat globules made by churning milk or cream; for cooking and table use', 'name': 'butter'}, {'frequency': 'c', 'synset': 'butterfly.n.01', 'synonyms': ['butterfly'], 'id': 176, 'def': 'insect typically having a slender body with knobbed antennae and broad colorful wings', 'name': 'butterfly'}, {'frequency': 'f', 'synset': 'button.n.01', 'synonyms': ['button'], 'id': 177, 'def': 'a round fastener sewn to shirts and coats etc to fit through buttonholes', 'name': 'button'}, {'frequency': 'f', 'synset': 'cab.n.03', 'synonyms': ['cab_(taxi)', 'taxi', 'taxicab'], 'id': 178, 'def': 'a car that takes passengers where they want to go in exchange for money', 'name': 'cab_(taxi)'}, {'frequency': 'r', 'synset': 'cabana.n.01', 'synonyms': ['cabana'], 'id': 179, 'def': 'a small tent used as a dressing room beside the sea or a swimming pool', 'name': 'cabana'}, {'frequency': 'c', 'synset': 'cabin_car.n.01', 'synonyms': ['cabin_car', 'caboose'], 'id': 180, 'def': 'a car on a freight train for use of the train crew; usually the last car on the train', 'name': 'cabin_car'}, {'frequency': 'f', 'synset': 'cabinet.n.01', 'synonyms': ['cabinet'], 'id': 181, 'def': 'a piece of furniture resembling a cupboard with doors and shelves and drawers', 'name': 'cabinet'}, {'frequency': 'r', 'synset': 'cabinet.n.03', 'synonyms': ['locker', 'storage_locker'], 'id': 182, 'def': 'a storage compartment for clothes and valuables; usually it has a lock', 'name': 'locker'}, {'frequency': 'f', 'synset': 'cake.n.03', 'synonyms': ['cake'], 'id': 183, 'def': 'baked goods made from or based on a mixture of flour, sugar, eggs, and fat', 'name': 'cake'}, {'frequency': 'c', 'synset': 'calculator.n.02', 'synonyms': ['calculator'], 'id': 184, 'def': 'a small machine that is used for mathematical calculations', 'name': 'calculator'}, {'frequency': 'f', 'synset': 'calendar.n.02', 'synonyms': ['calendar'], 'id': 185, 'def': 'a list or register of events (appointments/social events/court cases, etc)', 'name': 'calendar'}, {'frequency': 'c', 'synset': 'calf.n.01', 'synonyms': ['calf'], 'id': 186, 'def': 'young of domestic cattle', 'name': 'calf'}, {'frequency': 'c', 'synset': 'camcorder.n.01', 'synonyms': ['camcorder'], 'id': 187, 'def': 'a portable television camera and videocassette recorder', 'name': 'camcorder'}, {'frequency': 'c', 'synset': 'camel.n.01', 'synonyms': ['camel'], 'id': 188, 'def': 'cud-chewing mammal used as a draft or saddle animal in desert regions', 'name': 'camel'}, {'frequency': 'f', 'synset': 'camera.n.01', 'synonyms': ['camera'], 'id': 189, 'def': 'equipment for taking photographs', 'name': 'camera'}, {'frequency': 'c', 'synset': 'camera_lens.n.01', 'synonyms': ['camera_lens'], 'id': 190, 'def': 'a lens that focuses the image in a camera', 'name': 'camera_lens'}, {'frequency': 'c', 'synset': 'camper.n.02', 'synonyms': ['camper_(vehicle)', 'camping_bus', 'motor_home'], 'id': 191, 'def': 'a recreational vehicle equipped for camping out while traveling', 'name': 'camper_(vehicle)'}, {'frequency': 'f', 'synset': 'can.n.01', 'synonyms': ['can', 'tin_can'], 'id': 192, 'def': 'airtight sealed metal container for food or drink or paint etc.', 'name': 'can'}, {'frequency': 'c', 'synset': 'can_opener.n.01', 'synonyms': ['can_opener', 'tin_opener'], 'id': 193, 'def': 'a device for cutting cans open', 'name': 'can_opener'}, {'frequency': 'f', 'synset': 'candle.n.01', 'synonyms': ['candle', 'candlestick'], 'id': 194, 'def': 'stick of wax with a wick in the middle', 'name': 'candle'}, {'frequency': 'f', 'synset': 'candlestick.n.01', 'synonyms': ['candle_holder'], 'id': 195, 'def': 'a holder with sockets for candles', 'name': 'candle_holder'}, {'frequency': 'r', 'synset': 'candy_bar.n.01', 'synonyms': ['candy_bar'], 'id': 196, 'def': 'a candy shaped as a bar', 'name': 'candy_bar'}, {'frequency': 'c', 'synset': 'candy_cane.n.01', 'synonyms': ['candy_cane'], 'id': 197, 'def': 'a hard candy in the shape of a rod (usually with stripes)', 'name': 'candy_cane'}, {'frequency': 'c', 'synset': 'cane.n.01', 'synonyms': ['walking_cane'], 'id': 198, 'def': 'a stick that people can lean on to help them walk', 'name': 'walking_cane'}, {'frequency': 'c', 'synset': 'canister.n.02', 'synonyms': ['canister', 'cannister'], 'id': 199, 'def': 'metal container for storing dry foods such as tea or flour', 'name': 'canister'}, {'frequency': 'c', 'synset': 'canoe.n.01', 'synonyms': ['canoe'], 'id': 200, 'def': 'small and light boat; pointed at both ends; propelled with a paddle', 'name': 'canoe'}, {'frequency': 'c', 'synset': 'cantaloup.n.02', 'synonyms': ['cantaloup', 'cantaloupe'], 'id': 201, 'def': 'the fruit of a cantaloup vine; small to medium-sized melon with yellowish flesh', 'name': 'cantaloup'}, {'frequency': 'r', 'synset': 'canteen.n.01', 'synonyms': ['canteen'], 'id': 202, 'def': 'a flask for carrying water; used by soldiers or travelers', 'name': 'canteen'}, {'frequency': 'f', 'synset': 'cap.n.01', 'synonyms': ['cap_(headwear)'], 'id': 203, 'def': 'a tight-fitting headwear', 'name': 'cap_(headwear)'}, {'frequency': 'f', 'synset': 'cap.n.02', 'synonyms': ['bottle_cap', 'cap_(container_lid)'], 'id': 204, 'def': 'a top (as for a bottle)', 'name': 'bottle_cap'}, {'frequency': 'c', 'synset': 'cape.n.02', 'synonyms': ['cape'], 'id': 205, 'def': 'a sleeveless garment like a cloak but shorter', 'name': 'cape'}, {'frequency': 'c', 'synset': 'cappuccino.n.01', 'synonyms': ['cappuccino', 'coffee_cappuccino'], 'id': 206, 'def': 'equal parts of espresso and steamed milk', 'name': 'cappuccino'}, {'frequency': 'f', 'synset': 'car.n.01', 'synonyms': ['car_(automobile)', 'auto_(automobile)', 'automobile'], 'id': 207, 'def': 'a motor vehicle with four wheels', 'name': 'car_(automobile)'}, {'frequency': 'f', 'synset': 'car.n.02', 'synonyms': ['railcar_(part_of_a_train)', 'railway_car_(part_of_a_train)', 'railroad_car_(part_of_a_train)'], 'id': 208, 'def': 'a wheeled vehicle adapted to the rails of railroad (mark each individual railcar separately)', 'name': 'railcar_(part_of_a_train)'}, {'frequency': 'r', 'synset': 'car.n.04', 'synonyms': ['elevator_car'], 'id': 209, 'def': 'where passengers ride up and down', 'name': 'elevator_car'}, {'frequency': 'r', 'synset': 'car_battery.n.01', 'synonyms': ['car_battery', 'automobile_battery'], 'id': 210, 'def': 'a battery in a motor vehicle', 'name': 'car_battery'}, {'frequency': 'c', 'synset': 'card.n.02', 'synonyms': ['identity_card'], 'id': 211, 'def': 'a card certifying the identity of the bearer', 'name': 'identity_card'}, {'frequency': 'c', 'synset': 'card.n.03', 'synonyms': ['card'], 'id': 212, 'def': 'a rectangular piece of paper used to send messages (e.g. greetings or pictures)', 'name': 'card'}, {'frequency': 'c', 'synset': 'cardigan.n.01', 'synonyms': ['cardigan'], 'id': 213, 'def': 'knitted jacket that is fastened up the front with buttons or a zipper', 'name': 'cardigan'}, {'frequency': 'r', 'synset': 'cargo_ship.n.01', 'synonyms': ['cargo_ship', 'cargo_vessel'], 'id': 214, 'def': 'a ship designed to carry cargo', 'name': 'cargo_ship'}, {'frequency': 'r', 'synset': 'carnation.n.01', 'synonyms': ['carnation'], 'id': 215, 'def': 'plant with pink to purple-red spice-scented usually double flowers', 'name': 'carnation'}, {'frequency': 'c', 'synset': 'carriage.n.02', 'synonyms': ['horse_carriage'], 'id': 216, 'def': 'a vehicle with wheels drawn by one or more horses', 'name': 'horse_carriage'}, {'frequency': 'f', 'synset': 'carrot.n.01', 'synonyms': ['carrot'], 'id': 217, 'def': 'deep orange edible root of the cultivated carrot plant', 'name': 'carrot'}, {'frequency': 'f', 'synset': 'carryall.n.01', 'synonyms': ['tote_bag'], 'id': 218, 'def': 'a capacious bag or basket', 'name': 'tote_bag'}, {'frequency': 'c', 'synset': 'cart.n.01', 'synonyms': ['cart'], 'id': 219, 'def': 'a heavy open wagon usually having two wheels and drawn by an animal', 'name': 'cart'}, {'frequency': 'c', 'synset': 'carton.n.02', 'synonyms': ['carton'], 'id': 220, 'def': 'a container made of cardboard for holding food or drink', 'name': 'carton'}, {'frequency': 'c', 'synset': 'cash_register.n.01', 'synonyms': ['cash_register', 'register_(for_cash_transactions)'], 'id': 221, 'def': 'a cashbox with an adding machine to register transactions', 'name': 'cash_register'}, {'frequency': 'r', 'synset': 'casserole.n.01', 'synonyms': ['casserole'], 'id': 222, 'def': 'food cooked and served in a casserole', 'name': 'casserole'}, {'frequency': 'r', 'synset': 'cassette.n.01', 'synonyms': ['cassette'], 'id': 223, 'def': 'a container that holds a magnetic tape used for recording or playing sound or video', 'name': 'cassette'}, {'frequency': 'c', 'synset': 'cast.n.05', 'synonyms': ['cast', 'plaster_cast', 'plaster_bandage'], 'id': 224, 'def': 'bandage consisting of a firm covering that immobilizes broken bones while they heal', 'name': 'cast'}, {'frequency': 'f', 'synset': 'cat.n.01', 'synonyms': ['cat'], 'id': 225, 'def': 'a domestic house cat', 'name': 'cat'}, {'frequency': 'f', 'synset': 'cauliflower.n.02', 'synonyms': ['cauliflower'], 'id': 226, 'def': 'edible compact head of white undeveloped flowers', 'name': 'cauliflower'}, {'frequency': 'c', 'synset': 'cayenne.n.02', 'synonyms': ['cayenne_(spice)', 'cayenne_pepper_(spice)', 'red_pepper_(spice)'], 'id': 227, 'def': 'ground pods and seeds of pungent red peppers of the genus Capsicum', 'name': 'cayenne_(spice)'}, {'frequency': 'c', 'synset': 'cd_player.n.01', 'synonyms': ['CD_player'], 'id': 228, 'def': 'electronic equipment for playing compact discs (CDs)', 'name': 'CD_player'}, {'frequency': 'f', 'synset': 'celery.n.01', 'synonyms': ['celery'], 'id': 229, 'def': 'widely cultivated herb with aromatic leaf stalks that are eaten raw or cooked', 'name': 'celery'}, {'frequency': 'f', 'synset': 'cellular_telephone.n.01', 'synonyms': ['cellular_telephone', 'cellular_phone', 'cellphone', 'mobile_phone', 'smart_phone'], 'id': 230, 'def': 'a hand-held mobile telephone', 'name': 'cellular_telephone'}, {'frequency': 'r', 'synset': 'chain_mail.n.01', 'synonyms': ['chain_mail', 'ring_mail', 'chain_armor', 'chain_armour', 'ring_armor', 'ring_armour'], 'id': 231, 'def': '(Middle Ages) flexible armor made of interlinked metal rings', 'name': 'chain_mail'}, {'frequency': 'f', 'synset': 'chair.n.01', 'synonyms': ['chair'], 'id': 232, 'def': 'a seat for one person, with a support for the back', 'name': 'chair'}, {'frequency': 'r', 'synset': 'chaise_longue.n.01', 'synonyms': ['chaise_longue', 'chaise', 'daybed'], 'id': 233, 'def': 'a long chair; for reclining', 'name': 'chaise_longue'}, {'frequency': 'r', 'synset': 'chalice.n.01', 'synonyms': ['chalice'], 'id': 234, 'def': 'a bowl-shaped drinking vessel; especially the Eucharistic cup', 'name': 'chalice'}, {'frequency': 'f', 'synset': 'chandelier.n.01', 'synonyms': ['chandelier'], 'id': 235, 'def': 'branched lighting fixture; often ornate; hangs from the ceiling', 'name': 'chandelier'}, {'frequency': 'r', 'synset': 'chap.n.04', 'synonyms': ['chap'], 'id': 236, 'def': 'leather leggings without a seat; worn over trousers by cowboys to protect their legs', 'name': 'chap'}, {'frequency': 'r', 'synset': 'checkbook.n.01', 'synonyms': ['checkbook', 'chequebook'], 'id': 237, 'def': 'a book issued to holders of checking accounts', 'name': 'checkbook'}, {'frequency': 'r', 'synset': 'checkerboard.n.01', 'synonyms': ['checkerboard'], 'id': 238, 'def': 'a board having 64 squares of two alternating colors', 'name': 'checkerboard'}, {'frequency': 'c', 'synset': 'cherry.n.03', 'synonyms': ['cherry'], 'id': 239, 'def': 'a red fruit with a single hard stone', 'name': 'cherry'}, {'frequency': 'r', 'synset': 'chessboard.n.01', 'synonyms': ['chessboard'], 'id': 240, 'def': 'a checkerboard used to play chess', 'name': 'chessboard'}, {'frequency': 'c', 'synset': 'chicken.n.02', 'synonyms': ['chicken_(animal)'], 'id': 241, 'def': 'a domestic fowl bred for flesh or eggs', 'name': 'chicken_(animal)'}, {'frequency': 'c', 'synset': 'chickpea.n.01', 'synonyms': ['chickpea', 'garbanzo'], 'id': 242, 'def': 'the seed of the chickpea plant; usually dried', 'name': 'chickpea'}, {'frequency': 'c', 'synset': 'chili.n.02', 'synonyms': ['chili_(vegetable)', 'chili_pepper_(vegetable)', 'chilli_(vegetable)', 'chilly_(vegetable)', 'chile_(vegetable)'], 'id': 243, 'def': 'very hot and finely tapering pepper of special pungency', 'name': 'chili_(vegetable)'}, {'frequency': 'r', 'synset': 'chime.n.01', 'synonyms': ['chime', 'gong'], 'id': 244, 'def': 'an instrument consisting of a set of bells that are struck with a hammer', 'name': 'chime'}, {'frequency': 'r', 'synset': 'chinaware.n.01', 'synonyms': ['chinaware'], 'id': 245, 'def': 'dishware made of high quality porcelain', 'name': 'chinaware'}, {'frequency': 'c', 'synset': 'chip.n.04', 'synonyms': ['crisp_(potato_chip)', 'potato_chip'], 'id': 246, 'def': 'a thin crisp slice of potato fried in deep fat', 'name': 'crisp_(potato_chip)'}, {'frequency': 'r', 'synset': 'chip.n.06', 'synonyms': ['poker_chip'], 'id': 247, 'def': 'a small disk-shaped counter used to represent money when gambling', 'name': 'poker_chip'}, {'frequency': 'c', 'synset': 'chocolate_bar.n.01', 'synonyms': ['chocolate_bar'], 'id': 248, 'def': 'a bar of chocolate candy', 'name': 'chocolate_bar'}, {'frequency': 'c', 'synset': 'chocolate_cake.n.01', 'synonyms': ['chocolate_cake'], 'id': 249, 'def': 'cake containing chocolate', 'name': 'chocolate_cake'}, {'frequency': 'r', 'synset': 'chocolate_milk.n.01', 'synonyms': ['chocolate_milk'], 'id': 250, 'def': 'milk flavored with chocolate syrup', 'name': 'chocolate_milk'}, {'frequency': 'r', 'synset': 'chocolate_mousse.n.01', 'synonyms': ['chocolate_mousse'], 'id': 251, 'def': 'dessert mousse made with chocolate', 'name': 'chocolate_mousse'}, {'frequency': 'f', 'synset': 'choker.n.03', 'synonyms': ['choker', 'collar', 'neckband'], 'id': 252, 'def': 'shirt collar, animal collar, or tight-fitting necklace', 'name': 'choker'}, {'frequency': 'f', 'synset': 'chopping_board.n.01', 'synonyms': ['chopping_board', 'cutting_board', 'chopping_block'], 'id': 253, 'def': 'a wooden board where meats or vegetables can be cut', 'name': 'chopping_board'}, {'frequency': 'f', 'synset': 'chopstick.n.01', 'synonyms': ['chopstick'], 'id': 254, 'def': 'one of a pair of slender sticks used as oriental tableware to eat food with', 'name': 'chopstick'}, {'frequency': 'f', 'synset': 'christmas_tree.n.05', 'synonyms': ['Christmas_tree'], 'id': 255, 'def': 'an ornamented evergreen used as a Christmas decoration', 'name': 'Christmas_tree'}, {'frequency': 'c', 'synset': 'chute.n.02', 'synonyms': ['slide'], 'id': 256, 'def': 'sloping channel through which things can descend', 'name': 'slide'}, {'frequency': 'r', 'synset': 'cider.n.01', 'synonyms': ['cider', 'cyder'], 'id': 257, 'def': 'a beverage made from juice pressed from apples', 'name': 'cider'}, {'frequency': 'r', 'synset': 'cigar_box.n.01', 'synonyms': ['cigar_box'], 'id': 258, 'def': 'a box for holding cigars', 'name': 'cigar_box'}, {'frequency': 'f', 'synset': 'cigarette.n.01', 'synonyms': ['cigarette'], 'id': 259, 'def': 'finely ground tobacco wrapped in paper; for smoking', 'name': 'cigarette'}, {'frequency': 'c', 'synset': 'cigarette_case.n.01', 'synonyms': ['cigarette_case', 'cigarette_pack'], 'id': 260, 'def': 'a small flat case for holding cigarettes', 'name': 'cigarette_case'}, {'frequency': 'f', 'synset': 'cistern.n.02', 'synonyms': ['cistern', 'water_tank'], 'id': 261, 'def': 'a tank that holds the water used to flush a toilet', 'name': 'cistern'}, {'frequency': 'r', 'synset': 'clarinet.n.01', 'synonyms': ['clarinet'], 'id': 262, 'def': 'a single-reed instrument with a straight tube', 'name': 'clarinet'}, {'frequency': 'c', 'synset': 'clasp.n.01', 'synonyms': ['clasp'], 'id': 263, 'def': 'a fastener (as a buckle or hook) that is used to hold two things together', 'name': 'clasp'}, {'frequency': 'c', 'synset': 'cleansing_agent.n.01', 'synonyms': ['cleansing_agent', 'cleanser', 'cleaner'], 'id': 264, 'def': 'a preparation used in cleaning something', 'name': 'cleansing_agent'}, {'frequency': 'r', 'synset': 'cleat.n.02', 'synonyms': ['cleat_(for_securing_rope)'], 'id': 265, 'def': 'a fastener (usually with two projecting horns) around which a rope can be secured', 'name': 'cleat_(for_securing_rope)'}, {'frequency': 'r', 'synset': 'clementine.n.01', 'synonyms': ['clementine'], 'id': 266, 'def': 'a variety of mandarin orange', 'name': 'clementine'}, {'frequency': 'c', 'synset': 'clip.n.03', 'synonyms': ['clip'], 'id': 267, 'def': 'any of various small fasteners used to hold loose articles together', 'name': 'clip'}, {'frequency': 'c', 'synset': 'clipboard.n.01', 'synonyms': ['clipboard'], 'id': 268, 'def': 'a small writing board with a clip at the top for holding papers', 'name': 'clipboard'}, {'frequency': 'r', 'synset': 'clipper.n.03', 'synonyms': ['clippers_(for_plants)'], 'id': 269, 'def': 'shears for cutting grass or shrubbery (often used in the plural)', 'name': 'clippers_(for_plants)'}, {'frequency': 'r', 'synset': 'cloak.n.02', 'synonyms': ['cloak'], 'id': 270, 'def': 'a loose outer garment', 'name': 'cloak'}, {'frequency': 'f', 'synset': 'clock.n.01', 'synonyms': ['clock', 'timepiece', 'timekeeper'], 'id': 271, 'def': 'a timepiece that shows the time of day', 'name': 'clock'}, {'frequency': 'f', 'synset': 'clock_tower.n.01', 'synonyms': ['clock_tower'], 'id': 272, 'def': 'a tower with a large clock visible high up on an outside face', 'name': 'clock_tower'}, {'frequency': 'c', 'synset': 'clothes_hamper.n.01', 'synonyms': ['clothes_hamper', 'laundry_basket', 'clothes_basket'], 'id': 273, 'def': 'a hamper that holds dirty clothes to be washed or wet clothes to be dried', 'name': 'clothes_hamper'}, {'frequency': 'c', 'synset': 'clothespin.n.01', 'synonyms': ['clothespin', 'clothes_peg'], 'id': 274, 'def': 'wood or plastic fastener; for holding clothes on a clothesline', 'name': 'clothespin'}, {'frequency': 'r', 'synset': 'clutch_bag.n.01', 'synonyms': ['clutch_bag'], 'id': 275, 'def': "a woman's strapless purse that is carried in the hand", 'name': 'clutch_bag'}, {'frequency': 'f', 'synset': 'coaster.n.03', 'synonyms': ['coaster'], 'id': 276, 'def': 'a covering (plate or mat) that protects the surface of a table', 'name': 'coaster'}, {'frequency': 'f', 'synset': 'coat.n.01', 'synonyms': ['coat'], 'id': 277, 'def': 'an outer garment that has sleeves and covers the body from shoulder down', 'name': 'coat'}, {'frequency': 'c', 'synset': 'coat_hanger.n.01', 'synonyms': ['coat_hanger', 'clothes_hanger', 'dress_hanger'], 'id': 278, 'def': "a hanger that is shaped like a person's shoulders", 'name': 'coat_hanger'}, {'frequency': 'c', 'synset': 'coatrack.n.01', 'synonyms': ['coatrack', 'hatrack'], 'id': 279, 'def': 'a rack with hooks for temporarily holding coats and hats', 'name': 'coatrack'}, {'frequency': 'c', 'synset': 'cock.n.04', 'synonyms': ['cock', 'rooster'], 'id': 280, 'def': 'adult male chicken', 'name': 'cock'}, {'frequency': 'r', 'synset': 'cockroach.n.01', 'synonyms': ['cockroach'], 'id': 281, 'def': 'any of numerous chiefly nocturnal insects; some are domestic pests', 'name': 'cockroach'}, {'frequency': 'r', 'synset': 'cocoa.n.01', 'synonyms': ['cocoa_(beverage)', 'hot_chocolate_(beverage)', 'drinking_chocolate'], 'id': 282, 'def': 'a beverage made from cocoa powder and milk and sugar; usually drunk hot', 'name': 'cocoa_(beverage)'}, {'frequency': 'c', 'synset': 'coconut.n.02', 'synonyms': ['coconut', 'cocoanut'], 'id': 283, 'def': 'large hard-shelled brown oval nut with a fibrous husk', 'name': 'coconut'}, {'frequency': 'f', 'synset': 'coffee_maker.n.01', 'synonyms': ['coffee_maker', 'coffee_machine'], 'id': 284, 'def': 'a kitchen appliance for brewing coffee automatically', 'name': 'coffee_maker'}, {'frequency': 'f', 'synset': 'coffee_table.n.01', 'synonyms': ['coffee_table', 'cocktail_table'], 'id': 285, 'def': 'low table where magazines can be placed and coffee or cocktails are served', 'name': 'coffee_table'}, {'frequency': 'c', 'synset': 'coffeepot.n.01', 'synonyms': ['coffeepot'], 'id': 286, 'def': 'tall pot in which coffee is brewed', 'name': 'coffeepot'}, {'frequency': 'r', 'synset': 'coil.n.05', 'synonyms': ['coil'], 'id': 287, 'def': 'tubing that is wound in a spiral', 'name': 'coil'}, {'frequency': 'c', 'synset': 'coin.n.01', 'synonyms': ['coin'], 'id': 288, 'def': 'a flat metal piece (usually a disc) used as money', 'name': 'coin'}, {'frequency': 'c', 'synset': 'colander.n.01', 'synonyms': ['colander', 'cullender'], 'id': 289, 'def': 'bowl-shaped strainer; used to wash or drain foods', 'name': 'colander'}, {'frequency': 'c', 'synset': 'coleslaw.n.01', 'synonyms': ['coleslaw', 'slaw'], 'id': 290, 'def': 'basically shredded cabbage', 'name': 'coleslaw'}, {'frequency': 'r', 'synset': 'coloring_material.n.01', 'synonyms': ['coloring_material', 'colouring_material'], 'id': 291, 'def': 'any material used for its color', 'name': 'coloring_material'}, {'frequency': 'r', 'synset': 'combination_lock.n.01', 'synonyms': ['combination_lock'], 'id': 292, 'def': 'lock that can be opened only by turning dials in a special sequence', 'name': 'combination_lock'}, {'frequency': 'c', 'synset': 'comforter.n.04', 'synonyms': ['pacifier', 'teething_ring'], 'id': 293, 'def': 'device used for an infant to suck or bite on', 'name': 'pacifier'}, {'frequency': 'r', 'synset': 'comic_book.n.01', 'synonyms': ['comic_book'], 'id': 294, 'def': 'a magazine devoted to comic strips', 'name': 'comic_book'}, {'frequency': 'r', 'synset': 'compass.n.01', 'synonyms': ['compass'], 'id': 295, 'def': 'navigational instrument for finding directions', 'name': 'compass'}, {'frequency': 'f', 'synset': 'computer_keyboard.n.01', 'synonyms': ['computer_keyboard', 'keyboard_(computer)'], 'id': 296, 'def': 'a keyboard that is a data input device for computers', 'name': 'computer_keyboard'}, {'frequency': 'f', 'synset': 'condiment.n.01', 'synonyms': ['condiment'], 'id': 297, 'def': 'a preparation (a sauce or relish or spice) to enhance flavor or enjoyment', 'name': 'condiment'}, {'frequency': 'f', 'synset': 'cone.n.01', 'synonyms': ['cone', 'traffic_cone'], 'id': 298, 'def': 'a cone-shaped object used to direct traffic', 'name': 'cone'}, {'frequency': 'f', 'synset': 'control.n.09', 'synonyms': ['control', 'controller'], 'id': 299, 'def': 'a mechanism that controls the operation of a machine', 'name': 'control'}, {'frequency': 'r', 'synset': 'convertible.n.01', 'synonyms': ['convertible_(automobile)'], 'id': 300, 'def': 'a car that has top that can be folded or removed', 'name': 'convertible_(automobile)'}, {'frequency': 'r', 'synset': 'convertible.n.03', 'synonyms': ['sofa_bed'], 'id': 301, 'def': 'a sofa that can be converted into a bed', 'name': 'sofa_bed'}, {'frequency': 'r', 'synset': 'cooker.n.01', 'synonyms': ['cooker'], 'id': 302, 'def': 'a utensil for cooking', 'name': 'cooker'}, {'frequency': 'f', 'synset': 'cookie.n.01', 'synonyms': ['cookie', 'cooky', 'biscuit_(cookie)'], 'id': 303, 'def': "any of various small flat sweet cakes (`biscuit' is the British term)", 'name': 'cookie'}, {'frequency': 'r', 'synset': 'cooking_utensil.n.01', 'synonyms': ['cooking_utensil'], 'id': 304, 'def': 'a kitchen utensil made of material that does not melt easily; used for cooking', 'name': 'cooking_utensil'}, {'frequency': 'f', 'synset': 'cooler.n.01', 'synonyms': ['cooler_(for_food)', 'ice_chest'], 'id': 305, 'def': 'an insulated box for storing food often with ice', 'name': 'cooler_(for_food)'}, {'frequency': 'f', 'synset': 'cork.n.04', 'synonyms': ['cork_(bottle_plug)', 'bottle_cork'], 'id': 306, 'def': 'the plug in the mouth of a bottle (especially a wine bottle)', 'name': 'cork_(bottle_plug)'}, {'frequency': 'r', 'synset': 'corkboard.n.01', 'synonyms': ['corkboard'], 'id': 307, 'def': 'a sheet consisting of cork granules', 'name': 'corkboard'}, {'frequency': 'c', 'synset': 'corkscrew.n.01', 'synonyms': ['corkscrew', 'bottle_screw'], 'id': 308, 'def': 'a bottle opener that pulls corks', 'name': 'corkscrew'}, {'frequency': 'f', 'synset': 'corn.n.03', 'synonyms': ['edible_corn', 'corn', 'maize'], 'id': 309, 'def': 'ears or kernels of corn that can be prepared and served for human food (only mark individual ears or kernels)', 'name': 'edible_corn'}, {'frequency': 'r', 'synset': 'cornbread.n.01', 'synonyms': ['cornbread'], 'id': 310, 'def': 'bread made primarily of cornmeal', 'name': 'cornbread'}, {'frequency': 'c', 'synset': 'cornet.n.01', 'synonyms': ['cornet', 'horn', 'trumpet'], 'id': 311, 'def': 'a brass musical instrument with a narrow tube and a flared bell and many valves', 'name': 'cornet'}, {'frequency': 'c', 'synset': 'cornice.n.01', 'synonyms': ['cornice', 'valance', 'valance_board', 'pelmet'], 'id': 312, 'def': 'a decorative framework to conceal curtain fixtures at the top of a window casing', 'name': 'cornice'}, {'frequency': 'r', 'synset': 'cornmeal.n.01', 'synonyms': ['cornmeal'], 'id': 313, 'def': 'coarsely ground corn', 'name': 'cornmeal'}, {'frequency': 'c', 'synset': 'corset.n.01', 'synonyms': ['corset', 'girdle'], 'id': 314, 'def': "a woman's close-fitting foundation garment", 'name': 'corset'}, {'frequency': 'c', 'synset': 'costume.n.04', 'synonyms': ['costume'], 'id': 315, 'def': 'the attire characteristic of a country or a time or a social class', 'name': 'costume'}, {'frequency': 'r', 'synset': 'cougar.n.01', 'synonyms': ['cougar', 'puma', 'catamount', 'mountain_lion', 'panther'], 'id': 316, 'def': 'large American feline resembling a lion', 'name': 'cougar'}, {'frequency': 'r', 'synset': 'coverall.n.01', 'synonyms': ['coverall'], 'id': 317, 'def': 'a loose-fitting protective garment that is worn over other clothing', 'name': 'coverall'}, {'frequency': 'c', 'synset': 'cowbell.n.01', 'synonyms': ['cowbell'], 'id': 318, 'def': 'a bell hung around the neck of cow so that the cow can be easily located', 'name': 'cowbell'}, {'frequency': 'f', 'synset': 'cowboy_hat.n.01', 'synonyms': ['cowboy_hat', 'ten-gallon_hat'], 'id': 319, 'def': 'a hat with a wide brim and a soft crown; worn by American ranch hands', 'name': 'cowboy_hat'}, {'frequency': 'c', 'synset': 'crab.n.01', 'synonyms': ['crab_(animal)'], 'id': 320, 'def': 'decapod having eyes on short stalks and a broad flattened shell and pincers', 'name': 'crab_(animal)'}, {'frequency': 'r', 'synset': 'crab.n.05', 'synonyms': ['crabmeat'], 'id': 321, 'def': 'the edible flesh of any of various crabs', 'name': 'crabmeat'}, {'frequency': 'c', 'synset': 'cracker.n.01', 'synonyms': ['cracker'], 'id': 322, 'def': 'a thin crisp wafer', 'name': 'cracker'}, {'frequency': 'r', 'synset': 'crape.n.01', 'synonyms': ['crape', 'crepe', 'French_pancake'], 'id': 323, 'def': 'small very thin pancake', 'name': 'crape'}, {'frequency': 'f', 'synset': 'crate.n.01', 'synonyms': ['crate'], 'id': 324, 'def': 'a rugged box (usually made of wood); used for shipping', 'name': 'crate'}, {'frequency': 'c', 'synset': 'crayon.n.01', 'synonyms': ['crayon', 'wax_crayon'], 'id': 325, 'def': 'writing or drawing implement made of a colored stick of composition wax', 'name': 'crayon'}, {'frequency': 'r', 'synset': 'cream_pitcher.n.01', 'synonyms': ['cream_pitcher'], 'id': 326, 'def': 'a small pitcher for serving cream', 'name': 'cream_pitcher'}, {'frequency': 'c', 'synset': 'crescent_roll.n.01', 'synonyms': ['crescent_roll', 'croissant'], 'id': 327, 'def': 'very rich flaky crescent-shaped roll', 'name': 'crescent_roll'}, {'frequency': 'c', 'synset': 'crib.n.01', 'synonyms': ['crib', 'cot'], 'id': 328, 'def': 'baby bed with high sides made of slats', 'name': 'crib'}, {'frequency': 'c', 'synset': 'crock.n.03', 'synonyms': ['crock_pot', 'earthenware_jar'], 'id': 329, 'def': 'an earthen jar (made of baked clay) or a modern electric crockpot', 'name': 'crock_pot'}, {'frequency': 'f', 'synset': 'crossbar.n.01', 'synonyms': ['crossbar'], 'id': 330, 'def': 'a horizontal bar that goes across something', 'name': 'crossbar'}, {'frequency': 'r', 'synset': 'crouton.n.01', 'synonyms': ['crouton'], 'id': 331, 'def': 'a small piece of toasted or fried bread; served in soup or salads', 'name': 'crouton'}, {'frequency': 'c', 'synset': 'crow.n.01', 'synonyms': ['crow'], 'id': 332, 'def': 'black birds having a raucous call', 'name': 'crow'}, {'frequency': 'r', 'synset': 'crowbar.n.01', 'synonyms': ['crowbar', 'wrecking_bar', 'pry_bar'], 'id': 333, 'def': 'a heavy iron lever with one end forged into a wedge', 'name': 'crowbar'}, {'frequency': 'c', 'synset': 'crown.n.04', 'synonyms': ['crown'], 'id': 334, 'def': 'an ornamental jeweled headdress signifying sovereignty', 'name': 'crown'}, {'frequency': 'c', 'synset': 'crucifix.n.01', 'synonyms': ['crucifix'], 'id': 335, 'def': 'representation of the cross on which Jesus died', 'name': 'crucifix'}, {'frequency': 'c', 'synset': 'cruise_ship.n.01', 'synonyms': ['cruise_ship', 'cruise_liner'], 'id': 336, 'def': 'a passenger ship used commercially for pleasure cruises', 'name': 'cruise_ship'}, {'frequency': 'c', 'synset': 'cruiser.n.01', 'synonyms': ['police_cruiser', 'patrol_car', 'police_car', 'squad_car'], 'id': 337, 'def': 'a car in which policemen cruise the streets', 'name': 'police_cruiser'}, {'frequency': 'f', 'synset': 'crumb.n.03', 'synonyms': ['crumb'], 'id': 338, 'def': 'small piece of e.g. bread or cake', 'name': 'crumb'}, {'frequency': 'c', 'synset': 'crutch.n.01', 'synonyms': ['crutch'], 'id': 339, 'def': 'a wooden or metal staff that fits under the armpit and reaches to the ground', 'name': 'crutch'}, {'frequency': 'c', 'synset': 'cub.n.03', 'synonyms': ['cub_(animal)'], 'id': 340, 'def': 'the young of certain carnivorous mammals such as the bear or wolf or lion', 'name': 'cub_(animal)'}, {'frequency': 'c', 'synset': 'cube.n.05', 'synonyms': ['cube', 'square_block'], 'id': 341, 'def': 'a block in the (approximate) shape of a cube', 'name': 'cube'}, {'frequency': 'f', 'synset': 'cucumber.n.02', 'synonyms': ['cucumber', 'cuke'], 'id': 342, 'def': 'cylindrical green fruit with thin green rind and white flesh eaten as a vegetable', 'name': 'cucumber'}, {'frequency': 'c', 'synset': 'cufflink.n.01', 'synonyms': ['cufflink'], 'id': 343, 'def': 'jewelry consisting of linked buttons used to fasten the cuffs of a shirt', 'name': 'cufflink'}, {'frequency': 'f', 'synset': 'cup.n.01', 'synonyms': ['cup'], 'id': 344, 'def': 'a small open container usually used for drinking; usually has a handle', 'name': 'cup'}, {'frequency': 'c', 'synset': 'cup.n.08', 'synonyms': ['trophy_cup'], 'id': 345, 'def': 'a metal award or cup-shaped vessel with handles that is awarded as a trophy to a competition winner', 'name': 'trophy_cup'}, {'frequency': 'f', 'synset': 'cupboard.n.01', 'synonyms': ['cupboard', 'closet'], 'id': 346, 'def': 'a small room (or recess) or cabinet used for storage space', 'name': 'cupboard'}, {'frequency': 'f', 'synset': 'cupcake.n.01', 'synonyms': ['cupcake'], 'id': 347, 'def': 'small cake baked in a muffin tin', 'name': 'cupcake'}, {'frequency': 'r', 'synset': 'curler.n.01', 'synonyms': ['hair_curler', 'hair_roller', 'hair_crimper'], 'id': 348, 'def': 'a cylindrical tube around which the hair is wound to curl it', 'name': 'hair_curler'}, {'frequency': 'r', 'synset': 'curling_iron.n.01', 'synonyms': ['curling_iron'], 'id': 349, 'def': 'a cylindrical home appliance that heats hair that has been curled around it', 'name': 'curling_iron'}, {'frequency': 'f', 'synset': 'curtain.n.01', 'synonyms': ['curtain', 'drapery'], 'id': 350, 'def': 'hanging cloth used as a blind (especially for a window)', 'name': 'curtain'}, {'frequency': 'f', 'synset': 'cushion.n.03', 'synonyms': ['cushion'], 'id': 351, 'def': 'a soft bag filled with air or padding such as feathers or foam rubber', 'name': 'cushion'}, {'frequency': 'r', 'synset': 'cylinder.n.04', 'synonyms': ['cylinder'], 'id': 352, 'def': 'a cylindrical container', 'name': 'cylinder'}, {'frequency': 'r', 'synset': 'cymbal.n.01', 'synonyms': ['cymbal'], 'id': 353, 'def': 'a percussion instrument consisting of a concave brass disk', 'name': 'cymbal'}, {'frequency': 'r', 'synset': 'dagger.n.01', 'synonyms': ['dagger'], 'id': 354, 'def': 'a short knife with a pointed blade used for piercing or stabbing', 'name': 'dagger'}, {'frequency': 'r', 'synset': 'dalmatian.n.02', 'synonyms': ['dalmatian'], 'id': 355, 'def': 'a large breed having a smooth white coat with black or brown spots', 'name': 'dalmatian'}, {'frequency': 'c', 'synset': 'dartboard.n.01', 'synonyms': ['dartboard'], 'id': 356, 'def': 'a circular board of wood or cork used as the target in the game of darts', 'name': 'dartboard'}, {'frequency': 'r', 'synset': 'date.n.08', 'synonyms': ['date_(fruit)'], 'id': 357, 'def': 'sweet edible fruit of the date palm with a single long woody seed', 'name': 'date_(fruit)'}, {'frequency': 'f', 'synset': 'deck_chair.n.01', 'synonyms': ['deck_chair', 'beach_chair'], 'id': 358, 'def': 'a folding chair for use outdoors; a wooden frame supports a length of canvas', 'name': 'deck_chair'}, {'frequency': 'c', 'synset': 'deer.n.01', 'synonyms': ['deer', 'cervid'], 'id': 359, 'def': "distinguished from Bovidae by the male's having solid deciduous antlers", 'name': 'deer'}, {'frequency': 'c', 'synset': 'dental_floss.n.01', 'synonyms': ['dental_floss', 'floss'], 'id': 360, 'def': 'a soft thread for cleaning the spaces between the teeth', 'name': 'dental_floss'}, {'frequency': 'f', 'synset': 'desk.n.01', 'synonyms': ['desk'], 'id': 361, 'def': 'a piece of furniture with a writing surface and usually drawers or other compartments', 'name': 'desk'}, {'frequency': 'r', 'synset': 'detergent.n.01', 'synonyms': ['detergent'], 'id': 362, 'def': 'a surface-active chemical widely used in industry and laundering', 'name': 'detergent'}, {'frequency': 'c', 'synset': 'diaper.n.01', 'synonyms': ['diaper'], 'id': 363, 'def': 'garment consisting of a folded cloth drawn up between the legs and fastened at the waist', 'name': 'diaper'}, {'frequency': 'r', 'synset': 'diary.n.01', 'synonyms': ['diary', 'journal'], 'id': 364, 'def': 'yearly planner book', 'name': 'diary'}, {'frequency': 'r', 'synset': 'die.n.01', 'synonyms': ['die', 'dice'], 'id': 365, 'def': 'a small cube with 1 to 6 spots on the six faces; used in gambling', 'name': 'die'}, {'frequency': 'r', 'synset': 'dinghy.n.01', 'synonyms': ['dinghy', 'dory', 'rowboat'], 'id': 366, 'def': 'a small boat of shallow draft with seats and oars with which it is propelled', 'name': 'dinghy'}, {'frequency': 'f', 'synset': 'dining_table.n.01', 'synonyms': ['dining_table'], 'id': 367, 'def': 'a table at which meals are served', 'name': 'dining_table'}, {'frequency': 'r', 'synset': 'dinner_jacket.n.01', 'synonyms': ['tux', 'tuxedo'], 'id': 368, 'def': 'semiformal evening dress for men', 'name': 'tux'}, {'frequency': 'f', 'synset': 'dish.n.01', 'synonyms': ['dish'], 'id': 369, 'def': 'a piece of dishware normally used as a container for holding or serving food', 'name': 'dish'}, {'frequency': 'c', 'synset': 'dish.n.05', 'synonyms': ['dish_antenna'], 'id': 370, 'def': 'directional antenna consisting of a parabolic reflector', 'name': 'dish_antenna'}, {'frequency': 'c', 'synset': 'dishrag.n.01', 'synonyms': ['dishrag', 'dishcloth'], 'id': 371, 'def': 'a cloth for washing dishes or cleaning in general', 'name': 'dishrag'}, {'frequency': 'f', 'synset': 'dishtowel.n.01', 'synonyms': ['dishtowel', 'tea_towel'], 'id': 372, 'def': 'a towel for drying dishes', 'name': 'dishtowel'}, {'frequency': 'f', 'synset': 'dishwasher.n.01', 'synonyms': ['dishwasher', 'dishwashing_machine'], 'id': 373, 'def': 'a machine for washing dishes', 'name': 'dishwasher'}, {'frequency': 'r', 'synset': 'dishwasher_detergent.n.01', 'synonyms': ['dishwasher_detergent', 'dishwashing_detergent', 'dishwashing_liquid', 'dishsoap'], 'id': 374, 'def': 'dishsoap or dish detergent designed for use in dishwashers', 'name': 'dishwasher_detergent'}, {'frequency': 'f', 'synset': 'dispenser.n.01', 'synonyms': ['dispenser'], 'id': 375, 'def': 'a container so designed that the contents can be used in prescribed amounts', 'name': 'dispenser'}, {'frequency': 'r', 'synset': 'diving_board.n.01', 'synonyms': ['diving_board'], 'id': 376, 'def': 'a springboard from which swimmers can dive', 'name': 'diving_board'}, {'frequency': 'f', 'synset': 'dixie_cup.n.01', 'synonyms': ['Dixie_cup', 'paper_cup'], 'id': 377, 'def': 'a disposable cup made of paper; for holding drinks', 'name': 'Dixie_cup'}, {'frequency': 'f', 'synset': 'dog.n.01', 'synonyms': ['dog'], 'id': 378, 'def': 'a common domesticated dog', 'name': 'dog'}, {'frequency': 'f', 'synset': 'dog_collar.n.01', 'synonyms': ['dog_collar'], 'id': 379, 'def': 'a collar for a dog', 'name': 'dog_collar'}, {'frequency': 'f', 'synset': 'doll.n.01', 'synonyms': ['doll'], 'id': 380, 'def': 'a toy replica of a HUMAN (NOT AN ANIMAL)', 'name': 'doll'}, {'frequency': 'r', 'synset': 'dollar.n.02', 'synonyms': ['dollar', 'dollar_bill', 'one_dollar_bill'], 'id': 381, 'def': 'a piece of paper money worth one dollar', 'name': 'dollar'}, {'frequency': 'r', 'synset': 'dollhouse.n.01', 'synonyms': ['dollhouse', "doll's_house"], 'id': 382, 'def': "a house so small that it is likened to a child's plaything", 'name': 'dollhouse'}, {'frequency': 'c', 'synset': 'dolphin.n.02', 'synonyms': ['dolphin'], 'id': 383, 'def': 'any of various small toothed whales with a beaklike snout; larger than porpoises', 'name': 'dolphin'}, {'frequency': 'c', 'synset': 'domestic_ass.n.01', 'synonyms': ['domestic_ass', 'donkey'], 'id': 384, 'def': 'domestic beast of burden descended from the African wild ass; patient but stubborn', 'name': 'domestic_ass'}, {'frequency': 'f', 'synset': 'doorknob.n.01', 'synonyms': ['doorknob', 'doorhandle'], 'id': 385, 'def': "a knob used to open a door (often called `doorhandle' in Great Britain)", 'name': 'doorknob'}, {'frequency': 'c', 'synset': 'doormat.n.02', 'synonyms': ['doormat', 'welcome_mat'], 'id': 386, 'def': 'a mat placed outside an exterior door for wiping the shoes before entering', 'name': 'doormat'}, {'frequency': 'f', 'synset': 'doughnut.n.02', 'synonyms': ['doughnut', 'donut'], 'id': 387, 'def': 'a small ring-shaped friedcake', 'name': 'doughnut'}, {'frequency': 'r', 'synset': 'dove.n.01', 'synonyms': ['dove'], 'id': 388, 'def': 'any of numerous small pigeons', 'name': 'dove'}, {'frequency': 'r', 'synset': 'dragonfly.n.01', 'synonyms': ['dragonfly'], 'id': 389, 'def': 'slender-bodied non-stinging insect having iridescent wings that are outspread at rest', 'name': 'dragonfly'}, {'frequency': 'f', 'synset': 'drawer.n.01', 'synonyms': ['drawer'], 'id': 390, 'def': 'a boxlike container in a piece of furniture; made so as to slide in and out', 'name': 'drawer'}, {'frequency': 'c', 'synset': 'drawers.n.01', 'synonyms': ['underdrawers', 'boxers', 'boxershorts'], 'id': 391, 'def': 'underpants worn by men', 'name': 'underdrawers'}, {'frequency': 'f', 'synset': 'dress.n.01', 'synonyms': ['dress', 'frock'], 'id': 392, 'def': 'a one-piece garment for a woman; has skirt and bodice', 'name': 'dress'}, {'frequency': 'c', 'synset': 'dress_hat.n.01', 'synonyms': ['dress_hat', 'high_hat', 'opera_hat', 'silk_hat', 'top_hat'], 'id': 393, 'def': "a man's hat with a tall crown; usually covered with silk or with beaver fur", 'name': 'dress_hat'}, {'frequency': 'f', 'synset': 'dress_suit.n.01', 'synonyms': ['dress_suit'], 'id': 394, 'def': 'formalwear consisting of full evening dress for men', 'name': 'dress_suit'}, {'frequency': 'f', 'synset': 'dresser.n.05', 'synonyms': ['dresser'], 'id': 395, 'def': 'a cabinet with shelves', 'name': 'dresser'}, {'frequency': 'c', 'synset': 'drill.n.01', 'synonyms': ['drill'], 'id': 396, 'def': 'a tool with a sharp rotating point for making holes in hard materials', 'name': 'drill'}, {'frequency': 'r', 'synset': 'drone.n.04', 'synonyms': ['drone'], 'id': 397, 'def': 'an aircraft without a pilot that is operated by remote control', 'name': 'drone'}, {'frequency': 'r', 'synset': 'dropper.n.01', 'synonyms': ['dropper', 'eye_dropper'], 'id': 398, 'def': 'pipet consisting of a small tube with a vacuum bulb at one end for drawing liquid in and releasing it a drop at a time', 'name': 'dropper'}, {'frequency': 'c', 'synset': 'drum.n.01', 'synonyms': ['drum_(musical_instrument)'], 'id': 399, 'def': 'a musical percussion instrument; usually consists of a hollow cylinder with a membrane stretched across each end', 'name': 'drum_(musical_instrument)'}, {'frequency': 'r', 'synset': 'drumstick.n.02', 'synonyms': ['drumstick'], 'id': 400, 'def': 'a stick used for playing a drum', 'name': 'drumstick'}, {'frequency': 'f', 'synset': 'duck.n.01', 'synonyms': ['duck'], 'id': 401, 'def': 'small web-footed broad-billed swimming bird', 'name': 'duck'}, {'frequency': 'c', 'synset': 'duckling.n.02', 'synonyms': ['duckling'], 'id': 402, 'def': 'young duck', 'name': 'duckling'}, {'frequency': 'c', 'synset': 'duct_tape.n.01', 'synonyms': ['duct_tape'], 'id': 403, 'def': 'a wide silvery adhesive tape', 'name': 'duct_tape'}, {'frequency': 'f', 'synset': 'duffel_bag.n.01', 'synonyms': ['duffel_bag', 'duffle_bag', 'duffel', 'duffle'], 'id': 404, 'def': 'a large cylindrical bag of heavy cloth (does not include suitcases)', 'name': 'duffel_bag'}, {'frequency': 'r', 'synset': 'dumbbell.n.01', 'synonyms': ['dumbbell'], 'id': 405, 'def': 'an exercising weight with two ball-like ends connected by a short handle', 'name': 'dumbbell'}, {'frequency': 'c', 'synset': 'dumpster.n.01', 'synonyms': ['dumpster'], 'id': 406, 'def': 'a container designed to receive and transport and dump waste', 'name': 'dumpster'}, {'frequency': 'r', 'synset': 'dustpan.n.02', 'synonyms': ['dustpan'], 'id': 407, 'def': 'a short-handled receptacle into which dust can be swept', 'name': 'dustpan'}, {'frequency': 'c', 'synset': 'eagle.n.01', 'synonyms': ['eagle'], 'id': 408, 'def': 'large birds of prey noted for their broad wings and strong soaring flight', 'name': 'eagle'}, {'frequency': 'f', 'synset': 'earphone.n.01', 'synonyms': ['earphone', 'earpiece', 'headphone'], 'id': 409, 'def': 'device for listening to audio that is held over or inserted into the ear', 'name': 'earphone'}, {'frequency': 'r', 'synset': 'earplug.n.01', 'synonyms': ['earplug'], 'id': 410, 'def': 'a soft plug that is inserted into the ear canal to block sound', 'name': 'earplug'}, {'frequency': 'f', 'synset': 'earring.n.01', 'synonyms': ['earring'], 'id': 411, 'def': 'jewelry to ornament the ear', 'name': 'earring'}, {'frequency': 'c', 'synset': 'easel.n.01', 'synonyms': ['easel'], 'id': 412, 'def': "an upright tripod for displaying something (usually an artist's canvas)", 'name': 'easel'}, {'frequency': 'r', 'synset': 'eclair.n.01', 'synonyms': ['eclair'], 'id': 413, 'def': 'oblong cream puff', 'name': 'eclair'}, {'frequency': 'r', 'synset': 'eel.n.01', 'synonyms': ['eel'], 'id': 414, 'def': 'an elongate fish with fatty flesh', 'name': 'eel'}, {'frequency': 'f', 'synset': 'egg.n.02', 'synonyms': ['egg', 'eggs'], 'id': 415, 'def': 'oval reproductive body of a fowl (especially a hen) used as food', 'name': 'egg'}, {'frequency': 'r', 'synset': 'egg_roll.n.01', 'synonyms': ['egg_roll', 'spring_roll'], 'id': 416, 'def': 'minced vegetables and meat wrapped in a pancake and fried', 'name': 'egg_roll'}, {'frequency': 'c', 'synset': 'egg_yolk.n.01', 'synonyms': ['egg_yolk', 'yolk_(egg)'], 'id': 417, 'def': 'the yellow spherical part of an egg', 'name': 'egg_yolk'}, {'frequency': 'c', 'synset': 'eggbeater.n.02', 'synonyms': ['eggbeater', 'eggwhisk'], 'id': 418, 'def': 'a mixer for beating eggs or whipping cream', 'name': 'eggbeater'}, {'frequency': 'c', 'synset': 'eggplant.n.01', 'synonyms': ['eggplant', 'aubergine'], 'id': 419, 'def': 'egg-shaped vegetable having a shiny skin typically dark purple', 'name': 'eggplant'}, {'frequency': 'r', 'synset': 'electric_chair.n.01', 'synonyms': ['electric_chair'], 'id': 420, 'def': 'a chair-shaped instrument of execution by electrocution', 'name': 'electric_chair'}, {'frequency': 'f', 'synset': 'electric_refrigerator.n.01', 'synonyms': ['refrigerator'], 'id': 421, 'def': 'a refrigerator in which the coolant is pumped around by an electric motor', 'name': 'refrigerator'}, {'frequency': 'f', 'synset': 'elephant.n.01', 'synonyms': ['elephant'], 'id': 422, 'def': 'a common elephant', 'name': 'elephant'}, {'frequency': 'c', 'synset': 'elk.n.01', 'synonyms': ['elk', 'moose'], 'id': 423, 'def': 'large northern deer with enormous flattened antlers in the male', 'name': 'elk'}, {'frequency': 'c', 'synset': 'envelope.n.01', 'synonyms': ['envelope'], 'id': 424, 'def': 'a flat (usually rectangular) container for a letter, thin package, etc.', 'name': 'envelope'}, {'frequency': 'c', 'synset': 'eraser.n.01', 'synonyms': ['eraser'], 'id': 425, 'def': 'an implement used to erase something', 'name': 'eraser'}, {'frequency': 'r', 'synset': 'escargot.n.01', 'synonyms': ['escargot'], 'id': 426, 'def': 'edible snail usually served in the shell with a sauce of melted butter and garlic', 'name': 'escargot'}, {'frequency': 'r', 'synset': 'eyepatch.n.01', 'synonyms': ['eyepatch'], 'id': 427, 'def': 'a protective cloth covering for an injured eye', 'name': 'eyepatch'}, {'frequency': 'r', 'synset': 'falcon.n.01', 'synonyms': ['falcon'], 'id': 428, 'def': 'birds of prey having long pointed powerful wings adapted for swift flight', 'name': 'falcon'}, {'frequency': 'f', 'synset': 'fan.n.01', 'synonyms': ['fan'], 'id': 429, 'def': 'a device for creating a current of air by movement of a surface or surfaces', 'name': 'fan'}, {'frequency': 'f', 'synset': 'faucet.n.01', 'synonyms': ['faucet', 'spigot', 'tap'], 'id': 430, 'def': 'a regulator for controlling the flow of a liquid from a reservoir', 'name': 'faucet'}, {'frequency': 'r', 'synset': 'fedora.n.01', 'synonyms': ['fedora'], 'id': 431, 'def': 'a hat made of felt with a creased crown', 'name': 'fedora'}, {'frequency': 'r', 'synset': 'ferret.n.02', 'synonyms': ['ferret'], 'id': 432, 'def': 'domesticated albino variety of the European polecat bred for hunting rats and rabbits', 'name': 'ferret'}, {'frequency': 'c', 'synset': 'ferris_wheel.n.01', 'synonyms': ['Ferris_wheel'], 'id': 433, 'def': 'a large wheel with suspended seats that remain upright as the wheel rotates', 'name': 'Ferris_wheel'}, {'frequency': 'c', 'synset': 'ferry.n.01', 'synonyms': ['ferry', 'ferryboat'], 'id': 434, 'def': 'a boat that transports people or vehicles across a body of water and operates on a regular schedule', 'name': 'ferry'}, {'frequency': 'r', 'synset': 'fig.n.04', 'synonyms': ['fig_(fruit)'], 'id': 435, 'def': 'fleshy sweet pear-shaped yellowish or purple fruit eaten fresh or preserved or dried', 'name': 'fig_(fruit)'}, {'frequency': 'c', 'synset': 'fighter.n.02', 'synonyms': ['fighter_jet', 'fighter_aircraft', 'attack_aircraft'], 'id': 436, 'def': 'a high-speed military or naval airplane designed to destroy enemy targets', 'name': 'fighter_jet'}, {'frequency': 'f', 'synset': 'figurine.n.01', 'synonyms': ['figurine'], 'id': 437, 'def': 'a small carved or molded figure', 'name': 'figurine'}, {'frequency': 'c', 'synset': 'file.n.03', 'synonyms': ['file_cabinet', 'filing_cabinet'], 'id': 438, 'def': 'office furniture consisting of a container for keeping papers in order', 'name': 'file_cabinet'}, {'frequency': 'r', 'synset': 'file.n.04', 'synonyms': ['file_(tool)'], 'id': 439, 'def': 'a steel hand tool with small sharp teeth on some or all of its surfaces; used for smoothing wood or metal', 'name': 'file_(tool)'}, {'frequency': 'f', 'synset': 'fire_alarm.n.02', 'synonyms': ['fire_alarm', 'smoke_alarm'], 'id': 440, 'def': 'an alarm that is tripped off by fire or smoke', 'name': 'fire_alarm'}, {'frequency': 'f', 'synset': 'fire_engine.n.01', 'synonyms': ['fire_engine', 'fire_truck'], 'id': 441, 'def': 'large trucks that carry firefighters and equipment to the site of a fire', 'name': 'fire_engine'}, {'frequency': 'f', 'synset': 'fire_extinguisher.n.01', 'synonyms': ['fire_extinguisher', 'extinguisher'], 'id': 442, 'def': 'a manually operated device for extinguishing small fires', 'name': 'fire_extinguisher'}, {'frequency': 'c', 'synset': 'fire_hose.n.01', 'synonyms': ['fire_hose'], 'id': 443, 'def': 'a large hose that carries water from a fire hydrant to the site of the fire', 'name': 'fire_hose'}, {'frequency': 'f', 'synset': 'fireplace.n.01', 'synonyms': ['fireplace'], 'id': 444, 'def': 'an open recess in a wall at the base of a chimney where a fire can be built', 'name': 'fireplace'}, {'frequency': 'f', 'synset': 'fireplug.n.01', 'synonyms': ['fireplug', 'fire_hydrant', 'hydrant'], 'id': 445, 'def': 'an upright hydrant for drawing water to use in fighting a fire', 'name': 'fireplug'}, {'frequency': 'r', 'synset': 'first-aid_kit.n.01', 'synonyms': ['first-aid_kit'], 'id': 446, 'def': 'kit consisting of a set of bandages and medicines for giving first aid', 'name': 'first-aid_kit'}, {'frequency': 'f', 'synset': 'fish.n.01', 'synonyms': ['fish'], 'id': 447, 'def': 'any of various mostly cold-blooded aquatic vertebrates usually having scales and breathing through gills', 'name': 'fish'}, {'frequency': 'c', 'synset': 'fish.n.02', 'synonyms': ['fish_(food)'], 'id': 448, 'def': 'the flesh of fish used as food', 'name': 'fish_(food)'}, {'frequency': 'r', 'synset': 'fishbowl.n.02', 'synonyms': ['fishbowl', 'goldfish_bowl'], 'id': 449, 'def': 'a transparent bowl in which small fish are kept', 'name': 'fishbowl'}, {'frequency': 'c', 'synset': 'fishing_rod.n.01', 'synonyms': ['fishing_rod', 'fishing_pole'], 'id': 450, 'def': 'a rod that is used in fishing to extend the fishing line', 'name': 'fishing_rod'}, {'frequency': 'f', 'synset': 'flag.n.01', 'synonyms': ['flag'], 'id': 451, 'def': 'emblem usually consisting of a rectangular piece of cloth of distinctive design (do not include pole)', 'name': 'flag'}, {'frequency': 'f', 'synset': 'flagpole.n.02', 'synonyms': ['flagpole', 'flagstaff'], 'id': 452, 'def': 'a tall staff or pole on which a flag is raised', 'name': 'flagpole'}, {'frequency': 'c', 'synset': 'flamingo.n.01', 'synonyms': ['flamingo'], 'id': 453, 'def': 'large pink web-footed bird with down-bent bill', 'name': 'flamingo'}, {'frequency': 'c', 'synset': 'flannel.n.01', 'synonyms': ['flannel'], 'id': 454, 'def': 'a soft light woolen fabric; used for clothing', 'name': 'flannel'}, {'frequency': 'c', 'synset': 'flap.n.01', 'synonyms': ['flap'], 'id': 455, 'def': 'any broad thin covering attached at one edge, such as a mud flap next to a wheel or a flap on an airplane wing', 'name': 'flap'}, {'frequency': 'r', 'synset': 'flash.n.10', 'synonyms': ['flash', 'flashbulb'], 'id': 456, 'def': 'a lamp for providing momentary light to take a photograph', 'name': 'flash'}, {'frequency': 'c', 'synset': 'flashlight.n.01', 'synonyms': ['flashlight', 'torch'], 'id': 457, 'def': 'a small portable battery-powered electric lamp', 'name': 'flashlight'}, {'frequency': 'r', 'synset': 'fleece.n.03', 'synonyms': ['fleece'], 'id': 458, 'def': 'a soft bulky fabric with deep pile; used chiefly for clothing', 'name': 'fleece'}, {'frequency': 'f', 'synset': 'flip-flop.n.02', 'synonyms': ['flip-flop_(sandal)'], 'id': 459, 'def': 'a backless sandal held to the foot by a thong between two toes', 'name': 'flip-flop_(sandal)'}, {'frequency': 'c', 'synset': 'flipper.n.01', 'synonyms': ['flipper_(footwear)', 'fin_(footwear)'], 'id': 460, 'def': 'a shoe to aid a person in swimming', 'name': 'flipper_(footwear)'}, {'frequency': 'f', 'synset': 'flower_arrangement.n.01', 'synonyms': ['flower_arrangement', 'floral_arrangement'], 'id': 461, 'def': 'a decorative arrangement of flowers', 'name': 'flower_arrangement'}, {'frequency': 'c', 'synset': 'flute.n.02', 'synonyms': ['flute_glass', 'champagne_flute'], 'id': 462, 'def': 'a tall narrow wineglass', 'name': 'flute_glass'}, {'frequency': 'c', 'synset': 'foal.n.01', 'synonyms': ['foal'], 'id': 463, 'def': 'a young horse', 'name': 'foal'}, {'frequency': 'c', 'synset': 'folding_chair.n.01', 'synonyms': ['folding_chair'], 'id': 464, 'def': 'a chair that can be folded flat for storage', 'name': 'folding_chair'}, {'frequency': 'c', 'synset': 'food_processor.n.01', 'synonyms': ['food_processor'], 'id': 465, 'def': 'a kitchen appliance for shredding, blending, chopping, or slicing food', 'name': 'food_processor'}, {'frequency': 'c', 'synset': 'football.n.02', 'synonyms': ['football_(American)'], 'id': 466, 'def': 'the inflated oblong ball used in playing American football', 'name': 'football_(American)'}, {'frequency': 'r', 'synset': 'football_helmet.n.01', 'synonyms': ['football_helmet'], 'id': 467, 'def': 'a padded helmet with a face mask to protect the head of football players', 'name': 'football_helmet'}, {'frequency': 'c', 'synset': 'footstool.n.01', 'synonyms': ['footstool', 'footrest'], 'id': 468, 'def': 'a low seat or a stool to rest the feet of a seated person', 'name': 'footstool'}, {'frequency': 'f', 'synset': 'fork.n.01', 'synonyms': ['fork'], 'id': 469, 'def': 'cutlery used for serving and eating food', 'name': 'fork'}, {'frequency': 'c', 'synset': 'forklift.n.01', 'synonyms': ['forklift'], 'id': 470, 'def': 'an industrial vehicle with a power operated fork in front that can be inserted under loads to lift and move them', 'name': 'forklift'}, {'frequency': 'c', 'synset': 'freight_car.n.01', 'synonyms': ['freight_car'], 'id': 471, 'def': 'a railway car that carries freight', 'name': 'freight_car'}, {'frequency': 'c', 'synset': 'french_toast.n.01', 'synonyms': ['French_toast'], 'id': 472, 'def': 'bread slice dipped in egg and milk and fried', 'name': 'French_toast'}, {'frequency': 'c', 'synset': 'freshener.n.01', 'synonyms': ['freshener', 'air_freshener'], 'id': 473, 'def': 'anything that freshens air by removing or covering odor', 'name': 'freshener'}, {'frequency': 'f', 'synset': 'frisbee.n.01', 'synonyms': ['frisbee'], 'id': 474, 'def': 'a light, plastic disk propelled with a flip of the wrist for recreation or competition', 'name': 'frisbee'}, {'frequency': 'c', 'synset': 'frog.n.01', 'synonyms': ['frog', 'toad', 'toad_frog'], 'id': 475, 'def': 'a tailless stout-bodied amphibians with long hind limbs for leaping', 'name': 'frog'}, {'frequency': 'c', 'synset': 'fruit_juice.n.01', 'synonyms': ['fruit_juice'], 'id': 476, 'def': 'drink produced by squeezing or crushing fruit', 'name': 'fruit_juice'}, {'frequency': 'f', 'synset': 'frying_pan.n.01', 'synonyms': ['frying_pan', 'frypan', 'skillet'], 'id': 477, 'def': 'a pan used for frying foods', 'name': 'frying_pan'}, {'frequency': 'r', 'synset': 'fudge.n.01', 'synonyms': ['fudge'], 'id': 478, 'def': 'soft creamy candy', 'name': 'fudge'}, {'frequency': 'r', 'synset': 'funnel.n.02', 'synonyms': ['funnel'], 'id': 479, 'def': 'a cone-shaped utensil used to channel a substance into a container with a small mouth', 'name': 'funnel'}, {'frequency': 'r', 'synset': 'futon.n.01', 'synonyms': ['futon'], 'id': 480, 'def': 'a pad that is used for sleeping on the floor or on a raised frame', 'name': 'futon'}, {'frequency': 'r', 'synset': 'gag.n.02', 'synonyms': ['gag', 'muzzle'], 'id': 481, 'def': "restraint put into a person's mouth to prevent speaking or shouting", 'name': 'gag'}, {'frequency': 'r', 'synset': 'garbage.n.03', 'synonyms': ['garbage'], 'id': 482, 'def': 'a receptacle where waste can be discarded', 'name': 'garbage'}, {'frequency': 'c', 'synset': 'garbage_truck.n.01', 'synonyms': ['garbage_truck'], 'id': 483, 'def': 'a truck for collecting domestic refuse', 'name': 'garbage_truck'}, {'frequency': 'c', 'synset': 'garden_hose.n.01', 'synonyms': ['garden_hose'], 'id': 484, 'def': 'a hose used for watering a lawn or garden', 'name': 'garden_hose'}, {'frequency': 'c', 'synset': 'gargle.n.01', 'synonyms': ['gargle', 'mouthwash'], 'id': 485, 'def': 'a medicated solution used for gargling and rinsing the mouth', 'name': 'gargle'}, {'frequency': 'r', 'synset': 'gargoyle.n.02', 'synonyms': ['gargoyle'], 'id': 486, 'def': 'an ornament consisting of a grotesquely carved figure of a person or animal', 'name': 'gargoyle'}, {'frequency': 'c', 'synset': 'garlic.n.02', 'synonyms': ['garlic', 'ail'], 'id': 487, 'def': 'aromatic bulb used as seasoning', 'name': 'garlic'}, {'frequency': 'r', 'synset': 'gasmask.n.01', 'synonyms': ['gasmask', 'respirator', 'gas_helmet'], 'id': 488, 'def': 'a protective face mask with a filter', 'name': 'gasmask'}, {'frequency': 'c', 'synset': 'gazelle.n.01', 'synonyms': ['gazelle'], 'id': 489, 'def': 'small swift graceful antelope of Africa and Asia having lustrous eyes', 'name': 'gazelle'}, {'frequency': 'c', 'synset': 'gelatin.n.02', 'synonyms': ['gelatin', 'jelly'], 'id': 490, 'def': 'an edible jelly made with gelatin and used as a dessert or salad base or a coating for foods', 'name': 'gelatin'}, {'frequency': 'r', 'synset': 'gem.n.02', 'synonyms': ['gemstone'], 'id': 491, 'def': 'a crystalline rock that can be cut and polished for jewelry', 'name': 'gemstone'}, {'frequency': 'r', 'synset': 'generator.n.02', 'synonyms': ['generator'], 'id': 492, 'def': 'engine that converts mechanical energy into electrical energy by electromagnetic induction', 'name': 'generator'}, {'frequency': 'c', 'synset': 'giant_panda.n.01', 'synonyms': ['giant_panda', 'panda', 'panda_bear'], 'id': 493, 'def': 'large black-and-white herbivorous mammal of bamboo forests of China and Tibet', 'name': 'giant_panda'}, {'frequency': 'c', 'synset': 'gift_wrap.n.01', 'synonyms': ['gift_wrap'], 'id': 494, 'def': 'attractive wrapping paper suitable for wrapping gifts', 'name': 'gift_wrap'}, {'frequency': 'c', 'synset': 'ginger.n.03', 'synonyms': ['ginger', 'gingerroot'], 'id': 495, 'def': 'the root of the common ginger plant; used fresh as a seasoning', 'name': 'ginger'}, {'frequency': 'f', 'synset': 'giraffe.n.01', 'synonyms': ['giraffe'], 'id': 496, 'def': 'tall animal having a spotted coat and small horns and very long neck and legs', 'name': 'giraffe'}, {'frequency': 'c', 'synset': 'girdle.n.02', 'synonyms': ['cincture', 'sash', 'waistband', 'waistcloth'], 'id': 497, 'def': 'a band of material around the waist that strengthens a skirt or trousers', 'name': 'cincture'}, {'frequency': 'f', 'synset': 'glass.n.02', 'synonyms': ['glass_(drink_container)', 'drinking_glass'], 'id': 498, 'def': 'a container for holding liquids while drinking', 'name': 'glass_(drink_container)'}, {'frequency': 'c', 'synset': 'globe.n.03', 'synonyms': ['globe'], 'id': 499, 'def': 'a sphere on which a map (especially of the earth) is represented', 'name': 'globe'}, {'frequency': 'f', 'synset': 'glove.n.02', 'synonyms': ['glove'], 'id': 500, 'def': 'handwear covering the hand', 'name': 'glove'}, {'frequency': 'c', 'synset': 'goat.n.01', 'synonyms': ['goat'], 'id': 501, 'def': 'a common goat', 'name': 'goat'}, {'frequency': 'f', 'synset': 'goggles.n.01', 'synonyms': ['goggles'], 'id': 502, 'def': 'tight-fitting spectacles worn to protect the eyes', 'name': 'goggles'}, {'frequency': 'r', 'synset': 'goldfish.n.01', 'synonyms': ['goldfish'], 'id': 503, 'def': 'small golden or orange-red freshwater fishes used as pond or aquarium pets', 'name': 'goldfish'}, {'frequency': 'c', 'synset': 'golf_club.n.02', 'synonyms': ['golf_club', 'golf-club'], 'id': 504, 'def': 'golf equipment used by a golfer to hit a golf ball', 'name': 'golf_club'}, {'frequency': 'c', 'synset': 'golfcart.n.01', 'synonyms': ['golfcart'], 'id': 505, 'def': 'a small motor vehicle in which golfers can ride between shots', 'name': 'golfcart'}, {'frequency': 'r', 'synset': 'gondola.n.02', 'synonyms': ['gondola_(boat)'], 'id': 506, 'def': 'long narrow flat-bottomed boat propelled by sculling; traditionally used on canals of Venice', 'name': 'gondola_(boat)'}, {'frequency': 'c', 'synset': 'goose.n.01', 'synonyms': ['goose'], 'id': 507, 'def': 'loud, web-footed long-necked aquatic birds usually larger than ducks', 'name': 'goose'}, {'frequency': 'r', 'synset': 'gorilla.n.01', 'synonyms': ['gorilla'], 'id': 508, 'def': 'largest ape', 'name': 'gorilla'}, {'frequency': 'r', 'synset': 'gourd.n.02', 'synonyms': ['gourd'], 'id': 509, 'def': 'any of numerous inedible fruits with hard rinds', 'name': 'gourd'}, {'frequency': 'f', 'synset': 'grape.n.01', 'synonyms': ['grape'], 'id': 510, 'def': 'any of various juicy fruit with green or purple skins; grow in clusters', 'name': 'grape'}, {'frequency': 'c', 'synset': 'grater.n.01', 'synonyms': ['grater'], 'id': 511, 'def': 'utensil with sharp perforations for shredding foods (as vegetables or cheese)', 'name': 'grater'}, {'frequency': 'c', 'synset': 'gravestone.n.01', 'synonyms': ['gravestone', 'headstone', 'tombstone'], 'id': 512, 'def': 'a stone that is used to mark a grave', 'name': 'gravestone'}, {'frequency': 'r', 'synset': 'gravy_boat.n.01', 'synonyms': ['gravy_boat', 'gravy_holder'], 'id': 513, 'def': 'a dish (often boat-shaped) for serving gravy or sauce', 'name': 'gravy_boat'}, {'frequency': 'f', 'synset': 'green_bean.n.02', 'synonyms': ['green_bean'], 'id': 514, 'def': 'a common bean plant cultivated for its slender green edible pods', 'name': 'green_bean'}, {'frequency': 'f', 'synset': 'green_onion.n.01', 'synonyms': ['green_onion', 'spring_onion', 'scallion'], 'id': 515, 'def': 'a young onion before the bulb has enlarged', 'name': 'green_onion'}, {'frequency': 'r', 'synset': 'griddle.n.01', 'synonyms': ['griddle'], 'id': 516, 'def': 'cooking utensil consisting of a flat heated surface on which food is cooked', 'name': 'griddle'}, {'frequency': 'f', 'synset': 'grill.n.02', 'synonyms': ['grill', 'grille', 'grillwork', 'radiator_grille'], 'id': 517, 'def': 'a framework of metal bars used as a partition or a grate', 'name': 'grill'}, {'frequency': 'r', 'synset': 'grits.n.01', 'synonyms': ['grits', 'hominy_grits'], 'id': 518, 'def': 'coarsely ground corn boiled as a breakfast dish', 'name': 'grits'}, {'frequency': 'c', 'synset': 'grizzly.n.01', 'synonyms': ['grizzly', 'grizzly_bear'], 'id': 519, 'def': 'powerful brownish-yellow bear of the uplands of western North America', 'name': 'grizzly'}, {'frequency': 'c', 'synset': 'grocery_bag.n.01', 'synonyms': ['grocery_bag'], 'id': 520, 'def': "a sack for holding customer's groceries", 'name': 'grocery_bag'}, {'frequency': 'f', 'synset': 'guitar.n.01', 'synonyms': ['guitar'], 'id': 521, 'def': 'a stringed instrument usually having six strings; played by strumming or plucking', 'name': 'guitar'}, {'frequency': 'c', 'synset': 'gull.n.02', 'synonyms': ['gull', 'seagull'], 'id': 522, 'def': 'mostly white aquatic bird having long pointed wings and short legs', 'name': 'gull'}, {'frequency': 'c', 'synset': 'gun.n.01', 'synonyms': ['gun'], 'id': 523, 'def': 'a weapon that discharges a bullet at high velocity from a metal tube', 'name': 'gun'}, {'frequency': 'f', 'synset': 'hairbrush.n.01', 'synonyms': ['hairbrush'], 'id': 524, 'def': "a brush used to groom a person's hair", 'name': 'hairbrush'}, {'frequency': 'c', 'synset': 'hairnet.n.01', 'synonyms': ['hairnet'], 'id': 525, 'def': 'a small net that someone wears over their hair to keep it in place', 'name': 'hairnet'}, {'frequency': 'c', 'synset': 'hairpin.n.01', 'synonyms': ['hairpin'], 'id': 526, 'def': "a double pronged pin used to hold women's hair in place", 'name': 'hairpin'}, {'frequency': 'r', 'synset': 'halter.n.03', 'synonyms': ['halter_top'], 'id': 527, 'def': "a woman's top that fastens behind the back and neck leaving the back and arms uncovered", 'name': 'halter_top'}, {'frequency': 'f', 'synset': 'ham.n.01', 'synonyms': ['ham', 'jambon', 'gammon'], 'id': 528, 'def': 'meat cut from the thigh of a hog (usually smoked)', 'name': 'ham'}, {'frequency': 'c', 'synset': 'hamburger.n.01', 'synonyms': ['hamburger', 'beefburger', 'burger'], 'id': 529, 'def': 'a sandwich consisting of a patty of minced beef served on a bun', 'name': 'hamburger'}, {'frequency': 'c', 'synset': 'hammer.n.02', 'synonyms': ['hammer'], 'id': 530, 'def': 'a hand tool with a heavy head and a handle; used to deliver an impulsive force by striking', 'name': 'hammer'}, {'frequency': 'c', 'synset': 'hammock.n.02', 'synonyms': ['hammock'], 'id': 531, 'def': 'a hanging bed of canvas or rope netting (usually suspended between two trees)', 'name': 'hammock'}, {'frequency': 'r', 'synset': 'hamper.n.02', 'synonyms': ['hamper'], 'id': 532, 'def': 'a basket usually with a cover', 'name': 'hamper'}, {'frequency': 'c', 'synset': 'hamster.n.01', 'synonyms': ['hamster'], 'id': 533, 'def': 'short-tailed burrowing rodent with large cheek pouches', 'name': 'hamster'}, {'frequency': 'f', 'synset': 'hand_blower.n.01', 'synonyms': ['hair_dryer'], 'id': 534, 'def': 'a hand-held electric blower that can blow warm air onto the hair', 'name': 'hair_dryer'}, {'frequency': 'r', 'synset': 'hand_glass.n.01', 'synonyms': ['hand_glass', 'hand_mirror'], 'id': 535, 'def': 'a mirror intended to be held in the hand', 'name': 'hand_glass'}, {'frequency': 'f', 'synset': 'hand_towel.n.01', 'synonyms': ['hand_towel', 'face_towel'], 'id': 536, 'def': 'a small towel used to dry the hands or face', 'name': 'hand_towel'}, {'frequency': 'c', 'synset': 'handcart.n.01', 'synonyms': ['handcart', 'pushcart', 'hand_truck'], 'id': 537, 'def': 'wheeled vehicle that can be pushed by a person', 'name': 'handcart'}, {'frequency': 'r', 'synset': 'handcuff.n.01', 'synonyms': ['handcuff'], 'id': 538, 'def': 'shackle that consists of a metal loop that can be locked around the wrist', 'name': 'handcuff'}, {'frequency': 'c', 'synset': 'handkerchief.n.01', 'synonyms': ['handkerchief'], 'id': 539, 'def': 'a square piece of cloth used for wiping the eyes or nose or as a costume accessory', 'name': 'handkerchief'}, {'frequency': 'f', 'synset': 'handle.n.01', 'synonyms': ['handle', 'grip', 'handgrip'], 'id': 540, 'def': 'the appendage to an object that is designed to be held in order to use or move it', 'name': 'handle'}, {'frequency': 'r', 'synset': 'handsaw.n.01', 'synonyms': ['handsaw', "carpenter's_saw"], 'id': 541, 'def': 'a saw used with one hand for cutting wood', 'name': 'handsaw'}, {'frequency': 'r', 'synset': 'hardback.n.01', 'synonyms': ['hardback_book', 'hardcover_book'], 'id': 542, 'def': 'a book with cardboard or cloth or leather covers', 'name': 'hardback_book'}, {'frequency': 'r', 'synset': 'harmonium.n.01', 'synonyms': ['harmonium', 'organ_(musical_instrument)', 'reed_organ_(musical_instrument)'], 'id': 543, 'def': 'a free-reed instrument in which air is forced through the reeds by bellows', 'name': 'harmonium'}, {'frequency': 'f', 'synset': 'hat.n.01', 'synonyms': ['hat'], 'id': 544, 'def': 'headwear that protects the head from bad weather, sun, or worn for fashion', 'name': 'hat'}, {'frequency': 'r', 'synset': 'hatbox.n.01', 'synonyms': ['hatbox'], 'id': 545, 'def': 'a round piece of luggage for carrying hats', 'name': 'hatbox'}, {'frequency': 'c', 'synset': 'head_covering.n.01', 'synonyms': ['veil'], 'id': 546, 'def': 'a garment that covers the head OR face', 'name': 'veil'}, {'frequency': 'f', 'synset': 'headband.n.01', 'synonyms': ['headband'], 'id': 547, 'def': 'a band worn around or over the head', 'name': 'headband'}, {'frequency': 'f', 'synset': 'headboard.n.01', 'synonyms': ['headboard'], 'id': 548, 'def': 'a vertical board or panel forming the head of a bedstead', 'name': 'headboard'}, {'frequency': 'f', 'synset': 'headlight.n.01', 'synonyms': ['headlight', 'headlamp'], 'id': 549, 'def': 'a powerful light with reflector; attached to the front of an automobile or locomotive', 'name': 'headlight'}, {'frequency': 'c', 'synset': 'headscarf.n.01', 'synonyms': ['headscarf'], 'id': 550, 'def': 'a kerchief worn over the head and tied under the chin', 'name': 'headscarf'}, {'frequency': 'r', 'synset': 'headset.n.01', 'synonyms': ['headset'], 'id': 551, 'def': 'receiver consisting of a pair of headphones', 'name': 'headset'}, {'frequency': 'c', 'synset': 'headstall.n.01', 'synonyms': ['headstall_(for_horses)', 'headpiece_(for_horses)'], 'id': 552, 'def': "the band that is the part of a bridle that fits around a horse's head", 'name': 'headstall_(for_horses)'}, {'frequency': 'c', 'synset': 'heart.n.02', 'synonyms': ['heart'], 'id': 553, 'def': 'a muscular organ; its contractions move the blood through the body', 'name': 'heart'}, {'frequency': 'c', 'synset': 'heater.n.01', 'synonyms': ['heater', 'warmer'], 'id': 554, 'def': 'device that heats water or supplies warmth to a room', 'name': 'heater'}, {'frequency': 'c', 'synset': 'helicopter.n.01', 'synonyms': ['helicopter'], 'id': 555, 'def': 'an aircraft without wings that obtains its lift from the rotation of overhead blades', 'name': 'helicopter'}, {'frequency': 'f', 'synset': 'helmet.n.02', 'synonyms': ['helmet'], 'id': 556, 'def': 'a protective headgear made of hard material to resist blows', 'name': 'helmet'}, {'frequency': 'r', 'synset': 'heron.n.02', 'synonyms': ['heron'], 'id': 557, 'def': 'grey or white wading bird with long neck and long legs and (usually) long bill', 'name': 'heron'}, {'frequency': 'c', 'synset': 'highchair.n.01', 'synonyms': ['highchair', 'feeding_chair'], 'id': 558, 'def': 'a chair for feeding a very young child', 'name': 'highchair'}, {'frequency': 'f', 'synset': 'hinge.n.01', 'synonyms': ['hinge'], 'id': 559, 'def': 'a joint that holds two parts together so that one can swing relative to the other', 'name': 'hinge'}, {'frequency': 'r', 'synset': 'hippopotamus.n.01', 'synonyms': ['hippopotamus'], 'id': 560, 'def': 'massive thick-skinned animal living in or around rivers of tropical Africa', 'name': 'hippopotamus'}, {'frequency': 'r', 'synset': 'hockey_stick.n.01', 'synonyms': ['hockey_stick'], 'id': 561, 'def': 'sports implement consisting of a stick used by hockey players to move the puck', 'name': 'hockey_stick'}, {'frequency': 'c', 'synset': 'hog.n.03', 'synonyms': ['hog', 'pig'], 'id': 562, 'def': 'domestic swine', 'name': 'hog'}, {'frequency': 'f', 'synset': 'home_plate.n.01', 'synonyms': ['home_plate_(baseball)', 'home_base_(baseball)'], 'id': 563, 'def': '(baseball) a rubber slab where the batter stands; it must be touched by a base runner in order to score', 'name': 'home_plate_(baseball)'}, {'frequency': 'c', 'synset': 'honey.n.01', 'synonyms': ['honey'], 'id': 564, 'def': 'a sweet yellow liquid produced by bees', 'name': 'honey'}, {'frequency': 'f', 'synset': 'hood.n.06', 'synonyms': ['fume_hood', 'exhaust_hood'], 'id': 565, 'def': 'metal covering leading to a vent that exhausts smoke or fumes', 'name': 'fume_hood'}, {'frequency': 'f', 'synset': 'hook.n.05', 'synonyms': ['hook'], 'id': 566, 'def': 'a curved or bent implement for suspending or pulling something', 'name': 'hook'}, {'frequency': 'r', 'synset': 'hookah.n.01', 'synonyms': ['hookah', 'narghile', 'nargileh', 'sheesha', 'shisha', 'water_pipe'], 'id': 567, 'def': 'a tobacco pipe with a long flexible tube connected to a container where the smoke is cooled by passing through water', 'name': 'hookah'}, {'frequency': 'r', 'synset': 'hornet.n.01', 'synonyms': ['hornet'], 'id': 568, 'def': 'large stinging wasp', 'name': 'hornet'}, {'frequency': 'f', 'synset': 'horse.n.01', 'synonyms': ['horse'], 'id': 569, 'def': 'a common horse', 'name': 'horse'}, {'frequency': 'f', 'synset': 'hose.n.03', 'synonyms': ['hose', 'hosepipe'], 'id': 570, 'def': 'a flexible pipe for conveying a liquid or gas', 'name': 'hose'}, {'frequency': 'r', 'synset': 'hot-air_balloon.n.01', 'synonyms': ['hot-air_balloon'], 'id': 571, 'def': 'balloon for travel through the air in a basket suspended below a large bag of heated air', 'name': 'hot-air_balloon'}, {'frequency': 'r', 'synset': 'hot_plate.n.01', 'synonyms': ['hotplate'], 'id': 572, 'def': 'a portable electric appliance for heating or cooking or keeping food warm', 'name': 'hotplate'}, {'frequency': 'c', 'synset': 'hot_sauce.n.01', 'synonyms': ['hot_sauce'], 'id': 573, 'def': 'a pungent peppery sauce', 'name': 'hot_sauce'}, {'frequency': 'r', 'synset': 'hourglass.n.01', 'synonyms': ['hourglass'], 'id': 574, 'def': 'a sandglass timer that runs for sixty minutes', 'name': 'hourglass'}, {'frequency': 'r', 'synset': 'houseboat.n.01', 'synonyms': ['houseboat'], 'id': 575, 'def': 'a barge that is designed and equipped for use as a dwelling', 'name': 'houseboat'}, {'frequency': 'c', 'synset': 'hummingbird.n.01', 'synonyms': ['hummingbird'], 'id': 576, 'def': 'tiny American bird having brilliant iridescent plumage and long slender bills', 'name': 'hummingbird'}, {'frequency': 'r', 'synset': 'hummus.n.01', 'synonyms': ['hummus', 'humus', 'hommos', 'hoummos', 'humous'], 'id': 577, 'def': 'a thick spread made from mashed chickpeas', 'name': 'hummus'}, {'frequency': 'f', 'synset': 'ice_bear.n.01', 'synonyms': ['polar_bear'], 'id': 578, 'def': 'white bear of Arctic regions', 'name': 'polar_bear'}, {'frequency': 'c', 'synset': 'ice_cream.n.01', 'synonyms': ['icecream'], 'id': 579, 'def': 'frozen dessert containing cream and sugar and flavoring', 'name': 'icecream'}, {'frequency': 'r', 'synset': 'ice_lolly.n.01', 'synonyms': ['popsicle'], 'id': 580, 'def': 'ice cream or water ice on a small wooden stick', 'name': 'popsicle'}, {'frequency': 'c', 'synset': 'ice_maker.n.01', 'synonyms': ['ice_maker'], 'id': 581, 'def': 'an appliance included in some electric refrigerators for making ice cubes', 'name': 'ice_maker'}, {'frequency': 'r', 'synset': 'ice_pack.n.01', 'synonyms': ['ice_pack', 'ice_bag'], 'id': 582, 'def': 'a waterproof bag filled with ice: applied to the body (especially the head) to cool or reduce swelling', 'name': 'ice_pack'}, {'frequency': 'r', 'synset': 'ice_skate.n.01', 'synonyms': ['ice_skate'], 'id': 583, 'def': 'skate consisting of a boot with a steel blade fitted to the sole', 'name': 'ice_skate'}, {'frequency': 'c', 'synset': 'igniter.n.01', 'synonyms': ['igniter', 'ignitor', 'lighter'], 'id': 584, 'def': 'a substance or device used to start a fire', 'name': 'igniter'}, {'frequency': 'r', 'synset': 'inhaler.n.01', 'synonyms': ['inhaler', 'inhalator'], 'id': 585, 'def': 'a dispenser that produces a chemical vapor to be inhaled through mouth or nose', 'name': 'inhaler'}, {'frequency': 'f', 'synset': 'ipod.n.01', 'synonyms': ['iPod'], 'id': 586, 'def': 'a pocket-sized device used to play music files', 'name': 'iPod'}, {'frequency': 'c', 'synset': 'iron.n.04', 'synonyms': ['iron_(for_clothing)', 'smoothing_iron_(for_clothing)'], 'id': 587, 'def': 'home appliance consisting of a flat metal base that is heated and used to smooth cloth', 'name': 'iron_(for_clothing)'}, {'frequency': 'c', 'synset': 'ironing_board.n.01', 'synonyms': ['ironing_board'], 'id': 588, 'def': 'narrow padded board on collapsible supports; used for ironing clothes', 'name': 'ironing_board'}, {'frequency': 'f', 'synset': 'jacket.n.01', 'synonyms': ['jacket'], 'id': 589, 'def': 'a waist-length coat', 'name': 'jacket'}, {'frequency': 'c', 'synset': 'jam.n.01', 'synonyms': ['jam'], 'id': 590, 'def': 'preserve of crushed fruit', 'name': 'jam'}, {'frequency': 'f', 'synset': 'jar.n.01', 'synonyms': ['jar'], 'id': 591, 'def': 'a vessel (usually cylindrical) with a wide mouth and without handles', 'name': 'jar'}, {'frequency': 'f', 'synset': 'jean.n.01', 'synonyms': ['jean', 'blue_jean', 'denim'], 'id': 592, 'def': '(usually plural) close-fitting trousers of heavy denim for manual work or casual wear', 'name': 'jean'}, {'frequency': 'c', 'synset': 'jeep.n.01', 'synonyms': ['jeep', 'landrover'], 'id': 593, 'def': 'a car suitable for traveling over rough terrain', 'name': 'jeep'}, {'frequency': 'r', 'synset': 'jelly_bean.n.01', 'synonyms': ['jelly_bean', 'jelly_egg'], 'id': 594, 'def': 'sugar-glazed jellied candy', 'name': 'jelly_bean'}, {'frequency': 'f', 'synset': 'jersey.n.03', 'synonyms': ['jersey', 'T-shirt', 'tee_shirt'], 'id': 595, 'def': 'a close-fitting pullover shirt', 'name': 'jersey'}, {'frequency': 'c', 'synset': 'jet.n.01', 'synonyms': ['jet_plane', 'jet-propelled_plane'], 'id': 596, 'def': 'an airplane powered by one or more jet engines', 'name': 'jet_plane'}, {'frequency': 'r', 'synset': 'jewel.n.01', 'synonyms': ['jewel', 'gem', 'precious_stone'], 'id': 597, 'def': 'a precious or semiprecious stone incorporated into a piece of jewelry', 'name': 'jewel'}, {'frequency': 'c', 'synset': 'jewelry.n.01', 'synonyms': ['jewelry', 'jewellery'], 'id': 598, 'def': 'an adornment (as a bracelet or ring or necklace) made of precious metals and set with gems (or imitation gems)', 'name': 'jewelry'}, {'frequency': 'r', 'synset': 'joystick.n.02', 'synonyms': ['joystick'], 'id': 599, 'def': 'a control device for computers consisting of a vertical handle that can move freely in two directions', 'name': 'joystick'}, {'frequency': 'c', 'synset': 'jump_suit.n.01', 'synonyms': ['jumpsuit'], 'id': 600, 'def': "one-piece garment fashioned after a parachutist's uniform", 'name': 'jumpsuit'}, {'frequency': 'c', 'synset': 'kayak.n.01', 'synonyms': ['kayak'], 'id': 601, 'def': 'a small canoe consisting of a light frame made watertight with animal skins', 'name': 'kayak'}, {'frequency': 'r', 'synset': 'keg.n.02', 'synonyms': ['keg'], 'id': 602, 'def': 'small cask or barrel', 'name': 'keg'}, {'frequency': 'r', 'synset': 'kennel.n.01', 'synonyms': ['kennel', 'doghouse'], 'id': 603, 'def': 'outbuilding that serves as a shelter for a dog', 'name': 'kennel'}, {'frequency': 'c', 'synset': 'kettle.n.01', 'synonyms': ['kettle', 'boiler'], 'id': 604, 'def': 'a metal pot for stewing or boiling; usually has a lid', 'name': 'kettle'}, {'frequency': 'f', 'synset': 'key.n.01', 'synonyms': ['key'], 'id': 605, 'def': 'metal instrument used to unlock a lock', 'name': 'key'}, {'frequency': 'r', 'synset': 'keycard.n.01', 'synonyms': ['keycard'], 'id': 606, 'def': 'a plastic card used to gain access typically to a door', 'name': 'keycard'}, {'frequency': 'c', 'synset': 'kilt.n.01', 'synonyms': ['kilt'], 'id': 607, 'def': 'a knee-length pleated tartan skirt worn by men as part of the traditional dress in the Highlands of northern Scotland', 'name': 'kilt'}, {'frequency': 'c', 'synset': 'kimono.n.01', 'synonyms': ['kimono'], 'id': 608, 'def': 'a loose robe; imitated from robes originally worn by Japanese', 'name': 'kimono'}, {'frequency': 'f', 'synset': 'kitchen_sink.n.01', 'synonyms': ['kitchen_sink'], 'id': 609, 'def': 'a sink in a kitchen', 'name': 'kitchen_sink'}, {'frequency': 'r', 'synset': 'kitchen_table.n.01', 'synonyms': ['kitchen_table'], 'id': 610, 'def': 'a table in the kitchen', 'name': 'kitchen_table'}, {'frequency': 'f', 'synset': 'kite.n.03', 'synonyms': ['kite'], 'id': 611, 'def': 'plaything consisting of a light frame covered with tissue paper; flown in wind at end of a string', 'name': 'kite'}, {'frequency': 'c', 'synset': 'kitten.n.01', 'synonyms': ['kitten', 'kitty'], 'id': 612, 'def': 'young domestic cat', 'name': 'kitten'}, {'frequency': 'c', 'synset': 'kiwi.n.03', 'synonyms': ['kiwi_fruit'], 'id': 613, 'def': 'fuzzy brown egg-shaped fruit with slightly tart green flesh', 'name': 'kiwi_fruit'}, {'frequency': 'f', 'synset': 'knee_pad.n.01', 'synonyms': ['knee_pad'], 'id': 614, 'def': 'protective garment consisting of a pad worn by football or baseball or hockey players', 'name': 'knee_pad'}, {'frequency': 'f', 'synset': 'knife.n.01', 'synonyms': ['knife'], 'id': 615, 'def': 'tool with a blade and point used as a cutting instrument', 'name': 'knife'}, {'frequency': 'r', 'synset': 'knitting_needle.n.01', 'synonyms': ['knitting_needle'], 'id': 616, 'def': 'needle consisting of a slender rod with pointed ends; usually used in pairs', 'name': 'knitting_needle'}, {'frequency': 'f', 'synset': 'knob.n.02', 'synonyms': ['knob'], 'id': 617, 'def': 'a round handle often found on a door', 'name': 'knob'}, {'frequency': 'r', 'synset': 'knocker.n.05', 'synonyms': ['knocker_(on_a_door)', 'doorknocker'], 'id': 618, 'def': 'a device (usually metal and ornamental) attached by a hinge to a door', 'name': 'knocker_(on_a_door)'}, {'frequency': 'r', 'synset': 'koala.n.01', 'synonyms': ['koala', 'koala_bear'], 'id': 619, 'def': 'sluggish tailless Australian marsupial with grey furry ears and coat', 'name': 'koala'}, {'frequency': 'r', 'synset': 'lab_coat.n.01', 'synonyms': ['lab_coat', 'laboratory_coat'], 'id': 620, 'def': 'a light coat worn to protect clothing from substances used while working in a laboratory', 'name': 'lab_coat'}, {'frequency': 'f', 'synset': 'ladder.n.01', 'synonyms': ['ladder'], 'id': 621, 'def': 'steps consisting of two parallel members connected by rungs', 'name': 'ladder'}, {'frequency': 'c', 'synset': 'ladle.n.01', 'synonyms': ['ladle'], 'id': 622, 'def': 'a spoon-shaped vessel with a long handle frequently used to transfer liquids', 'name': 'ladle'}, {'frequency': 'c', 'synset': 'ladybug.n.01', 'synonyms': ['ladybug', 'ladybeetle', 'ladybird_beetle'], 'id': 623, 'def': 'small round bright-colored and spotted beetle, typically red and black', 'name': 'ladybug'}, {'frequency': 'f', 'synset': 'lamb.n.01', 'synonyms': ['lamb_(animal)'], 'id': 624, 'def': 'young sheep', 'name': 'lamb_(animal)'}, {'frequency': 'r', 'synset': 'lamb_chop.n.01', 'synonyms': ['lamb-chop', 'lambchop'], 'id': 625, 'def': 'chop cut from a lamb', 'name': 'lamb-chop'}, {'frequency': 'f', 'synset': 'lamp.n.02', 'synonyms': ['lamp'], 'id': 626, 'def': 'a piece of furniture holding one or more electric light bulbs', 'name': 'lamp'}, {'frequency': 'f', 'synset': 'lamppost.n.01', 'synonyms': ['lamppost'], 'id': 627, 'def': 'a metal post supporting an outdoor lamp (such as a streetlight)', 'name': 'lamppost'}, {'frequency': 'f', 'synset': 'lampshade.n.01', 'synonyms': ['lampshade'], 'id': 628, 'def': 'a protective ornamental shade used to screen a light bulb from direct view', 'name': 'lampshade'}, {'frequency': 'c', 'synset': 'lantern.n.01', 'synonyms': ['lantern'], 'id': 629, 'def': 'light in a transparent protective case', 'name': 'lantern'}, {'frequency': 'f', 'synset': 'lanyard.n.02', 'synonyms': ['lanyard', 'laniard'], 'id': 630, 'def': 'a cord worn around the neck to hold a knife or whistle, etc.', 'name': 'lanyard'}, {'frequency': 'f', 'synset': 'laptop.n.01', 'synonyms': ['laptop_computer', 'notebook_computer'], 'id': 631, 'def': 'a portable computer small enough to use in your lap', 'name': 'laptop_computer'}, {'frequency': 'r', 'synset': 'lasagna.n.01', 'synonyms': ['lasagna', 'lasagne'], 'id': 632, 'def': 'baked dish of layers of lasagna pasta with sauce and cheese and meat or vegetables', 'name': 'lasagna'}, {'frequency': 'f', 'synset': 'latch.n.02', 'synonyms': ['latch'], 'id': 633, 'def': 'a bar that can be lowered or slid into a groove to fasten a door or gate', 'name': 'latch'}, {'frequency': 'r', 'synset': 'lawn_mower.n.01', 'synonyms': ['lawn_mower'], 'id': 634, 'def': 'garden tool for mowing grass on lawns', 'name': 'lawn_mower'}, {'frequency': 'r', 'synset': 'leather.n.01', 'synonyms': ['leather'], 'id': 635, 'def': 'an animal skin made smooth and flexible by removing the hair and then tanning', 'name': 'leather'}, {'frequency': 'c', 'synset': 'legging.n.01', 'synonyms': ['legging_(clothing)', 'leging_(clothing)', 'leg_covering'], 'id': 636, 'def': 'a garment covering the leg (usually extending from the knee to the ankle)', 'name': 'legging_(clothing)'}, {'frequency': 'c', 'synset': 'lego.n.01', 'synonyms': ['Lego', 'Lego_set'], 'id': 637, 'def': "a child's plastic construction set for making models from blocks", 'name': 'Lego'}, {'frequency': 'r', 'synset': 'legume.n.02', 'synonyms': ['legume'], 'id': 638, 'def': 'the fruit or seed of bean or pea plants', 'name': 'legume'}, {'frequency': 'f', 'synset': 'lemon.n.01', 'synonyms': ['lemon'], 'id': 639, 'def': 'yellow oval fruit with juicy acidic flesh', 'name': 'lemon'}, {'frequency': 'r', 'synset': 'lemonade.n.01', 'synonyms': ['lemonade'], 'id': 640, 'def': 'sweetened beverage of diluted lemon juice', 'name': 'lemonade'}, {'frequency': 'f', 'synset': 'lettuce.n.02', 'synonyms': ['lettuce'], 'id': 641, 'def': 'leafy plant commonly eaten in salad or on sandwiches', 'name': 'lettuce'}, {'frequency': 'f', 'synset': 'license_plate.n.01', 'synonyms': ['license_plate', 'numberplate'], 'id': 642, 'def': "a plate mounted on the front and back of car and bearing the car's registration number", 'name': 'license_plate'}, {'frequency': 'f', 'synset': 'life_buoy.n.01', 'synonyms': ['life_buoy', 'lifesaver', 'life_belt', 'life_ring'], 'id': 643, 'def': 'a ring-shaped life preserver used to prevent drowning (NOT a life-jacket or vest)', 'name': 'life_buoy'}, {'frequency': 'f', 'synset': 'life_jacket.n.01', 'synonyms': ['life_jacket', 'life_vest'], 'id': 644, 'def': 'life preserver consisting of a sleeveless jacket of buoyant or inflatable design', 'name': 'life_jacket'}, {'frequency': 'f', 'synset': 'light_bulb.n.01', 'synonyms': ['lightbulb'], 'id': 645, 'def': 'lightblub/source of light', 'name': 'lightbulb'}, {'frequency': 'r', 'synset': 'lightning_rod.n.02', 'synonyms': ['lightning_rod', 'lightning_conductor'], 'id': 646, 'def': 'a metallic conductor that is attached to a high point and leads to the ground', 'name': 'lightning_rod'}, {'frequency': 'f', 'synset': 'lime.n.06', 'synonyms': ['lime'], 'id': 647, 'def': 'the green acidic fruit of any of various lime trees', 'name': 'lime'}, {'frequency': 'r', 'synset': 'limousine.n.01', 'synonyms': ['limousine'], 'id': 648, 'def': 'long luxurious car; usually driven by a chauffeur', 'name': 'limousine'}, {'frequency': 'c', 'synset': 'lion.n.01', 'synonyms': ['lion'], 'id': 649, 'def': 'large gregarious predatory cat of Africa and India', 'name': 'lion'}, {'frequency': 'c', 'synset': 'lip_balm.n.01', 'synonyms': ['lip_balm'], 'id': 650, 'def': 'a balm applied to the lips', 'name': 'lip_balm'}, {'frequency': 'r', 'synset': 'liquor.n.01', 'synonyms': ['liquor', 'spirits', 'hard_liquor', 'liqueur', 'cordial'], 'id': 651, 'def': 'liquor or beer', 'name': 'liquor'}, {'frequency': 'c', 'synset': 'lizard.n.01', 'synonyms': ['lizard'], 'id': 652, 'def': 'a reptile with usually two pairs of legs and a tapering tail', 'name': 'lizard'}, {'frequency': 'f', 'synset': 'log.n.01', 'synonyms': ['log'], 'id': 653, 'def': 'a segment of the trunk of a tree when stripped of branches', 'name': 'log'}, {'frequency': 'c', 'synset': 'lollipop.n.02', 'synonyms': ['lollipop'], 'id': 654, 'def': 'hard candy on a stick', 'name': 'lollipop'}, {'frequency': 'f', 'synset': 'loudspeaker.n.01', 'synonyms': ['speaker_(stero_equipment)'], 'id': 655, 'def': 'electronic device that produces sound often as part of a stereo system', 'name': 'speaker_(stero_equipment)'}, {'frequency': 'c', 'synset': 'love_seat.n.01', 'synonyms': ['loveseat'], 'id': 656, 'def': 'small sofa that seats two people', 'name': 'loveseat'}, {'frequency': 'r', 'synset': 'machine_gun.n.01', 'synonyms': ['machine_gun'], 'id': 657, 'def': 'a rapidly firing automatic gun', 'name': 'machine_gun'}, {'frequency': 'f', 'synset': 'magazine.n.02', 'synonyms': ['magazine'], 'id': 658, 'def': 'a paperback periodic publication', 'name': 'magazine'}, {'frequency': 'f', 'synset': 'magnet.n.01', 'synonyms': ['magnet'], 'id': 659, 'def': 'a device that attracts iron and produces a magnetic field', 'name': 'magnet'}, {'frequency': 'c', 'synset': 'mail_slot.n.01', 'synonyms': ['mail_slot'], 'id': 660, 'def': 'a slot (usually in a door) through which mail can be delivered', 'name': 'mail_slot'}, {'frequency': 'f', 'synset': 'mailbox.n.01', 'synonyms': ['mailbox_(at_home)', 'letter_box_(at_home)'], 'id': 661, 'def': 'a private box for delivery of mail', 'name': 'mailbox_(at_home)'}, {'frequency': 'r', 'synset': 'mallard.n.01', 'synonyms': ['mallard'], 'id': 662, 'def': 'wild dabbling duck from which domestic ducks are descended', 'name': 'mallard'}, {'frequency': 'r', 'synset': 'mallet.n.01', 'synonyms': ['mallet'], 'id': 663, 'def': 'a sports implement with a long handle and a hammer-like head used to hit a ball', 'name': 'mallet'}, {'frequency': 'r', 'synset': 'mammoth.n.01', 'synonyms': ['mammoth'], 'id': 664, 'def': 'any of numerous extinct elephants widely distributed in the Pleistocene', 'name': 'mammoth'}, {'frequency': 'r', 'synset': 'manatee.n.01', 'synonyms': ['manatee'], 'id': 665, 'def': 'sirenian mammal of tropical coastal waters of America', 'name': 'manatee'}, {'frequency': 'c', 'synset': 'mandarin.n.05', 'synonyms': ['mandarin_orange'], 'id': 666, 'def': 'a somewhat flat reddish-orange loose skinned citrus of China', 'name': 'mandarin_orange'}, {'frequency': 'c', 'synset': 'manger.n.01', 'synonyms': ['manger', 'trough'], 'id': 667, 'def': 'a container (usually in a barn or stable) from which cattle or horses feed', 'name': 'manger'}, {'frequency': 'f', 'synset': 'manhole.n.01', 'synonyms': ['manhole'], 'id': 668, 'def': 'a hole (usually with a flush cover) through which a person can gain access to an underground structure', 'name': 'manhole'}, {'frequency': 'f', 'synset': 'map.n.01', 'synonyms': ['map'], 'id': 669, 'def': "a diagrammatic representation of the earth's surface (or part of it)", 'name': 'map'}, {'frequency': 'f', 'synset': 'marker.n.03', 'synonyms': ['marker'], 'id': 670, 'def': 'a writing implement for making a mark', 'name': 'marker'}, {'frequency': 'r', 'synset': 'martini.n.01', 'synonyms': ['martini'], 'id': 671, 'def': 'a cocktail made of gin (or vodka) with dry vermouth', 'name': 'martini'}, {'frequency': 'r', 'synset': 'mascot.n.01', 'synonyms': ['mascot'], 'id': 672, 'def': 'a person or animal that is adopted by a team or other group as a symbolic figure', 'name': 'mascot'}, {'frequency': 'c', 'synset': 'mashed_potato.n.01', 'synonyms': ['mashed_potato'], 'id': 673, 'def': 'potato that has been peeled and boiled and then mashed', 'name': 'mashed_potato'}, {'frequency': 'r', 'synset': 'masher.n.02', 'synonyms': ['masher'], 'id': 674, 'def': 'a kitchen utensil used for mashing (e.g. potatoes)', 'name': 'masher'}, {'frequency': 'f', 'synset': 'mask.n.04', 'synonyms': ['mask', 'facemask'], 'id': 675, 'def': 'a protective covering worn over the face', 'name': 'mask'}, {'frequency': 'f', 'synset': 'mast.n.01', 'synonyms': ['mast'], 'id': 676, 'def': 'a vertical spar for supporting sails', 'name': 'mast'}, {'frequency': 'c', 'synset': 'mat.n.03', 'synonyms': ['mat_(gym_equipment)', 'gym_mat'], 'id': 677, 'def': 'sports equipment consisting of a piece of thick padding on the floor for gymnastics', 'name': 'mat_(gym_equipment)'}, {'frequency': 'r', 'synset': 'matchbox.n.01', 'synonyms': ['matchbox'], 'id': 678, 'def': 'a box for holding matches', 'name': 'matchbox'}, {'frequency': 'f', 'synset': 'mattress.n.01', 'synonyms': ['mattress'], 'id': 679, 'def': 'a thick pad filled with resilient material used as a bed or part of a bed', 'name': 'mattress'}, {'frequency': 'c', 'synset': 'measuring_cup.n.01', 'synonyms': ['measuring_cup'], 'id': 680, 'def': 'graduated cup used to measure liquid or granular ingredients', 'name': 'measuring_cup'}, {'frequency': 'c', 'synset': 'measuring_stick.n.01', 'synonyms': ['measuring_stick', 'ruler_(measuring_stick)', 'measuring_rod'], 'id': 681, 'def': 'measuring instrument having a sequence of marks at regular intervals', 'name': 'measuring_stick'}, {'frequency': 'c', 'synset': 'meatball.n.01', 'synonyms': ['meatball'], 'id': 682, 'def': 'ground meat formed into a ball and fried or simmered in broth', 'name': 'meatball'}, {'frequency': 'c', 'synset': 'medicine.n.02', 'synonyms': ['medicine'], 'id': 683, 'def': 'something that treats or prevents or alleviates the symptoms of disease', 'name': 'medicine'}, {'frequency': 'c', 'synset': 'melon.n.01', 'synonyms': ['melon'], 'id': 684, 'def': 'fruit of the gourd family having a hard rind and sweet juicy flesh', 'name': 'melon'}, {'frequency': 'f', 'synset': 'microphone.n.01', 'synonyms': ['microphone'], 'id': 685, 'def': 'device for converting sound waves into electrical energy', 'name': 'microphone'}, {'frequency': 'r', 'synset': 'microscope.n.01', 'synonyms': ['microscope'], 'id': 686, 'def': 'magnifier of the image of small objects', 'name': 'microscope'}, {'frequency': 'f', 'synset': 'microwave.n.02', 'synonyms': ['microwave_oven'], 'id': 687, 'def': 'kitchen appliance that cooks food by passing an electromagnetic wave through it', 'name': 'microwave_oven'}, {'frequency': 'r', 'synset': 'milestone.n.01', 'synonyms': ['milestone', 'milepost'], 'id': 688, 'def': 'stone post at side of a road to show distances', 'name': 'milestone'}, {'frequency': 'f', 'synset': 'milk.n.01', 'synonyms': ['milk'], 'id': 689, 'def': 'a white nutritious liquid secreted by mammals and used as food by human beings', 'name': 'milk'}, {'frequency': 'r', 'synset': 'milk_can.n.01', 'synonyms': ['milk_can'], 'id': 690, 'def': 'can for transporting milk', 'name': 'milk_can'}, {'frequency': 'r', 'synset': 'milkshake.n.01', 'synonyms': ['milkshake'], 'id': 691, 'def': 'frothy drink of milk and flavoring and sometimes fruit or ice cream', 'name': 'milkshake'}, {'frequency': 'f', 'synset': 'minivan.n.01', 'synonyms': ['minivan'], 'id': 692, 'def': 'a small box-shaped passenger van', 'name': 'minivan'}, {'frequency': 'r', 'synset': 'mint.n.05', 'synonyms': ['mint_candy'], 'id': 693, 'def': 'a candy that is flavored with a mint oil', 'name': 'mint_candy'}, {'frequency': 'f', 'synset': 'mirror.n.01', 'synonyms': ['mirror'], 'id': 694, 'def': 'polished surface that forms images by reflecting light', 'name': 'mirror'}, {'frequency': 'c', 'synset': 'mitten.n.01', 'synonyms': ['mitten'], 'id': 695, 'def': 'glove that encases the thumb separately and the other four fingers together', 'name': 'mitten'}, {'frequency': 'c', 'synset': 'mixer.n.04', 'synonyms': ['mixer_(kitchen_tool)', 'stand_mixer'], 'id': 696, 'def': 'a kitchen utensil that is used for mixing foods', 'name': 'mixer_(kitchen_tool)'}, {'frequency': 'c', 'synset': 'money.n.03', 'synonyms': ['money'], 'id': 697, 'def': 'the official currency issued by a government or national bank', 'name': 'money'}, {'frequency': 'f', 'synset': 'monitor.n.04', 'synonyms': ['monitor_(computer_equipment) computer_monitor'], 'id': 698, 'def': 'a computer monitor', 'name': 'monitor_(computer_equipment) computer_monitor'}, {'frequency': 'c', 'synset': 'monkey.n.01', 'synonyms': ['monkey'], 'id': 699, 'def': 'any of various long-tailed primates', 'name': 'monkey'}, {'frequency': 'f', 'synset': 'motor.n.01', 'synonyms': ['motor'], 'id': 700, 'def': 'machine that converts other forms of energy into mechanical energy and so imparts motion', 'name': 'motor'}, {'frequency': 'f', 'synset': 'motor_scooter.n.01', 'synonyms': ['motor_scooter', 'scooter'], 'id': 701, 'def': 'a wheeled vehicle with small wheels and a low-powered engine', 'name': 'motor_scooter'}, {'frequency': 'r', 'synset': 'motor_vehicle.n.01', 'synonyms': ['motor_vehicle', 'automotive_vehicle'], 'id': 702, 'def': 'a self-propelled wheeled vehicle that does not run on rails', 'name': 'motor_vehicle'}, {'frequency': 'f', 'synset': 'motorcycle.n.01', 'synonyms': ['motorcycle'], 'id': 703, 'def': 'a motor vehicle with two wheels and a strong frame', 'name': 'motorcycle'}, {'frequency': 'f', 'synset': 'mound.n.01', 'synonyms': ['mound_(baseball)', "pitcher's_mound"], 'id': 704, 'def': '(baseball) the slight elevation on which the pitcher stands', 'name': 'mound_(baseball)'}, {'frequency': 'f', 'synset': 'mouse.n.04', 'synonyms': ['mouse_(computer_equipment)', 'computer_mouse'], 'id': 705, 'def': 'a computer input device that controls an on-screen pointer (does not include trackpads / touchpads)', 'name': 'mouse_(computer_equipment)'}, {'frequency': 'f', 'synset': 'mousepad.n.01', 'synonyms': ['mousepad'], 'id': 706, 'def': 'a small portable pad that provides an operating surface for a computer mouse', 'name': 'mousepad'}, {'frequency': 'c', 'synset': 'muffin.n.01', 'synonyms': ['muffin'], 'id': 707, 'def': 'a sweet quick bread baked in a cup-shaped pan', 'name': 'muffin'}, {'frequency': 'f', 'synset': 'mug.n.04', 'synonyms': ['mug'], 'id': 708, 'def': 'with handle and usually cylindrical', 'name': 'mug'}, {'frequency': 'f', 'synset': 'mushroom.n.02', 'synonyms': ['mushroom'], 'id': 709, 'def': 'a common mushroom', 'name': 'mushroom'}, {'frequency': 'r', 'synset': 'music_stool.n.01', 'synonyms': ['music_stool', 'piano_stool'], 'id': 710, 'def': 'a stool for piano players; usually adjustable in height', 'name': 'music_stool'}, {'frequency': 'c', 'synset': 'musical_instrument.n.01', 'synonyms': ['musical_instrument', 'instrument_(musical)'], 'id': 711, 'def': 'any of various devices or contrivances that can be used to produce musical tones or sounds', 'name': 'musical_instrument'}, {'frequency': 'r', 'synset': 'nailfile.n.01', 'synonyms': ['nailfile'], 'id': 712, 'def': 'a small flat file for shaping the nails', 'name': 'nailfile'}, {'frequency': 'f', 'synset': 'napkin.n.01', 'synonyms': ['napkin', 'table_napkin', 'serviette'], 'id': 713, 'def': 'a small piece of table linen or paper that is used to wipe the mouth and to cover the lap in order to protect clothing', 'name': 'napkin'}, {'frequency': 'r', 'synset': 'neckerchief.n.01', 'synonyms': ['neckerchief'], 'id': 714, 'def': 'a kerchief worn around the neck', 'name': 'neckerchief'}, {'frequency': 'f', 'synset': 'necklace.n.01', 'synonyms': ['necklace'], 'id': 715, 'def': 'jewelry consisting of a cord or chain (often bearing gems) worn about the neck as an ornament', 'name': 'necklace'}, {'frequency': 'f', 'synset': 'necktie.n.01', 'synonyms': ['necktie', 'tie_(necktie)'], 'id': 716, 'def': 'neckwear consisting of a long narrow piece of material worn under a collar and tied in knot at the front', 'name': 'necktie'}, {'frequency': 'c', 'synset': 'needle.n.03', 'synonyms': ['needle'], 'id': 717, 'def': 'a sharp pointed implement (usually metal)', 'name': 'needle'}, {'frequency': 'c', 'synset': 'nest.n.01', 'synonyms': ['nest'], 'id': 718, 'def': 'a structure in which animals lay eggs or give birth to their young', 'name': 'nest'}, {'frequency': 'f', 'synset': 'newspaper.n.01', 'synonyms': ['newspaper', 'paper_(newspaper)'], 'id': 719, 'def': 'a daily or weekly publication on folded sheets containing news, articles, and advertisements', 'name': 'newspaper'}, {'frequency': 'c', 'synset': 'newsstand.n.01', 'synonyms': ['newsstand'], 'id': 720, 'def': 'a stall where newspapers and other periodicals are sold', 'name': 'newsstand'}, {'frequency': 'c', 'synset': 'nightwear.n.01', 'synonyms': ['nightshirt', 'nightwear', 'sleepwear', 'nightclothes'], 'id': 721, 'def': 'garments designed to be worn in bed', 'name': 'nightshirt'}, {'frequency': 'r', 'synset': 'nosebag.n.01', 'synonyms': ['nosebag_(for_animals)', 'feedbag'], 'id': 722, 'def': 'a canvas bag that is used to feed an animal (such as a horse); covers the muzzle and fastens at the top of the head', 'name': 'nosebag_(for_animals)'}, {'frequency': 'c', 'synset': 'noseband.n.01', 'synonyms': ['noseband_(for_animals)', 'nosepiece_(for_animals)'], 'id': 723, 'def': "a strap that is the part of a bridle that goes over the animal's nose", 'name': 'noseband_(for_animals)'}, {'frequency': 'f', 'synset': 'notebook.n.01', 'synonyms': ['notebook'], 'id': 724, 'def': 'a book with blank pages for recording notes or memoranda', 'name': 'notebook'}, {'frequency': 'c', 'synset': 'notepad.n.01', 'synonyms': ['notepad'], 'id': 725, 'def': 'a pad of paper for keeping notes', 'name': 'notepad'}, {'frequency': 'f', 'synset': 'nut.n.03', 'synonyms': ['nut'], 'id': 726, 'def': 'a small metal block (usually square or hexagonal) with internal screw thread to be fitted onto a bolt', 'name': 'nut'}, {'frequency': 'r', 'synset': 'nutcracker.n.01', 'synonyms': ['nutcracker'], 'id': 727, 'def': 'a hand tool used to crack nuts open', 'name': 'nutcracker'}, {'frequency': 'f', 'synset': 'oar.n.01', 'synonyms': ['oar'], 'id': 728, 'def': 'an implement used to propel or steer a boat', 'name': 'oar'}, {'frequency': 'r', 'synset': 'octopus.n.01', 'synonyms': ['octopus_(food)'], 'id': 729, 'def': 'tentacles of octopus prepared as food', 'name': 'octopus_(food)'}, {'frequency': 'r', 'synset': 'octopus.n.02', 'synonyms': ['octopus_(animal)'], 'id': 730, 'def': 'bottom-living cephalopod having a soft oval body with eight long tentacles', 'name': 'octopus_(animal)'}, {'frequency': 'c', 'synset': 'oil_lamp.n.01', 'synonyms': ['oil_lamp', 'kerosene_lamp', 'kerosine_lamp'], 'id': 731, 'def': 'a lamp that burns oil (as kerosine) for light', 'name': 'oil_lamp'}, {'frequency': 'c', 'synset': 'olive_oil.n.01', 'synonyms': ['olive_oil'], 'id': 732, 'def': 'oil from olives', 'name': 'olive_oil'}, {'frequency': 'r', 'synset': 'omelet.n.01', 'synonyms': ['omelet', 'omelette'], 'id': 733, 'def': 'beaten eggs cooked until just set; may be folded around e.g. ham or cheese or jelly', 'name': 'omelet'}, {'frequency': 'f', 'synset': 'onion.n.01', 'synonyms': ['onion'], 'id': 734, 'def': 'the bulb of an onion plant', 'name': 'onion'}, {'frequency': 'f', 'synset': 'orange.n.01', 'synonyms': ['orange_(fruit)'], 'id': 735, 'def': 'orange (FRUIT of an orange tree)', 'name': 'orange_(fruit)'}, {'frequency': 'c', 'synset': 'orange_juice.n.01', 'synonyms': ['orange_juice'], 'id': 736, 'def': 'bottled or freshly squeezed juice of oranges', 'name': 'orange_juice'}, {'frequency': 'c', 'synset': 'ostrich.n.02', 'synonyms': ['ostrich'], 'id': 737, 'def': 'fast-running African flightless bird with two-toed feet; largest living bird', 'name': 'ostrich'}, {'frequency': 'f', 'synset': 'ottoman.n.03', 'synonyms': ['ottoman', 'pouf', 'pouffe', 'hassock'], 'id': 738, 'def': 'a thick standalone cushion used as a seat or footrest, often next to a chair', 'name': 'ottoman'}, {'frequency': 'f', 'synset': 'oven.n.01', 'synonyms': ['oven'], 'id': 739, 'def': 'kitchen appliance used for baking or roasting', 'name': 'oven'}, {'frequency': 'c', 'synset': 'overall.n.01', 'synonyms': ['overalls_(clothing)'], 'id': 740, 'def': 'work clothing consisting of denim trousers usually with a bib and shoulder straps', 'name': 'overalls_(clothing)'}, {'frequency': 'c', 'synset': 'owl.n.01', 'synonyms': ['owl'], 'id': 741, 'def': 'nocturnal bird of prey with hawk-like beak and claws and large head with front-facing eyes', 'name': 'owl'}, {'frequency': 'c', 'synset': 'packet.n.03', 'synonyms': ['packet'], 'id': 742, 'def': 'a small package or bundle', 'name': 'packet'}, {'frequency': 'r', 'synset': 'pad.n.03', 'synonyms': ['inkpad', 'inking_pad', 'stamp_pad'], 'id': 743, 'def': 'absorbent material saturated with ink used to transfer ink evenly to a rubber stamp', 'name': 'inkpad'}, {'frequency': 'c', 'synset': 'pad.n.04', 'synonyms': ['pad'], 'id': 744, 'def': 'mostly arm/knee pads labeled', 'name': 'pad'}, {'frequency': 'f', 'synset': 'paddle.n.04', 'synonyms': ['paddle', 'boat_paddle'], 'id': 745, 'def': 'a short light oar used without an oarlock to propel a canoe or small boat', 'name': 'paddle'}, {'frequency': 'c', 'synset': 'padlock.n.01', 'synonyms': ['padlock'], 'id': 746, 'def': 'a detachable, portable lock', 'name': 'padlock'}, {'frequency': 'c', 'synset': 'paintbrush.n.01', 'synonyms': ['paintbrush'], 'id': 747, 'def': 'a brush used as an applicator to apply paint', 'name': 'paintbrush'}, {'frequency': 'f', 'synset': 'painting.n.01', 'synonyms': ['painting'], 'id': 748, 'def': 'graphic art consisting of an artistic composition made by applying paints to a surface', 'name': 'painting'}, {'frequency': 'f', 'synset': 'pajama.n.02', 'synonyms': ['pajamas', 'pyjamas'], 'id': 749, 'def': 'loose-fitting nightclothes worn for sleeping or lounging', 'name': 'pajamas'}, {'frequency': 'c', 'synset': 'palette.n.02', 'synonyms': ['palette', 'pallet'], 'id': 750, 'def': 'board that provides a flat surface on which artists mix paints and the range of colors used', 'name': 'palette'}, {'frequency': 'f', 'synset': 'pan.n.01', 'synonyms': ['pan_(for_cooking)', 'cooking_pan'], 'id': 751, 'def': 'cooking utensil consisting of a wide metal vessel', 'name': 'pan_(for_cooking)'}, {'frequency': 'r', 'synset': 'pan.n.03', 'synonyms': ['pan_(metal_container)'], 'id': 752, 'def': 'shallow container made of metal', 'name': 'pan_(metal_container)'}, {'frequency': 'c', 'synset': 'pancake.n.01', 'synonyms': ['pancake'], 'id': 753, 'def': 'a flat cake of thin batter fried on both sides on a griddle', 'name': 'pancake'}, {'frequency': 'r', 'synset': 'pantyhose.n.01', 'synonyms': ['pantyhose'], 'id': 754, 'def': "a woman's tights consisting of underpants and stockings", 'name': 'pantyhose'}, {'frequency': 'r', 'synset': 'papaya.n.02', 'synonyms': ['papaya'], 'id': 755, 'def': 'large oval melon-like tropical fruit with yellowish flesh', 'name': 'papaya'}, {'frequency': 'f', 'synset': 'paper_plate.n.01', 'synonyms': ['paper_plate'], 'id': 756, 'def': 'a disposable plate made of cardboard', 'name': 'paper_plate'}, {'frequency': 'f', 'synset': 'paper_towel.n.01', 'synonyms': ['paper_towel'], 'id': 757, 'def': 'a disposable towel made of absorbent paper', 'name': 'paper_towel'}, {'frequency': 'r', 'synset': 'paperback_book.n.01', 'synonyms': ['paperback_book', 'paper-back_book', 'softback_book', 'soft-cover_book'], 'id': 758, 'def': 'a book with paper covers', 'name': 'paperback_book'}, {'frequency': 'r', 'synset': 'paperweight.n.01', 'synonyms': ['paperweight'], 'id': 759, 'def': 'a weight used to hold down a stack of papers', 'name': 'paperweight'}, {'frequency': 'c', 'synset': 'parachute.n.01', 'synonyms': ['parachute'], 'id': 760, 'def': 'rescue equipment consisting of a device that fills with air and retards your fall', 'name': 'parachute'}, {'frequency': 'c', 'synset': 'parakeet.n.01', 'synonyms': ['parakeet', 'parrakeet', 'parroket', 'paraquet', 'paroquet', 'parroquet'], 'id': 761, 'def': 'any of numerous small slender long-tailed parrots', 'name': 'parakeet'}, {'frequency': 'c', 'synset': 'parasail.n.01', 'synonyms': ['parasail_(sports)'], 'id': 762, 'def': 'parachute that will lift a person up into the air when it is towed by a motorboat or a car', 'name': 'parasail_(sports)'}, {'frequency': 'c', 'synset': 'parasol.n.01', 'synonyms': ['parasol', 'sunshade'], 'id': 763, 'def': 'a handheld collapsible source of shade', 'name': 'parasol'}, {'frequency': 'r', 'synset': 'parchment.n.01', 'synonyms': ['parchment'], 'id': 764, 'def': 'a superior paper resembling sheepskin', 'name': 'parchment'}, {'frequency': 'c', 'synset': 'parka.n.01', 'synonyms': ['parka', 'anorak'], 'id': 765, 'def': "a kind of heavy jacket (`windcheater' is a British term)", 'name': 'parka'}, {'frequency': 'f', 'synset': 'parking_meter.n.01', 'synonyms': ['parking_meter'], 'id': 766, 'def': 'a coin-operated timer located next to a parking space', 'name': 'parking_meter'}, {'frequency': 'c', 'synset': 'parrot.n.01', 'synonyms': ['parrot'], 'id': 767, 'def': 'usually brightly colored tropical birds with short hooked beaks and the ability to mimic sounds', 'name': 'parrot'}, {'frequency': 'c', 'synset': 'passenger_car.n.01', 'synonyms': ['passenger_car_(part_of_a_train)', 'coach_(part_of_a_train)'], 'id': 768, 'def': 'a railcar where passengers ride', 'name': 'passenger_car_(part_of_a_train)'}, {'frequency': 'r', 'synset': 'passenger_ship.n.01', 'synonyms': ['passenger_ship'], 'id': 769, 'def': 'a ship built to carry passengers', 'name': 'passenger_ship'}, {'frequency': 'c', 'synset': 'passport.n.02', 'synonyms': ['passport'], 'id': 770, 'def': 'a document issued by a country to a citizen allowing that person to travel abroad and re-enter the home country', 'name': 'passport'}, {'frequency': 'f', 'synset': 'pastry.n.02', 'synonyms': ['pastry'], 'id': 771, 'def': 'any of various baked foods made of dough or batter', 'name': 'pastry'}, {'frequency': 'r', 'synset': 'patty.n.01', 'synonyms': ['patty_(food)'], 'id': 772, 'def': 'small flat mass of chopped food', 'name': 'patty_(food)'}, {'frequency': 'c', 'synset': 'pea.n.01', 'synonyms': ['pea_(food)'], 'id': 773, 'def': 'seed of a pea plant used for food', 'name': 'pea_(food)'}, {'frequency': 'c', 'synset': 'peach.n.03', 'synonyms': ['peach'], 'id': 774, 'def': 'downy juicy fruit with sweet yellowish or whitish flesh', 'name': 'peach'}, {'frequency': 'c', 'synset': 'peanut_butter.n.01', 'synonyms': ['peanut_butter'], 'id': 775, 'def': 'a spread made from ground peanuts', 'name': 'peanut_butter'}, {'frequency': 'f', 'synset': 'pear.n.01', 'synonyms': ['pear'], 'id': 776, 'def': 'sweet juicy gritty-textured fruit available in many varieties', 'name': 'pear'}, {'frequency': 'c', 'synset': 'peeler.n.03', 'synonyms': ['peeler_(tool_for_fruit_and_vegetables)'], 'id': 777, 'def': 'a device for peeling vegetables or fruits', 'name': 'peeler_(tool_for_fruit_and_vegetables)'}, {'frequency': 'r', 'synset': 'peg.n.04', 'synonyms': ['wooden_leg', 'pegleg'], 'id': 778, 'def': 'a prosthesis that replaces a missing leg', 'name': 'wooden_leg'}, {'frequency': 'r', 'synset': 'pegboard.n.01', 'synonyms': ['pegboard'], 'id': 779, 'def': 'a board perforated with regularly spaced holes into which pegs can be fitted', 'name': 'pegboard'}, {'frequency': 'c', 'synset': 'pelican.n.01', 'synonyms': ['pelican'], 'id': 780, 'def': 'large long-winged warm-water seabird having a large bill with a distensible pouch for fish', 'name': 'pelican'}, {'frequency': 'f', 'synset': 'pen.n.01', 'synonyms': ['pen'], 'id': 781, 'def': 'a writing implement with a point from which ink flows', 'name': 'pen'}, {'frequency': 'f', 'synset': 'pencil.n.01', 'synonyms': ['pencil'], 'id': 782, 'def': 'a thin cylindrical pointed writing implement made of wood and graphite', 'name': 'pencil'}, {'frequency': 'r', 'synset': 'pencil_box.n.01', 'synonyms': ['pencil_box', 'pencil_case'], 'id': 783, 'def': 'a box for holding pencils', 'name': 'pencil_box'}, {'frequency': 'r', 'synset': 'pencil_sharpener.n.01', 'synonyms': ['pencil_sharpener'], 'id': 784, 'def': 'a rotary implement for sharpening the point on pencils', 'name': 'pencil_sharpener'}, {'frequency': 'r', 'synset': 'pendulum.n.01', 'synonyms': ['pendulum'], 'id': 785, 'def': 'an apparatus consisting of an object mounted so that it swings freely under the influence of gravity', 'name': 'pendulum'}, {'frequency': 'c', 'synset': 'penguin.n.01', 'synonyms': ['penguin'], 'id': 786, 'def': 'short-legged flightless birds of cold southern regions having webbed feet and wings modified as flippers', 'name': 'penguin'}, {'frequency': 'r', 'synset': 'pennant.n.02', 'synonyms': ['pennant'], 'id': 787, 'def': 'a flag longer than it is wide (and often tapering)', 'name': 'pennant'}, {'frequency': 'r', 'synset': 'penny.n.02', 'synonyms': ['penny_(coin)'], 'id': 788, 'def': 'a coin worth one-hundredth of the value of the basic unit', 'name': 'penny_(coin)'}, {'frequency': 'f', 'synset': 'pepper.n.03', 'synonyms': ['pepper', 'peppercorn'], 'id': 789, 'def': 'pungent seasoning from the berry of the common pepper plant; whole or ground', 'name': 'pepper'}, {'frequency': 'c', 'synset': 'pepper_mill.n.01', 'synonyms': ['pepper_mill', 'pepper_grinder'], 'id': 790, 'def': 'a mill for grinding pepper', 'name': 'pepper_mill'}, {'frequency': 'c', 'synset': 'perfume.n.02', 'synonyms': ['perfume'], 'id': 791, 'def': 'a toiletry that emits and diffuses a fragrant odor', 'name': 'perfume'}, {'frequency': 'r', 'synset': 'persimmon.n.02', 'synonyms': ['persimmon'], 'id': 792, 'def': 'orange fruit resembling a plum; edible when fully ripe', 'name': 'persimmon'}, {'frequency': 'f', 'synset': 'person.n.01', 'synonyms': ['person', 'baby', 'child', 'boy', 'girl', 'man', 'woman', 'human'], 'id': 793, 'def': 'a human being', 'name': 'person'}, {'frequency': 'c', 'synset': 'pet.n.01', 'synonyms': ['pet'], 'id': 794, 'def': 'a domesticated animal kept for companionship or amusement', 'name': 'pet'}, {'frequency': 'c', 'synset': 'pew.n.01', 'synonyms': ['pew_(church_bench)', 'church_bench'], 'id': 795, 'def': 'long bench with backs; used in church by the congregation', 'name': 'pew_(church_bench)'}, {'frequency': 'r', 'synset': 'phonebook.n.01', 'synonyms': ['phonebook', 'telephone_book', 'telephone_directory'], 'id': 796, 'def': 'a directory containing an alphabetical list of telephone subscribers and their telephone numbers', 'name': 'phonebook'}, {'frequency': 'c', 'synset': 'phonograph_record.n.01', 'synonyms': ['phonograph_record', 'phonograph_recording', 'record_(phonograph_recording)'], 'id': 797, 'def': 'sound recording consisting of a typically black disk with a continuous groove', 'name': 'phonograph_record'}, {'frequency': 'f', 'synset': 'piano.n.01', 'synonyms': ['piano'], 'id': 798, 'def': 'a keyboard instrument that is played by depressing keys that cause hammers to strike tuned strings and produce sounds', 'name': 'piano'}, {'frequency': 'f', 'synset': 'pickle.n.01', 'synonyms': ['pickle'], 'id': 799, 'def': 'vegetables (especially cucumbers) preserved in brine or vinegar', 'name': 'pickle'}, {'frequency': 'f', 'synset': 'pickup.n.01', 'synonyms': ['pickup_truck'], 'id': 800, 'def': 'a light truck with an open body and low sides and a tailboard', 'name': 'pickup_truck'}, {'frequency': 'c', 'synset': 'pie.n.01', 'synonyms': ['pie'], 'id': 801, 'def': 'dish baked in pastry-lined pan often with a pastry top', 'name': 'pie'}, {'frequency': 'c', 'synset': 'pigeon.n.01', 'synonyms': ['pigeon'], 'id': 802, 'def': 'wild and domesticated birds having a heavy body and short legs', 'name': 'pigeon'}, {'frequency': 'r', 'synset': 'piggy_bank.n.01', 'synonyms': ['piggy_bank', 'penny_bank'], 'id': 803, 'def': "a child's coin bank (often shaped like a pig)", 'name': 'piggy_bank'}, {'frequency': 'f', 'synset': 'pillow.n.01', 'synonyms': ['pillow'], 'id': 804, 'def': 'a cushion to support the head of a sleeping person', 'name': 'pillow'}, {'frequency': 'r', 'synset': 'pin.n.09', 'synonyms': ['pin_(non_jewelry)'], 'id': 805, 'def': 'a small slender (often pointed) piece of wood or metal used to support or fasten or attach things', 'name': 'pin_(non_jewelry)'}, {'frequency': 'f', 'synset': 'pineapple.n.02', 'synonyms': ['pineapple'], 'id': 806, 'def': 'large sweet fleshy tropical fruit with a tuft of stiff leaves', 'name': 'pineapple'}, {'frequency': 'c', 'synset': 'pinecone.n.01', 'synonyms': ['pinecone'], 'id': 807, 'def': 'the seed-producing cone of a pine tree', 'name': 'pinecone'}, {'frequency': 'r', 'synset': 'ping-pong_ball.n.01', 'synonyms': ['ping-pong_ball'], 'id': 808, 'def': 'light hollow ball used in playing table tennis', 'name': 'ping-pong_ball'}, {'frequency': 'r', 'synset': 'pinwheel.n.03', 'synonyms': ['pinwheel'], 'id': 809, 'def': 'a toy consisting of vanes of colored paper or plastic that is pinned to a stick and spins when it is pointed into the wind', 'name': 'pinwheel'}, {'frequency': 'r', 'synset': 'pipe.n.01', 'synonyms': ['tobacco_pipe'], 'id': 810, 'def': 'a tube with a small bowl at one end; used for smoking tobacco', 'name': 'tobacco_pipe'}, {'frequency': 'f', 'synset': 'pipe.n.02', 'synonyms': ['pipe', 'piping'], 'id': 811, 'def': 'a long tube made of metal or plastic that is used to carry water or oil or gas etc.', 'name': 'pipe'}, {'frequency': 'r', 'synset': 'pistol.n.01', 'synonyms': ['pistol', 'handgun'], 'id': 812, 'def': 'a firearm that is held and fired with one hand', 'name': 'pistol'}, {'frequency': 'c', 'synset': 'pita.n.01', 'synonyms': ['pita_(bread)', 'pocket_bread'], 'id': 813, 'def': 'usually small round bread that can open into a pocket for filling', 'name': 'pita_(bread)'}, {'frequency': 'f', 'synset': 'pitcher.n.02', 'synonyms': ['pitcher_(vessel_for_liquid)', 'ewer'], 'id': 814, 'def': 'an open vessel with a handle and a spout for pouring', 'name': 'pitcher_(vessel_for_liquid)'}, {'frequency': 'r', 'synset': 'pitchfork.n.01', 'synonyms': ['pitchfork'], 'id': 815, 'def': 'a long-handled hand tool with sharp widely spaced prongs for lifting and pitching hay', 'name': 'pitchfork'}, {'frequency': 'f', 'synset': 'pizza.n.01', 'synonyms': ['pizza'], 'id': 816, 'def': 'Italian open pie made of thin bread dough spread with a spiced mixture of e.g. tomato sauce and cheese', 'name': 'pizza'}, {'frequency': 'f', 'synset': 'place_mat.n.01', 'synonyms': ['place_mat'], 'id': 817, 'def': 'a mat placed on a table for an individual place setting', 'name': 'place_mat'}, {'frequency': 'f', 'synset': 'plate.n.04', 'synonyms': ['plate'], 'id': 818, 'def': 'dish on which food is served or from which food is eaten', 'name': 'plate'}, {'frequency': 'c', 'synset': 'platter.n.01', 'synonyms': ['platter'], 'id': 819, 'def': 'a large shallow dish used for serving food', 'name': 'platter'}, {'frequency': 'r', 'synset': 'playpen.n.01', 'synonyms': ['playpen'], 'id': 820, 'def': 'a portable enclosure in which babies may be left to play', 'name': 'playpen'}, {'frequency': 'c', 'synset': 'pliers.n.01', 'synonyms': ['pliers', 'plyers'], 'id': 821, 'def': 'a gripping hand tool with two hinged arms and (usually) serrated jaws', 'name': 'pliers'}, {'frequency': 'r', 'synset': 'plow.n.01', 'synonyms': ['plow_(farm_equipment)', 'plough_(farm_equipment)'], 'id': 822, 'def': 'a farm tool having one or more heavy blades to break the soil and cut a furrow prior to sowing', 'name': 'plow_(farm_equipment)'}, {'frequency': 'r', 'synset': 'plume.n.02', 'synonyms': ['plume'], 'id': 823, 'def': 'a feather or cluster of feathers worn as an ornament', 'name': 'plume'}, {'frequency': 'r', 'synset': 'pocket_watch.n.01', 'synonyms': ['pocket_watch'], 'id': 824, 'def': 'a watch that is carried in a small watch pocket', 'name': 'pocket_watch'}, {'frequency': 'c', 'synset': 'pocketknife.n.01', 'synonyms': ['pocketknife'], 'id': 825, 'def': 'a knife with a blade that folds into the handle; suitable for carrying in the pocket', 'name': 'pocketknife'}, {'frequency': 'c', 'synset': 'poker.n.01', 'synonyms': ['poker_(fire_stirring_tool)', 'stove_poker', 'fire_hook'], 'id': 826, 'def': 'fire iron consisting of a metal rod with a handle; used to stir a fire', 'name': 'poker_(fire_stirring_tool)'}, {'frequency': 'f', 'synset': 'pole.n.01', 'synonyms': ['pole', 'post'], 'id': 827, 'def': 'a long (usually round) rod of wood or metal or plastic', 'name': 'pole'}, {'frequency': 'f', 'synset': 'polo_shirt.n.01', 'synonyms': ['polo_shirt', 'sport_shirt'], 'id': 828, 'def': 'a shirt with short sleeves designed for comfort and casual wear', 'name': 'polo_shirt'}, {'frequency': 'r', 'synset': 'poncho.n.01', 'synonyms': ['poncho'], 'id': 829, 'def': 'a blanket-like cloak with a hole in the center for the head', 'name': 'poncho'}, {'frequency': 'c', 'synset': 'pony.n.05', 'synonyms': ['pony'], 'id': 830, 'def': 'any of various breeds of small gentle horses usually less than five feet high at the shoulder', 'name': 'pony'}, {'frequency': 'r', 'synset': 'pool_table.n.01', 'synonyms': ['pool_table', 'billiard_table', 'snooker_table'], 'id': 831, 'def': 'game equipment consisting of a heavy table on which pool is played', 'name': 'pool_table'}, {'frequency': 'f', 'synset': 'pop.n.02', 'synonyms': ['pop_(soda)', 'soda_(pop)', 'tonic', 'soft_drink'], 'id': 832, 'def': 'a sweet drink containing carbonated water and flavoring', 'name': 'pop_(soda)'}, {'frequency': 'c', 'synset': 'postbox.n.01', 'synonyms': ['postbox_(public)', 'mailbox_(public)'], 'id': 833, 'def': 'public box for deposit of mail', 'name': 'postbox_(public)'}, {'frequency': 'c', 'synset': 'postcard.n.01', 'synonyms': ['postcard', 'postal_card', 'mailing-card'], 'id': 834, 'def': 'a card for sending messages by post without an envelope', 'name': 'postcard'}, {'frequency': 'f', 'synset': 'poster.n.01', 'synonyms': ['poster', 'placard'], 'id': 835, 'def': 'a sign posted in a public place as an advertisement', 'name': 'poster'}, {'frequency': 'f', 'synset': 'pot.n.01', 'synonyms': ['pot'], 'id': 836, 'def': 'metal or earthenware cooking vessel that is usually round and deep; often has a handle and lid', 'name': 'pot'}, {'frequency': 'f', 'synset': 'pot.n.04', 'synonyms': ['flowerpot'], 'id': 837, 'def': 'a container in which plants are cultivated', 'name': 'flowerpot'}, {'frequency': 'f', 'synset': 'potato.n.01', 'synonyms': ['potato'], 'id': 838, 'def': 'an edible tuber native to South America', 'name': 'potato'}, {'frequency': 'c', 'synset': 'potholder.n.01', 'synonyms': ['potholder'], 'id': 839, 'def': 'an insulated pad for holding hot pots', 'name': 'potholder'}, {'frequency': 'c', 'synset': 'pottery.n.01', 'synonyms': ['pottery', 'clayware'], 'id': 840, 'def': 'ceramic ware made from clay and baked in a kiln', 'name': 'pottery'}, {'frequency': 'c', 'synset': 'pouch.n.01', 'synonyms': ['pouch'], 'id': 841, 'def': 'a small or medium size container for holding or carrying things', 'name': 'pouch'}, {'frequency': 'c', 'synset': 'power_shovel.n.01', 'synonyms': ['power_shovel', 'excavator', 'digger'], 'id': 842, 'def': 'a machine for excavating', 'name': 'power_shovel'}, {'frequency': 'c', 'synset': 'prawn.n.01', 'synonyms': ['prawn', 'shrimp'], 'id': 843, 'def': 'any of various edible decapod crustaceans', 'name': 'prawn'}, {'frequency': 'c', 'synset': 'pretzel.n.01', 'synonyms': ['pretzel'], 'id': 844, 'def': 'glazed and salted cracker typically in the shape of a loose knot', 'name': 'pretzel'}, {'frequency': 'f', 'synset': 'printer.n.03', 'synonyms': ['printer', 'printing_machine'], 'id': 845, 'def': 'a machine that prints', 'name': 'printer'}, {'frequency': 'c', 'synset': 'projectile.n.01', 'synonyms': ['projectile_(weapon)', 'missile'], 'id': 846, 'def': 'a weapon that is forcibly thrown or projected at a targets', 'name': 'projectile_(weapon)'}, {'frequency': 'c', 'synset': 'projector.n.02', 'synonyms': ['projector'], 'id': 847, 'def': 'an optical instrument that projects an enlarged image onto a screen', 'name': 'projector'}, {'frequency': 'f', 'synset': 'propeller.n.01', 'synonyms': ['propeller', 'propellor'], 'id': 848, 'def': 'a mechanical device that rotates to push against air or water', 'name': 'propeller'}, {'frequency': 'r', 'synset': 'prune.n.01', 'synonyms': ['prune'], 'id': 849, 'def': 'dried plum', 'name': 'prune'}, {'frequency': 'r', 'synset': 'pudding.n.01', 'synonyms': ['pudding'], 'id': 850, 'def': 'any of various soft thick unsweetened baked dishes', 'name': 'pudding'}, {'frequency': 'r', 'synset': 'puffer.n.02', 'synonyms': ['puffer_(fish)', 'pufferfish', 'blowfish', 'globefish'], 'id': 851, 'def': 'fishes whose elongated spiny body can inflate itself with water or air to form a globe', 'name': 'puffer_(fish)'}, {'frequency': 'r', 'synset': 'puffin.n.01', 'synonyms': ['puffin'], 'id': 852, 'def': 'seabirds having short necks and brightly colored compressed bills', 'name': 'puffin'}, {'frequency': 'r', 'synset': 'pug.n.01', 'synonyms': ['pug-dog'], 'id': 853, 'def': 'small compact smooth-coated breed of Asiatic origin having a tightly curled tail and broad flat wrinkled muzzle', 'name': 'pug-dog'}, {'frequency': 'c', 'synset': 'pumpkin.n.02', 'synonyms': ['pumpkin'], 'id': 854, 'def': 'usually large pulpy deep-yellow round fruit of the squash family maturing in late summer or early autumn', 'name': 'pumpkin'}, {'frequency': 'r', 'synset': 'punch.n.03', 'synonyms': ['puncher'], 'id': 855, 'def': 'a tool for making holes or indentations', 'name': 'puncher'}, {'frequency': 'r', 'synset': 'puppet.n.01', 'synonyms': ['puppet', 'marionette'], 'id': 856, 'def': 'a small figure of a person operated from above with strings by a puppeteer', 'name': 'puppet'}, {'frequency': 'c', 'synset': 'puppy.n.01', 'synonyms': ['puppy'], 'id': 857, 'def': 'a young dog', 'name': 'puppy'}, {'frequency': 'r', 'synset': 'quesadilla.n.01', 'synonyms': ['quesadilla'], 'id': 858, 'def': 'a tortilla that is filled with cheese and heated', 'name': 'quesadilla'}, {'frequency': 'r', 'synset': 'quiche.n.02', 'synonyms': ['quiche'], 'id': 859, 'def': 'a tart filled with rich unsweetened custard; often contains other ingredients (as cheese or ham or seafood or vegetables)', 'name': 'quiche'}, {'frequency': 'f', 'synset': 'quilt.n.01', 'synonyms': ['quilt', 'comforter'], 'id': 860, 'def': 'bedding made of two layers of cloth filled with stuffing and stitched together', 'name': 'quilt'}, {'frequency': 'c', 'synset': 'rabbit.n.01', 'synonyms': ['rabbit'], 'id': 861, 'def': 'any of various burrowing animals of the family Leporidae having long ears and short tails', 'name': 'rabbit'}, {'frequency': 'r', 'synset': 'racer.n.02', 'synonyms': ['race_car', 'racing_car'], 'id': 862, 'def': 'a fast car that competes in races', 'name': 'race_car'}, {'frequency': 'c', 'synset': 'racket.n.04', 'synonyms': ['racket', 'racquet'], 'id': 863, 'def': 'a sports implement used to strike a ball in various games', 'name': 'racket'}, {'frequency': 'r', 'synset': 'radar.n.01', 'synonyms': ['radar'], 'id': 864, 'def': 'measuring instrument in which the echo of a pulse of microwave radiation is used to detect and locate distant objects', 'name': 'radar'}, {'frequency': 'f', 'synset': 'radiator.n.03', 'synonyms': ['radiator'], 'id': 865, 'def': 'a mechanism consisting of a metal honeycomb through which hot fluids circulate', 'name': 'radiator'}, {'frequency': 'c', 'synset': 'radio_receiver.n.01', 'synonyms': ['radio_receiver', 'radio_set', 'radio', 'tuner_(radio)'], 'id': 866, 'def': 'an electronic receiver that detects and demodulates and amplifies transmitted radio signals', 'name': 'radio_receiver'}, {'frequency': 'c', 'synset': 'radish.n.03', 'synonyms': ['radish', 'daikon'], 'id': 867, 'def': 'pungent edible root of any of various cultivated radish plants', 'name': 'radish'}, {'frequency': 'c', 'synset': 'raft.n.01', 'synonyms': ['raft'], 'id': 868, 'def': 'a flat float (usually made of logs or planks) that can be used for transport or as a platform for swimmers', 'name': 'raft'}, {'frequency': 'r', 'synset': 'rag_doll.n.01', 'synonyms': ['rag_doll'], 'id': 869, 'def': 'a cloth doll that is stuffed and (usually) painted', 'name': 'rag_doll'}, {'frequency': 'c', 'synset': 'raincoat.n.01', 'synonyms': ['raincoat', 'waterproof_jacket'], 'id': 870, 'def': 'a water-resistant coat', 'name': 'raincoat'}, {'frequency': 'c', 'synset': 'ram.n.05', 'synonyms': ['ram_(animal)'], 'id': 871, 'def': 'uncastrated adult male sheep', 'name': 'ram_(animal)'}, {'frequency': 'c', 'synset': 'raspberry.n.02', 'synonyms': ['raspberry'], 'id': 872, 'def': 'red or black edible aggregate berries usually smaller than the related blackberries', 'name': 'raspberry'}, {'frequency': 'r', 'synset': 'rat.n.01', 'synonyms': ['rat'], 'id': 873, 'def': 'any of various long-tailed rodents similar to but larger than a mouse', 'name': 'rat'}, {'frequency': 'c', 'synset': 'razorblade.n.01', 'synonyms': ['razorblade'], 'id': 874, 'def': 'a blade that has very sharp edge', 'name': 'razorblade'}, {'frequency': 'c', 'synset': 'reamer.n.01', 'synonyms': ['reamer_(juicer)', 'juicer', 'juice_reamer'], 'id': 875, 'def': 'a squeezer with a conical ridged center that is used for squeezing juice from citrus fruit', 'name': 'reamer_(juicer)'}, {'frequency': 'f', 'synset': 'rearview_mirror.n.01', 'synonyms': ['rearview_mirror'], 'id': 876, 'def': 'vehicle mirror (side or rearview)', 'name': 'rearview_mirror'}, {'frequency': 'c', 'synset': 'receipt.n.02', 'synonyms': ['receipt'], 'id': 877, 'def': 'an acknowledgment (usually tangible) that payment has been made', 'name': 'receipt'}, {'frequency': 'c', 'synset': 'recliner.n.01', 'synonyms': ['recliner', 'reclining_chair', 'lounger_(chair)'], 'id': 878, 'def': 'an armchair whose back can be lowered and foot can be raised to allow the sitter to recline in it', 'name': 'recliner'}, {'frequency': 'c', 'synset': 'record_player.n.01', 'synonyms': ['record_player', 'phonograph_(record_player)', 'turntable'], 'id': 879, 'def': 'machine in which rotating records cause a stylus to vibrate and the vibrations are amplified acoustically or electronically', 'name': 'record_player'}, {'frequency': 'f', 'synset': 'reflector.n.01', 'synonyms': ['reflector'], 'id': 880, 'def': 'device that reflects light, radiation, etc.', 'name': 'reflector'}, {'frequency': 'f', 'synset': 'remote_control.n.01', 'synonyms': ['remote_control'], 'id': 881, 'def': 'a device that can be used to control a machine or apparatus from a distance', 'name': 'remote_control'}, {'frequency': 'c', 'synset': 'rhinoceros.n.01', 'synonyms': ['rhinoceros'], 'id': 882, 'def': 'massive powerful herbivorous odd-toed ungulate of southeast Asia and Africa having very thick skin and one or two horns on the snout', 'name': 'rhinoceros'}, {'frequency': 'r', 'synset': 'rib.n.03', 'synonyms': ['rib_(food)'], 'id': 883, 'def': 'cut of meat including one or more ribs', 'name': 'rib_(food)'}, {'frequency': 'c', 'synset': 'rifle.n.01', 'synonyms': ['rifle'], 'id': 884, 'def': 'a shoulder firearm with a long barrel', 'name': 'rifle'}, {'frequency': 'f', 'synset': 'ring.n.08', 'synonyms': ['ring'], 'id': 885, 'def': 'jewelry consisting of a circlet of precious metal (often set with jewels) worn on the finger', 'name': 'ring'}, {'frequency': 'r', 'synset': 'river_boat.n.01', 'synonyms': ['river_boat'], 'id': 886, 'def': 'a boat used on rivers or to ply a river', 'name': 'river_boat'}, {'frequency': 'r', 'synset': 'road_map.n.02', 'synonyms': ['road_map'], 'id': 887, 'def': '(NOT A ROAD) a MAP showing roads (for automobile travel)', 'name': 'road_map'}, {'frequency': 'c', 'synset': 'robe.n.01', 'synonyms': ['robe'], 'id': 888, 'def': 'any loose flowing garment', 'name': 'robe'}, {'frequency': 'c', 'synset': 'rocking_chair.n.01', 'synonyms': ['rocking_chair'], 'id': 889, 'def': 'a chair mounted on rockers', 'name': 'rocking_chair'}, {'frequency': 'r', 'synset': 'rodent.n.01', 'synonyms': ['rodent'], 'id': 890, 'def': 'relatively small placental mammals having a single pair of constantly growing incisor teeth specialized for gnawing', 'name': 'rodent'}, {'frequency': 'r', 'synset': 'roller_skate.n.01', 'synonyms': ['roller_skate'], 'id': 891, 'def': 'a shoe with pairs of rollers (small hard wheels) fixed to the sole', 'name': 'roller_skate'}, {'frequency': 'r', 'synset': 'rollerblade.n.01', 'synonyms': ['Rollerblade'], 'id': 892, 'def': 'an in-line variant of a roller skate', 'name': 'Rollerblade'}, {'frequency': 'c', 'synset': 'rolling_pin.n.01', 'synonyms': ['rolling_pin'], 'id': 893, 'def': 'utensil consisting of a cylinder (usually of wood) with a handle at each end; used to roll out dough', 'name': 'rolling_pin'}, {'frequency': 'r', 'synset': 'root_beer.n.01', 'synonyms': ['root_beer'], 'id': 894, 'def': 'carbonated drink containing extracts of roots and herbs', 'name': 'root_beer'}, {'frequency': 'c', 'synset': 'router.n.02', 'synonyms': ['router_(computer_equipment)'], 'id': 895, 'def': 'a device that forwards data packets between computer networks', 'name': 'router_(computer_equipment)'}, {'frequency': 'f', 'synset': 'rubber_band.n.01', 'synonyms': ['rubber_band', 'elastic_band'], 'id': 896, 'def': 'a narrow band of elastic rubber used to hold things (such as papers) together', 'name': 'rubber_band'}, {'frequency': 'c', 'synset': 'runner.n.08', 'synonyms': ['runner_(carpet)'], 'id': 897, 'def': 'a long narrow carpet', 'name': 'runner_(carpet)'}, {'frequency': 'f', 'synset': 'sack.n.01', 'synonyms': ['plastic_bag', 'paper_bag'], 'id': 898, 'def': "a bag made of paper or plastic for holding customer's purchases", 'name': 'plastic_bag'}, {'frequency': 'f', 'synset': 'saddle.n.01', 'synonyms': ['saddle_(on_an_animal)'], 'id': 899, 'def': 'a seat for the rider of a horse or camel', 'name': 'saddle_(on_an_animal)'}, {'frequency': 'f', 'synset': 'saddle_blanket.n.01', 'synonyms': ['saddle_blanket', 'saddlecloth', 'horse_blanket'], 'id': 900, 'def': 'stable gear consisting of a blanket placed under the saddle', 'name': 'saddle_blanket'}, {'frequency': 'c', 'synset': 'saddlebag.n.01', 'synonyms': ['saddlebag'], 'id': 901, 'def': 'a large bag (or pair of bags) hung over a saddle', 'name': 'saddlebag'}, {'frequency': 'r', 'synset': 'safety_pin.n.01', 'synonyms': ['safety_pin'], 'id': 902, 'def': 'a pin in the form of a clasp; has a guard so the point of the pin will not stick the user', 'name': 'safety_pin'}, {'frequency': 'f', 'synset': 'sail.n.01', 'synonyms': ['sail'], 'id': 903, 'def': 'a large piece of fabric by means of which wind is used to propel a sailing vessel', 'name': 'sail'}, {'frequency': 'f', 'synset': 'salad.n.01', 'synonyms': ['salad'], 'id': 904, 'def': 'food mixtures either arranged on a plate or tossed and served with a moist dressing; usually consisting of or including greens', 'name': 'salad'}, {'frequency': 'r', 'synset': 'salad_plate.n.01', 'synonyms': ['salad_plate', 'salad_bowl'], 'id': 905, 'def': 'a plate or bowl for individual servings of salad', 'name': 'salad_plate'}, {'frequency': 'c', 'synset': 'salami.n.01', 'synonyms': ['salami'], 'id': 906, 'def': 'highly seasoned fatty sausage of pork and beef usually dried', 'name': 'salami'}, {'frequency': 'c', 'synset': 'salmon.n.01', 'synonyms': ['salmon_(fish)'], 'id': 907, 'def': 'any of various large food and game fishes of northern waters', 'name': 'salmon_(fish)'}, {'frequency': 'r', 'synset': 'salmon.n.03', 'synonyms': ['salmon_(food)'], 'id': 908, 'def': 'flesh of any of various marine or freshwater fish of the family Salmonidae', 'name': 'salmon_(food)'}, {'frequency': 'c', 'synset': 'salsa.n.01', 'synonyms': ['salsa'], 'id': 909, 'def': 'spicy sauce of tomatoes and onions and chili peppers to accompany Mexican foods', 'name': 'salsa'}, {'frequency': 'f', 'synset': 'saltshaker.n.01', 'synonyms': ['saltshaker'], 'id': 910, 'def': 'a shaker with a perforated top for sprinkling salt', 'name': 'saltshaker'}, {'frequency': 'f', 'synset': 'sandal.n.01', 'synonyms': ['sandal_(type_of_shoe)'], 'id': 911, 'def': 'a shoe consisting of a sole fastened by straps to the foot', 'name': 'sandal_(type_of_shoe)'}, {'frequency': 'f', 'synset': 'sandwich.n.01', 'synonyms': ['sandwich'], 'id': 912, 'def': 'two (or more) slices of bread with a filling between them', 'name': 'sandwich'}, {'frequency': 'r', 'synset': 'satchel.n.01', 'synonyms': ['satchel'], 'id': 913, 'def': 'luggage consisting of a small case with a flat bottom and (usually) a shoulder strap', 'name': 'satchel'}, {'frequency': 'r', 'synset': 'saucepan.n.01', 'synonyms': ['saucepan'], 'id': 914, 'def': 'a deep pan with a handle; used for stewing or boiling', 'name': 'saucepan'}, {'frequency': 'f', 'synset': 'saucer.n.02', 'synonyms': ['saucer'], 'id': 915, 'def': 'a small shallow dish for holding a cup at the table', 'name': 'saucer'}, {'frequency': 'f', 'synset': 'sausage.n.01', 'synonyms': ['sausage'], 'id': 916, 'def': 'highly seasoned minced meat stuffed in casings', 'name': 'sausage'}, {'frequency': 'r', 'synset': 'sawhorse.n.01', 'synonyms': ['sawhorse', 'sawbuck'], 'id': 917, 'def': 'a framework for holding wood that is being sawed', 'name': 'sawhorse'}, {'frequency': 'r', 'synset': 'sax.n.02', 'synonyms': ['saxophone'], 'id': 918, 'def': "a wind instrument with a `J'-shaped form typically made of brass", 'name': 'saxophone'}, {'frequency': 'f', 'synset': 'scale.n.07', 'synonyms': ['scale_(measuring_instrument)'], 'id': 919, 'def': 'a measuring instrument for weighing; shows amount of mass', 'name': 'scale_(measuring_instrument)'}, {'frequency': 'r', 'synset': 'scarecrow.n.01', 'synonyms': ['scarecrow', 'strawman'], 'id': 920, 'def': 'an effigy in the shape of a man to frighten birds away from seeds', 'name': 'scarecrow'}, {'frequency': 'f', 'synset': 'scarf.n.01', 'synonyms': ['scarf'], 'id': 921, 'def': 'a garment worn around the head or neck or shoulders for warmth or decoration', 'name': 'scarf'}, {'frequency': 'c', 'synset': 'school_bus.n.01', 'synonyms': ['school_bus'], 'id': 922, 'def': 'a bus used to transport children to or from school', 'name': 'school_bus'}, {'frequency': 'f', 'synset': 'scissors.n.01', 'synonyms': ['scissors'], 'id': 923, 'def': 'a tool having two crossed pivoting blades with looped handles', 'name': 'scissors'}, {'frequency': 'f', 'synset': 'scoreboard.n.01', 'synonyms': ['scoreboard'], 'id': 924, 'def': 'a large board for displaying the score of a contest (and some other information)', 'name': 'scoreboard'}, {'frequency': 'r', 'synset': 'scraper.n.01', 'synonyms': ['scraper'], 'id': 925, 'def': 'any of various hand tools for scraping', 'name': 'scraper'}, {'frequency': 'c', 'synset': 'screwdriver.n.01', 'synonyms': ['screwdriver'], 'id': 926, 'def': 'a hand tool for driving screws; has a tip that fits into the head of a screw', 'name': 'screwdriver'}, {'frequency': 'f', 'synset': 'scrub_brush.n.01', 'synonyms': ['scrubbing_brush'], 'id': 927, 'def': 'a brush with short stiff bristles for heavy cleaning', 'name': 'scrubbing_brush'}, {'frequency': 'c', 'synset': 'sculpture.n.01', 'synonyms': ['sculpture'], 'id': 928, 'def': 'a three-dimensional work of art', 'name': 'sculpture'}, {'frequency': 'c', 'synset': 'seabird.n.01', 'synonyms': ['seabird', 'seafowl'], 'id': 929, 'def': 'a bird that frequents coastal waters and the open ocean: gulls; pelicans; gannets; cormorants; albatrosses; petrels; etc.', 'name': 'seabird'}, {'frequency': 'c', 'synset': 'seahorse.n.02', 'synonyms': ['seahorse'], 'id': 930, 'def': 'small fish with horse-like heads bent sharply downward and curled tails', 'name': 'seahorse'}, {'frequency': 'r', 'synset': 'seaplane.n.01', 'synonyms': ['seaplane', 'hydroplane'], 'id': 931, 'def': 'an airplane that can land on or take off from water', 'name': 'seaplane'}, {'frequency': 'c', 'synset': 'seashell.n.01', 'synonyms': ['seashell'], 'id': 932, 'def': 'the shell of a marine organism', 'name': 'seashell'}, {'frequency': 'c', 'synset': 'sewing_machine.n.01', 'synonyms': ['sewing_machine'], 'id': 933, 'def': 'a textile machine used as a home appliance for sewing', 'name': 'sewing_machine'}, {'frequency': 'c', 'synset': 'shaker.n.03', 'synonyms': ['shaker'], 'id': 934, 'def': 'a container in which something can be shaken', 'name': 'shaker'}, {'frequency': 'c', 'synset': 'shampoo.n.01', 'synonyms': ['shampoo'], 'id': 935, 'def': 'cleansing agent consisting of soaps or detergents used for washing the hair', 'name': 'shampoo'}, {'frequency': 'c', 'synset': 'shark.n.01', 'synonyms': ['shark'], 'id': 936, 'def': 'typically large carnivorous fishes with sharpe teeth', 'name': 'shark'}, {'frequency': 'r', 'synset': 'sharpener.n.01', 'synonyms': ['sharpener'], 'id': 937, 'def': 'any implement that is used to make something (an edge or a point) sharper', 'name': 'sharpener'}, {'frequency': 'r', 'synset': 'sharpie.n.03', 'synonyms': ['Sharpie'], 'id': 938, 'def': 'a pen with indelible ink that will write on any surface', 'name': 'Sharpie'}, {'frequency': 'r', 'synset': 'shaver.n.03', 'synonyms': ['shaver_(electric)', 'electric_shaver', 'electric_razor'], 'id': 939, 'def': 'a razor powered by an electric motor', 'name': 'shaver_(electric)'}, {'frequency': 'c', 'synset': 'shaving_cream.n.01', 'synonyms': ['shaving_cream', 'shaving_soap'], 'id': 940, 'def': 'toiletry consisting that forms a rich lather for softening the beard before shaving', 'name': 'shaving_cream'}, {'frequency': 'r', 'synset': 'shawl.n.01', 'synonyms': ['shawl'], 'id': 941, 'def': 'cloak consisting of an oblong piece of cloth used to cover the head and shoulders', 'name': 'shawl'}, {'frequency': 'r', 'synset': 'shears.n.01', 'synonyms': ['shears'], 'id': 942, 'def': 'large scissors with strong blades', 'name': 'shears'}, {'frequency': 'f', 'synset': 'sheep.n.01', 'synonyms': ['sheep'], 'id': 943, 'def': 'woolly usually horned ruminant mammal related to the goat', 'name': 'sheep'}, {'frequency': 'r', 'synset': 'shepherd_dog.n.01', 'synonyms': ['shepherd_dog', 'sheepdog'], 'id': 944, 'def': 'any of various usually long-haired breeds of dog reared to herd and guard sheep', 'name': 'shepherd_dog'}, {'frequency': 'r', 'synset': 'sherbert.n.01', 'synonyms': ['sherbert', 'sherbet'], 'id': 945, 'def': 'a frozen dessert made primarily of fruit juice and sugar', 'name': 'sherbert'}, {'frequency': 'c', 'synset': 'shield.n.02', 'synonyms': ['shield'], 'id': 946, 'def': 'armor carried on the arm to intercept blows', 'name': 'shield'}, {'frequency': 'f', 'synset': 'shirt.n.01', 'synonyms': ['shirt'], 'id': 947, 'def': 'a garment worn on the upper half of the body', 'name': 'shirt'}, {'frequency': 'f', 'synset': 'shoe.n.01', 'synonyms': ['shoe', 'sneaker_(type_of_shoe)', 'tennis_shoe'], 'id': 948, 'def': 'common footwear covering the foot', 'name': 'shoe'}, {'frequency': 'f', 'synset': 'shopping_bag.n.01', 'synonyms': ['shopping_bag'], 'id': 949, 'def': 'a bag made of plastic or strong paper (often with handles); used to transport goods after shopping', 'name': 'shopping_bag'}, {'frequency': 'c', 'synset': 'shopping_cart.n.01', 'synonyms': ['shopping_cart'], 'id': 950, 'def': 'a handcart that holds groceries or other goods while shopping', 'name': 'shopping_cart'}, {'frequency': 'f', 'synset': 'short_pants.n.01', 'synonyms': ['short_pants', 'shorts_(clothing)', 'trunks_(clothing)'], 'id': 951, 'def': 'trousers that end at or above the knee', 'name': 'short_pants'}, {'frequency': 'r', 'synset': 'shot_glass.n.01', 'synonyms': ['shot_glass'], 'id': 952, 'def': 'a small glass adequate to hold a single swallow of whiskey', 'name': 'shot_glass'}, {'frequency': 'f', 'synset': 'shoulder_bag.n.01', 'synonyms': ['shoulder_bag'], 'id': 953, 'def': 'a large handbag that can be carried by a strap looped over the shoulder', 'name': 'shoulder_bag'}, {'frequency': 'c', 'synset': 'shovel.n.01', 'synonyms': ['shovel'], 'id': 954, 'def': 'a hand tool for lifting loose material such as snow, dirt, etc.', 'name': 'shovel'}, {'frequency': 'f', 'synset': 'shower.n.01', 'synonyms': ['shower_head'], 'id': 955, 'def': 'a plumbing fixture that sprays water over you', 'name': 'shower_head'}, {'frequency': 'r', 'synset': 'shower_cap.n.01', 'synonyms': ['shower_cap'], 'id': 956, 'def': 'a tight cap worn to keep hair dry while showering', 'name': 'shower_cap'}, {'frequency': 'f', 'synset': 'shower_curtain.n.01', 'synonyms': ['shower_curtain'], 'id': 957, 'def': 'a curtain that keeps water from splashing out of the shower area', 'name': 'shower_curtain'}, {'frequency': 'r', 'synset': 'shredder.n.01', 'synonyms': ['shredder_(for_paper)'], 'id': 958, 'def': 'a device that shreds documents', 'name': 'shredder_(for_paper)'}, {'frequency': 'f', 'synset': 'signboard.n.01', 'synonyms': ['signboard'], 'id': 959, 'def': 'structure displaying a board on which advertisements can be posted', 'name': 'signboard'}, {'frequency': 'c', 'synset': 'silo.n.01', 'synonyms': ['silo'], 'id': 960, 'def': 'a cylindrical tower used for storing goods', 'name': 'silo'}, {'frequency': 'f', 'synset': 'sink.n.01', 'synonyms': ['sink'], 'id': 961, 'def': 'plumbing fixture consisting of a water basin fixed to a wall or floor and having a drainpipe', 'name': 'sink'}, {'frequency': 'f', 'synset': 'skateboard.n.01', 'synonyms': ['skateboard'], 'id': 962, 'def': 'a board with wheels that is ridden in a standing or crouching position and propelled by foot', 'name': 'skateboard'}, {'frequency': 'c', 'synset': 'skewer.n.01', 'synonyms': ['skewer'], 'id': 963, 'def': 'a long pin for holding meat in position while it is being roasted', 'name': 'skewer'}, {'frequency': 'f', 'synset': 'ski.n.01', 'synonyms': ['ski'], 'id': 964, 'def': 'sports equipment for skiing on snow', 'name': 'ski'}, {'frequency': 'f', 'synset': 'ski_boot.n.01', 'synonyms': ['ski_boot'], 'id': 965, 'def': 'a stiff boot that is fastened to a ski with a ski binding', 'name': 'ski_boot'}, {'frequency': 'f', 'synset': 'ski_parka.n.01', 'synonyms': ['ski_parka', 'ski_jacket'], 'id': 966, 'def': 'a parka to be worn while skiing', 'name': 'ski_parka'}, {'frequency': 'f', 'synset': 'ski_pole.n.01', 'synonyms': ['ski_pole'], 'id': 967, 'def': 'a pole with metal points used as an aid in skiing', 'name': 'ski_pole'}, {'frequency': 'f', 'synset': 'skirt.n.02', 'synonyms': ['skirt'], 'id': 968, 'def': 'a garment hanging from the waist; worn mainly by girls and women', 'name': 'skirt'}, {'frequency': 'r', 'synset': 'skullcap.n.01', 'synonyms': ['skullcap'], 'id': 969, 'def': 'rounded brimless cap fitting the crown of the head', 'name': 'skullcap'}, {'frequency': 'c', 'synset': 'sled.n.01', 'synonyms': ['sled', 'sledge', 'sleigh'], 'id': 970, 'def': 'a vehicle or flat object for transportation over snow by sliding or pulled by dogs, etc.', 'name': 'sled'}, {'frequency': 'c', 'synset': 'sleeping_bag.n.01', 'synonyms': ['sleeping_bag'], 'id': 971, 'def': 'large padded bag designed to be slept in outdoors', 'name': 'sleeping_bag'}, {'frequency': 'r', 'synset': 'sling.n.05', 'synonyms': ['sling_(bandage)', 'triangular_bandage'], 'id': 972, 'def': 'bandage to support an injured forearm; slung over the shoulder or neck', 'name': 'sling_(bandage)'}, {'frequency': 'c', 'synset': 'slipper.n.01', 'synonyms': ['slipper_(footwear)', 'carpet_slipper_(footwear)'], 'id': 973, 'def': 'low footwear that can be slipped on and off easily; usually worn indoors', 'name': 'slipper_(footwear)'}, {'frequency': 'r', 'synset': 'smoothie.n.02', 'synonyms': ['smoothie'], 'id': 974, 'def': 'a thick smooth drink consisting of fresh fruit pureed with ice cream or yoghurt or milk', 'name': 'smoothie'}, {'frequency': 'r', 'synset': 'snake.n.01', 'synonyms': ['snake', 'serpent'], 'id': 975, 'def': 'limbless scaly elongate reptile; some are venomous', 'name': 'snake'}, {'frequency': 'f', 'synset': 'snowboard.n.01', 'synonyms': ['snowboard'], 'id': 976, 'def': 'a board that resembles a broad ski or a small surfboard; used in a standing position to slide down snow-covered slopes', 'name': 'snowboard'}, {'frequency': 'c', 'synset': 'snowman.n.01', 'synonyms': ['snowman'], 'id': 977, 'def': 'a figure of a person made of packed snow', 'name': 'snowman'}, {'frequency': 'c', 'synset': 'snowmobile.n.01', 'synonyms': ['snowmobile'], 'id': 978, 'def': 'tracked vehicle for travel on snow having skis in front', 'name': 'snowmobile'}, {'frequency': 'f', 'synset': 'soap.n.01', 'synonyms': ['soap'], 'id': 979, 'def': 'a cleansing agent made from the salts of vegetable or animal fats', 'name': 'soap'}, {'frequency': 'f', 'synset': 'soccer_ball.n.01', 'synonyms': ['soccer_ball'], 'id': 980, 'def': "an inflated ball used in playing soccer (called `football' outside of the United States)", 'name': 'soccer_ball'}, {'frequency': 'f', 'synset': 'sock.n.01', 'synonyms': ['sock'], 'id': 981, 'def': 'cloth covering for the foot; worn inside the shoe; reaches to between the ankle and the knee', 'name': 'sock'}, {'frequency': 'f', 'synset': 'sofa.n.01', 'synonyms': ['sofa', 'couch', 'lounge'], 'id': 982, 'def': 'an upholstered seat for more than one person', 'name': 'sofa'}, {'frequency': 'r', 'synset': 'softball.n.01', 'synonyms': ['softball'], 'id': 983, 'def': 'ball used in playing softball', 'name': 'softball'}, {'frequency': 'c', 'synset': 'solar_array.n.01', 'synonyms': ['solar_array', 'solar_battery', 'solar_panel'], 'id': 984, 'def': 'electrical device consisting of a large array of connected solar cells', 'name': 'solar_array'}, {'frequency': 'r', 'synset': 'sombrero.n.02', 'synonyms': ['sombrero'], 'id': 985, 'def': 'a straw hat with a tall crown and broad brim; worn in American southwest and in Mexico', 'name': 'sombrero'}, {'frequency': 'f', 'synset': 'soup.n.01', 'synonyms': ['soup'], 'id': 986, 'def': 'liquid food especially of meat or fish or vegetable stock often containing pieces of solid food', 'name': 'soup'}, {'frequency': 'r', 'synset': 'soup_bowl.n.01', 'synonyms': ['soup_bowl'], 'id': 987, 'def': 'a bowl for serving soup', 'name': 'soup_bowl'}, {'frequency': 'c', 'synset': 'soupspoon.n.01', 'synonyms': ['soupspoon'], 'id': 988, 'def': 'a spoon with a rounded bowl for eating soup', 'name': 'soupspoon'}, {'frequency': 'c', 'synset': 'sour_cream.n.01', 'synonyms': ['sour_cream', 'soured_cream'], 'id': 989, 'def': 'soured light cream', 'name': 'sour_cream'}, {'frequency': 'r', 'synset': 'soya_milk.n.01', 'synonyms': ['soya_milk', 'soybean_milk', 'soymilk'], 'id': 990, 'def': 'a milk substitute containing soybean flour and water; used in some infant formulas and in making tofu', 'name': 'soya_milk'}, {'frequency': 'r', 'synset': 'space_shuttle.n.01', 'synonyms': ['space_shuttle'], 'id': 991, 'def': "a reusable spacecraft with wings for a controlled descent through the Earth's atmosphere", 'name': 'space_shuttle'}, {'frequency': 'r', 'synset': 'sparkler.n.02', 'synonyms': ['sparkler_(fireworks)'], 'id': 992, 'def': 'a firework that burns slowly and throws out a shower of sparks', 'name': 'sparkler_(fireworks)'}, {'frequency': 'f', 'synset': 'spatula.n.02', 'synonyms': ['spatula'], 'id': 993, 'def': 'a hand tool with a thin flexible blade used to mix or spread soft substances', 'name': 'spatula'}, {'frequency': 'r', 'synset': 'spear.n.01', 'synonyms': ['spear', 'lance'], 'id': 994, 'def': 'a long pointed rod used as a tool or weapon', 'name': 'spear'}, {'frequency': 'f', 'synset': 'spectacles.n.01', 'synonyms': ['spectacles', 'specs', 'eyeglasses', 'glasses'], 'id': 995, 'def': 'optical instrument consisting of a frame that holds a pair of lenses for correcting defective vision', 'name': 'spectacles'}, {'frequency': 'c', 'synset': 'spice_rack.n.01', 'synonyms': ['spice_rack'], 'id': 996, 'def': 'a rack for displaying containers filled with spices', 'name': 'spice_rack'}, {'frequency': 'c', 'synset': 'spider.n.01', 'synonyms': ['spider'], 'id': 997, 'def': 'predatory arachnid with eight legs, two poison fangs, two feelers, and usually two silk-spinning organs at the back end of the body', 'name': 'spider'}, {'frequency': 'r', 'synset': 'spiny_lobster.n.02', 'synonyms': ['crawfish', 'crayfish'], 'id': 998, 'def': 'large edible marine crustacean having a spiny carapace but lacking the large pincers of true lobsters', 'name': 'crawfish'}, {'frequency': 'c', 'synset': 'sponge.n.01', 'synonyms': ['sponge'], 'id': 999, 'def': 'a porous mass usable to absorb water typically used for cleaning', 'name': 'sponge'}, {'frequency': 'f', 'synset': 'spoon.n.01', 'synonyms': ['spoon'], 'id': 1000, 'def': 'a piece of cutlery with a shallow bowl-shaped container and a handle', 'name': 'spoon'}, {'frequency': 'c', 'synset': 'sportswear.n.01', 'synonyms': ['sportswear', 'athletic_wear', 'activewear'], 'id': 1001, 'def': 'attire worn for sport or for casual wear', 'name': 'sportswear'}, {'frequency': 'c', 'synset': 'spotlight.n.02', 'synonyms': ['spotlight'], 'id': 1002, 'def': 'a lamp that produces a strong beam of light to illuminate a restricted area; used to focus attention of a stage performer', 'name': 'spotlight'}, {'frequency': 'r', 'synset': 'squid.n.01', 'synonyms': ['squid_(food)', 'calamari', 'calamary'], 'id': 1003, 'def': '(Italian cuisine) squid prepared as food', 'name': 'squid_(food)'}, {'frequency': 'c', 'synset': 'squirrel.n.01', 'synonyms': ['squirrel'], 'id': 1004, 'def': 'a kind of arboreal rodent having a long bushy tail', 'name': 'squirrel'}, {'frequency': 'r', 'synset': 'stagecoach.n.01', 'synonyms': ['stagecoach'], 'id': 1005, 'def': 'a large coach-and-four formerly used to carry passengers and mail on regular routes between towns', 'name': 'stagecoach'}, {'frequency': 'c', 'synset': 'stapler.n.01', 'synonyms': ['stapler_(stapling_machine)'], 'id': 1006, 'def': 'a machine that inserts staples into sheets of paper in order to fasten them together', 'name': 'stapler_(stapling_machine)'}, {'frequency': 'c', 'synset': 'starfish.n.01', 'synonyms': ['starfish', 'sea_star'], 'id': 1007, 'def': 'echinoderms characterized by five arms extending from a central disk', 'name': 'starfish'}, {'frequency': 'f', 'synset': 'statue.n.01', 'synonyms': ['statue_(sculpture)'], 'id': 1008, 'def': 'a sculpture representing a human or animal', 'name': 'statue_(sculpture)'}, {'frequency': 'c', 'synset': 'steak.n.01', 'synonyms': ['steak_(food)'], 'id': 1009, 'def': 'a slice of meat cut from the fleshy part of an animal or large fish', 'name': 'steak_(food)'}, {'frequency': 'r', 'synset': 'steak_knife.n.01', 'synonyms': ['steak_knife'], 'id': 1010, 'def': 'a sharp table knife used in eating steak', 'name': 'steak_knife'}, {'frequency': 'f', 'synset': 'steering_wheel.n.01', 'synonyms': ['steering_wheel'], 'id': 1011, 'def': 'a handwheel that is used for steering', 'name': 'steering_wheel'}, {'frequency': 'r', 'synset': 'step_ladder.n.01', 'synonyms': ['stepladder'], 'id': 1012, 'def': 'a folding portable ladder hinged at the top', 'name': 'stepladder'}, {'frequency': 'c', 'synset': 'step_stool.n.01', 'synonyms': ['step_stool'], 'id': 1013, 'def': 'a stool that has one or two steps that fold under the seat', 'name': 'step_stool'}, {'frequency': 'c', 'synset': 'stereo.n.01', 'synonyms': ['stereo_(sound_system)'], 'id': 1014, 'def': 'electronic device for playing audio', 'name': 'stereo_(sound_system)'}, {'frequency': 'r', 'synset': 'stew.n.02', 'synonyms': ['stew'], 'id': 1015, 'def': 'food prepared by stewing especially meat or fish with vegetables', 'name': 'stew'}, {'frequency': 'r', 'synset': 'stirrer.n.02', 'synonyms': ['stirrer'], 'id': 1016, 'def': 'an implement used for stirring', 'name': 'stirrer'}, {'frequency': 'f', 'synset': 'stirrup.n.01', 'synonyms': ['stirrup'], 'id': 1017, 'def': "support consisting of metal loops into which rider's feet go", 'name': 'stirrup'}, {'frequency': 'f', 'synset': 'stool.n.01', 'synonyms': ['stool'], 'id': 1018, 'def': 'a simple seat without a back or arms', 'name': 'stool'}, {'frequency': 'f', 'synset': 'stop_sign.n.01', 'synonyms': ['stop_sign'], 'id': 1019, 'def': 'a traffic sign to notify drivers that they must come to a complete stop', 'name': 'stop_sign'}, {'frequency': 'f', 'synset': 'stoplight.n.01', 'synonyms': ['brake_light'], 'id': 1020, 'def': 'a red light on the rear of a motor vehicle that signals when the brakes are applied', 'name': 'brake_light'}, {'frequency': 'f', 'synset': 'stove.n.01', 'synonyms': ['stove', 'kitchen_stove', 'range_(kitchen_appliance)', 'kitchen_range', 'cooking_stove'], 'id': 1021, 'def': 'a kitchen appliance used for cooking food', 'name': 'stove'}, {'frequency': 'c', 'synset': 'strainer.n.01', 'synonyms': ['strainer'], 'id': 1022, 'def': 'a filter to retain larger pieces while smaller pieces and liquids pass through', 'name': 'strainer'}, {'frequency': 'f', 'synset': 'strap.n.01', 'synonyms': ['strap'], 'id': 1023, 'def': 'an elongated strip of material for binding things together or holding', 'name': 'strap'}, {'frequency': 'f', 'synset': 'straw.n.04', 'synonyms': ['straw_(for_drinking)', 'drinking_straw'], 'id': 1024, 'def': 'a thin paper or plastic tube used to suck liquids into the mouth', 'name': 'straw_(for_drinking)'}, {'frequency': 'f', 'synset': 'strawberry.n.01', 'synonyms': ['strawberry'], 'id': 1025, 'def': 'sweet fleshy red fruit', 'name': 'strawberry'}, {'frequency': 'f', 'synset': 'street_sign.n.01', 'synonyms': ['street_sign'], 'id': 1026, 'def': 'a sign visible from the street', 'name': 'street_sign'}, {'frequency': 'f', 'synset': 'streetlight.n.01', 'synonyms': ['streetlight', 'street_lamp'], 'id': 1027, 'def': 'a lamp supported on a lamppost; for illuminating a street', 'name': 'streetlight'}, {'frequency': 'r', 'synset': 'string_cheese.n.01', 'synonyms': ['string_cheese'], 'id': 1028, 'def': 'cheese formed in long strings twisted together', 'name': 'string_cheese'}, {'frequency': 'r', 'synset': 'stylus.n.02', 'synonyms': ['stylus'], 'id': 1029, 'def': 'a pointed tool for writing or drawing or engraving, including pens', 'name': 'stylus'}, {'frequency': 'r', 'synset': 'subwoofer.n.01', 'synonyms': ['subwoofer'], 'id': 1030, 'def': 'a loudspeaker that is designed to reproduce very low bass frequencies', 'name': 'subwoofer'}, {'frequency': 'r', 'synset': 'sugar_bowl.n.01', 'synonyms': ['sugar_bowl'], 'id': 1031, 'def': 'a dish in which sugar is served', 'name': 'sugar_bowl'}, {'frequency': 'r', 'synset': 'sugarcane.n.01', 'synonyms': ['sugarcane_(plant)'], 'id': 1032, 'def': 'juicy canes whose sap is a source of molasses and commercial sugar; fresh canes are sometimes chewed for the juice', 'name': 'sugarcane_(plant)'}, {'frequency': 'f', 'synset': 'suit.n.01', 'synonyms': ['suit_(clothing)'], 'id': 1033, 'def': 'a set of garments (usually including a jacket and trousers or skirt) for outerwear all of the same fabric and color', 'name': 'suit_(clothing)'}, {'frequency': 'c', 'synset': 'sunflower.n.01', 'synonyms': ['sunflower'], 'id': 1034, 'def': 'any plant of the genus Helianthus having large flower heads with dark disk florets and showy yellow rays', 'name': 'sunflower'}, {'frequency': 'f', 'synset': 'sunglasses.n.01', 'synonyms': ['sunglasses'], 'id': 1035, 'def': 'spectacles that are darkened or polarized to protect the eyes from the glare of the sun', 'name': 'sunglasses'}, {'frequency': 'c', 'synset': 'sunhat.n.01', 'synonyms': ['sunhat'], 'id': 1036, 'def': 'a hat with a broad brim that protects the face from direct exposure to the sun', 'name': 'sunhat'}, {'frequency': 'f', 'synset': 'surfboard.n.01', 'synonyms': ['surfboard'], 'id': 1037, 'def': 'a narrow buoyant board for riding surf', 'name': 'surfboard'}, {'frequency': 'c', 'synset': 'sushi.n.01', 'synonyms': ['sushi'], 'id': 1038, 'def': 'rice (with raw fish) wrapped in seaweed', 'name': 'sushi'}, {'frequency': 'c', 'synset': 'swab.n.02', 'synonyms': ['mop'], 'id': 1039, 'def': 'cleaning implement consisting of absorbent material fastened to a handle; for cleaning floors', 'name': 'mop'}, {'frequency': 'c', 'synset': 'sweat_pants.n.01', 'synonyms': ['sweat_pants'], 'id': 1040, 'def': 'loose-fitting trousers with elastic cuffs; worn by athletes', 'name': 'sweat_pants'}, {'frequency': 'c', 'synset': 'sweatband.n.02', 'synonyms': ['sweatband'], 'id': 1041, 'def': 'a band of material tied around the forehead or wrist to absorb sweat', 'name': 'sweatband'}, {'frequency': 'f', 'synset': 'sweater.n.01', 'synonyms': ['sweater'], 'id': 1042, 'def': 'a crocheted or knitted garment covering the upper part of the body', 'name': 'sweater'}, {'frequency': 'f', 'synset': 'sweatshirt.n.01', 'synonyms': ['sweatshirt'], 'id': 1043, 'def': 'cotton knit pullover with long sleeves worn during athletic activity', 'name': 'sweatshirt'}, {'frequency': 'c', 'synset': 'sweet_potato.n.02', 'synonyms': ['sweet_potato'], 'id': 1044, 'def': 'the edible tuberous root of the sweet potato vine', 'name': 'sweet_potato'}, {'frequency': 'f', 'synset': 'swimsuit.n.01', 'synonyms': ['swimsuit', 'swimwear', 'bathing_suit', 'swimming_costume', 'bathing_costume', 'swimming_trunks', 'bathing_trunks'], 'id': 1045, 'def': 'garment worn for swimming', 'name': 'swimsuit'}, {'frequency': 'c', 'synset': 'sword.n.01', 'synonyms': ['sword'], 'id': 1046, 'def': 'a cutting or thrusting weapon that has a long metal blade', 'name': 'sword'}, {'frequency': 'r', 'synset': 'syringe.n.01', 'synonyms': ['syringe'], 'id': 1047, 'def': 'a medical instrument used to inject or withdraw fluids', 'name': 'syringe'}, {'frequency': 'r', 'synset': 'tabasco.n.02', 'synonyms': ['Tabasco_sauce'], 'id': 1048, 'def': 'very spicy sauce (trade name Tabasco) made from fully-aged red peppers', 'name': 'Tabasco_sauce'}, {'frequency': 'r', 'synset': 'table-tennis_table.n.01', 'synonyms': ['table-tennis_table', 'ping-pong_table'], 'id': 1049, 'def': 'a table used for playing table tennis', 'name': 'table-tennis_table'}, {'frequency': 'f', 'synset': 'table.n.02', 'synonyms': ['table'], 'id': 1050, 'def': 'a piece of furniture having a smooth flat top that is usually supported by one or more vertical legs', 'name': 'table'}, {'frequency': 'c', 'synset': 'table_lamp.n.01', 'synonyms': ['table_lamp'], 'id': 1051, 'def': 'a lamp that sits on a table', 'name': 'table_lamp'}, {'frequency': 'f', 'synset': 'tablecloth.n.01', 'synonyms': ['tablecloth'], 'id': 1052, 'def': 'a covering spread over a dining table', 'name': 'tablecloth'}, {'frequency': 'r', 'synset': 'tachometer.n.01', 'synonyms': ['tachometer'], 'id': 1053, 'def': 'measuring instrument for indicating speed of rotation', 'name': 'tachometer'}, {'frequency': 'r', 'synset': 'taco.n.02', 'synonyms': ['taco'], 'id': 1054, 'def': 'a small tortilla cupped around a filling', 'name': 'taco'}, {'frequency': 'f', 'synset': 'tag.n.02', 'synonyms': ['tag'], 'id': 1055, 'def': 'a label associated with something for the purpose of identification or information', 'name': 'tag'}, {'frequency': 'f', 'synset': 'taillight.n.01', 'synonyms': ['taillight', 'rear_light'], 'id': 1056, 'def': 'lamp (usually red) mounted at the rear of a motor vehicle', 'name': 'taillight'}, {'frequency': 'r', 'synset': 'tambourine.n.01', 'synonyms': ['tambourine'], 'id': 1057, 'def': 'a shallow drum with a single drumhead and with metallic disks in the sides', 'name': 'tambourine'}, {'frequency': 'r', 'synset': 'tank.n.01', 'synonyms': ['army_tank', 'armored_combat_vehicle', 'armoured_combat_vehicle'], 'id': 1058, 'def': 'an enclosed armored military vehicle; has a cannon and moves on caterpillar treads', 'name': 'army_tank'}, {'frequency': 'f', 'synset': 'tank.n.02', 'synonyms': ['tank_(storage_vessel)', 'storage_tank'], 'id': 1059, 'def': 'a large (usually metallic) vessel for holding gases or liquids', 'name': 'tank_(storage_vessel)'}, {'frequency': 'f', 'synset': 'tank_top.n.01', 'synonyms': ['tank_top_(clothing)'], 'id': 1060, 'def': 'a tight-fitting sleeveless shirt with wide shoulder straps and low neck and no front opening', 'name': 'tank_top_(clothing)'}, {'frequency': 'f', 'synset': 'tape.n.01', 'synonyms': ['tape_(sticky_cloth_or_paper)'], 'id': 1061, 'def': 'a long thin piece of cloth or paper as used for binding or fastening', 'name': 'tape_(sticky_cloth_or_paper)'}, {'frequency': 'c', 'synset': 'tape.n.04', 'synonyms': ['tape_measure', 'measuring_tape'], 'id': 1062, 'def': 'measuring instrument consisting of a narrow strip (cloth or metal) marked in inches or centimeters and used for measuring lengths', 'name': 'tape_measure'}, {'frequency': 'c', 'synset': 'tapestry.n.02', 'synonyms': ['tapestry'], 'id': 1063, 'def': 'a heavy textile with a woven design; used for curtains and upholstery', 'name': 'tapestry'}, {'frequency': 'f', 'synset': 'tarpaulin.n.01', 'synonyms': ['tarp'], 'id': 1064, 'def': 'waterproofed canvas', 'name': 'tarp'}, {'frequency': 'c', 'synset': 'tartan.n.01', 'synonyms': ['tartan', 'plaid'], 'id': 1065, 'def': 'a cloth having a crisscross design', 'name': 'tartan'}, {'frequency': 'c', 'synset': 'tassel.n.01', 'synonyms': ['tassel'], 'id': 1066, 'def': 'adornment consisting of a bunch of cords fastened at one end', 'name': 'tassel'}, {'frequency': 'c', 'synset': 'tea_bag.n.01', 'synonyms': ['tea_bag'], 'id': 1067, 'def': 'a measured amount of tea in a bag for an individual serving of tea', 'name': 'tea_bag'}, {'frequency': 'c', 'synset': 'teacup.n.02', 'synonyms': ['teacup'], 'id': 1068, 'def': 'a cup from which tea is drunk', 'name': 'teacup'}, {'frequency': 'c', 'synset': 'teakettle.n.01', 'synonyms': ['teakettle'], 'id': 1069, 'def': 'kettle for boiling water to make tea', 'name': 'teakettle'}, {'frequency': 'f', 'synset': 'teapot.n.01', 'synonyms': ['teapot'], 'id': 1070, 'def': 'pot for brewing tea; usually has a spout and handle', 'name': 'teapot'}, {'frequency': 'f', 'synset': 'teddy.n.01', 'synonyms': ['teddy_bear'], 'id': 1071, 'def': "plaything consisting of a child's toy bear (usually plush and stuffed with soft materials)", 'name': 'teddy_bear'}, {'frequency': 'f', 'synset': 'telephone.n.01', 'synonyms': ['telephone', 'phone', 'telephone_set'], 'id': 1072, 'def': 'electronic device for communicating by voice over long distances (includes wired and wireless/cell phones)', 'name': 'telephone'}, {'frequency': 'c', 'synset': 'telephone_booth.n.01', 'synonyms': ['telephone_booth', 'phone_booth', 'call_box', 'telephone_box', 'telephone_kiosk'], 'id': 1073, 'def': 'booth for using a telephone', 'name': 'telephone_booth'}, {'frequency': 'f', 'synset': 'telephone_pole.n.01', 'synonyms': ['telephone_pole', 'telegraph_pole', 'telegraph_post'], 'id': 1074, 'def': 'tall pole supporting telephone wires', 'name': 'telephone_pole'}, {'frequency': 'r', 'synset': 'telephoto_lens.n.01', 'synonyms': ['telephoto_lens', 'zoom_lens'], 'id': 1075, 'def': 'a camera lens that magnifies the image', 'name': 'telephoto_lens'}, {'frequency': 'c', 'synset': 'television_camera.n.01', 'synonyms': ['television_camera', 'tv_camera'], 'id': 1076, 'def': 'television equipment for capturing and recording video', 'name': 'television_camera'}, {'frequency': 'f', 'synset': 'television_receiver.n.01', 'synonyms': ['television_set', 'tv', 'tv_set'], 'id': 1077, 'def': 'an electronic device that receives television signals and displays them on a screen', 'name': 'television_set'}, {'frequency': 'f', 'synset': 'tennis_ball.n.01', 'synonyms': ['tennis_ball'], 'id': 1078, 'def': 'ball about the size of a fist used in playing tennis', 'name': 'tennis_ball'}, {'frequency': 'f', 'synset': 'tennis_racket.n.01', 'synonyms': ['tennis_racket'], 'id': 1079, 'def': 'a racket used to play tennis', 'name': 'tennis_racket'}, {'frequency': 'r', 'synset': 'tequila.n.01', 'synonyms': ['tequila'], 'id': 1080, 'def': 'Mexican liquor made from fermented juices of an agave plant', 'name': 'tequila'}, {'frequency': 'c', 'synset': 'thermometer.n.01', 'synonyms': ['thermometer'], 'id': 1081, 'def': 'measuring instrument for measuring temperature', 'name': 'thermometer'}, {'frequency': 'c', 'synset': 'thermos.n.01', 'synonyms': ['thermos_bottle'], 'id': 1082, 'def': 'vacuum flask that preserves temperature of hot or cold drinks', 'name': 'thermos_bottle'}, {'frequency': 'f', 'synset': 'thermostat.n.01', 'synonyms': ['thermostat'], 'id': 1083, 'def': 'a regulator for automatically regulating temperature by starting or stopping the supply of heat', 'name': 'thermostat'}, {'frequency': 'r', 'synset': 'thimble.n.02', 'synonyms': ['thimble'], 'id': 1084, 'def': 'a small metal cap to protect the finger while sewing; can be used as a small container', 'name': 'thimble'}, {'frequency': 'c', 'synset': 'thread.n.01', 'synonyms': ['thread', 'yarn'], 'id': 1085, 'def': 'a fine cord of twisted fibers (of cotton or silk or wool or nylon etc.) used in sewing and weaving', 'name': 'thread'}, {'frequency': 'c', 'synset': 'thumbtack.n.01', 'synonyms': ['thumbtack', 'drawing_pin', 'pushpin'], 'id': 1086, 'def': 'a tack for attaching papers to a bulletin board or drawing board', 'name': 'thumbtack'}, {'frequency': 'c', 'synset': 'tiara.n.01', 'synonyms': ['tiara'], 'id': 1087, 'def': 'a jeweled headdress worn by women on formal occasions', 'name': 'tiara'}, {'frequency': 'c', 'synset': 'tiger.n.02', 'synonyms': ['tiger'], 'id': 1088, 'def': 'large feline of forests in most of Asia having a tawny coat with black stripes', 'name': 'tiger'}, {'frequency': 'c', 'synset': 'tights.n.01', 'synonyms': ['tights_(clothing)', 'leotards'], 'id': 1089, 'def': 'skintight knit hose covering the body from the waist to the feet worn by acrobats and dancers and as stockings by women and girls', 'name': 'tights_(clothing)'}, {'frequency': 'c', 'synset': 'timer.n.01', 'synonyms': ['timer', 'stopwatch'], 'id': 1090, 'def': 'a timepiece that measures a time interval and signals its end', 'name': 'timer'}, {'frequency': 'f', 'synset': 'tinfoil.n.01', 'synonyms': ['tinfoil'], 'id': 1091, 'def': 'foil made of tin or an alloy of tin and lead', 'name': 'tinfoil'}, {'frequency': 'c', 'synset': 'tinsel.n.01', 'synonyms': ['tinsel'], 'id': 1092, 'def': 'a showy decoration that is basically valueless', 'name': 'tinsel'}, {'frequency': 'f', 'synset': 'tissue.n.02', 'synonyms': ['tissue_paper'], 'id': 1093, 'def': 'a soft thin (usually translucent) paper', 'name': 'tissue_paper'}, {'frequency': 'c', 'synset': 'toast.n.01', 'synonyms': ['toast_(food)'], 'id': 1094, 'def': 'slice of bread that has been toasted', 'name': 'toast_(food)'}, {'frequency': 'f', 'synset': 'toaster.n.02', 'synonyms': ['toaster'], 'id': 1095, 'def': 'a kitchen appliance (usually electric) for toasting bread', 'name': 'toaster'}, {'frequency': 'f', 'synset': 'toaster_oven.n.01', 'synonyms': ['toaster_oven'], 'id': 1096, 'def': 'kitchen appliance consisting of a small electric oven for toasting or warming food', 'name': 'toaster_oven'}, {'frequency': 'f', 'synset': 'toilet.n.02', 'synonyms': ['toilet'], 'id': 1097, 'def': 'a plumbing fixture for defecation and urination', 'name': 'toilet'}, {'frequency': 'f', 'synset': 'toilet_tissue.n.01', 'synonyms': ['toilet_tissue', 'toilet_paper', 'bathroom_tissue'], 'id': 1098, 'def': 'a soft thin absorbent paper for use in toilets', 'name': 'toilet_tissue'}, {'frequency': 'f', 'synset': 'tomato.n.01', 'synonyms': ['tomato'], 'id': 1099, 'def': 'mildly acid red or yellow pulpy fruit eaten as a vegetable', 'name': 'tomato'}, {'frequency': 'f', 'synset': 'tongs.n.01', 'synonyms': ['tongs'], 'id': 1100, 'def': 'any of various devices for taking hold of objects; usually have two hinged legs with handles above and pointed hooks below', 'name': 'tongs'}, {'frequency': 'c', 'synset': 'toolbox.n.01', 'synonyms': ['toolbox'], 'id': 1101, 'def': 'a box or chest or cabinet for holding hand tools', 'name': 'toolbox'}, {'frequency': 'f', 'synset': 'toothbrush.n.01', 'synonyms': ['toothbrush'], 'id': 1102, 'def': 'small brush; has long handle; used to clean teeth', 'name': 'toothbrush'}, {'frequency': 'f', 'synset': 'toothpaste.n.01', 'synonyms': ['toothpaste'], 'id': 1103, 'def': 'a dentifrice in the form of a paste', 'name': 'toothpaste'}, {'frequency': 'f', 'synset': 'toothpick.n.01', 'synonyms': ['toothpick'], 'id': 1104, 'def': 'pick consisting of a small strip of wood or plastic; used to pick food from between the teeth', 'name': 'toothpick'}, {'frequency': 'f', 'synset': 'top.n.09', 'synonyms': ['cover'], 'id': 1105, 'def': 'covering for a hole (especially a hole in the top of a container)', 'name': 'cover'}, {'frequency': 'c', 'synset': 'tortilla.n.01', 'synonyms': ['tortilla'], 'id': 1106, 'def': 'thin unleavened pancake made from cornmeal or wheat flour', 'name': 'tortilla'}, {'frequency': 'c', 'synset': 'tow_truck.n.01', 'synonyms': ['tow_truck'], 'id': 1107, 'def': 'a truck equipped to hoist and pull wrecked cars (or to remove cars from no-parking zones)', 'name': 'tow_truck'}, {'frequency': 'f', 'synset': 'towel.n.01', 'synonyms': ['towel'], 'id': 1108, 'def': 'a rectangular piece of absorbent cloth (or paper) for drying or wiping', 'name': 'towel'}, {'frequency': 'f', 'synset': 'towel_rack.n.01', 'synonyms': ['towel_rack', 'towel_rail', 'towel_bar'], 'id': 1109, 'def': 'a rack consisting of one or more bars on which towels can be hung', 'name': 'towel_rack'}, {'frequency': 'f', 'synset': 'toy.n.03', 'synonyms': ['toy'], 'id': 1110, 'def': 'a device regarded as providing amusement', 'name': 'toy'}, {'frequency': 'c', 'synset': 'tractor.n.01', 'synonyms': ['tractor_(farm_equipment)'], 'id': 1111, 'def': 'a wheeled vehicle with large wheels; used in farming and other applications', 'name': 'tractor_(farm_equipment)'}, {'frequency': 'f', 'synset': 'traffic_light.n.01', 'synonyms': ['traffic_light'], 'id': 1112, 'def': 'a device to control vehicle traffic often consisting of three or more lights', 'name': 'traffic_light'}, {'frequency': 'c', 'synset': 'trail_bike.n.01', 'synonyms': ['dirt_bike'], 'id': 1113, 'def': 'a lightweight motorcycle equipped with rugged tires and suspension for off-road use', 'name': 'dirt_bike'}, {'frequency': 'f', 'synset': 'trailer_truck.n.01', 'synonyms': ['trailer_truck', 'tractor_trailer', 'trucking_rig', 'articulated_lorry', 'semi_truck'], 'id': 1114, 'def': 'a truck consisting of a tractor and trailer together', 'name': 'trailer_truck'}, {'frequency': 'f', 'synset': 'train.n.01', 'synonyms': ['train_(railroad_vehicle)', 'railroad_train'], 'id': 1115, 'def': 'public or private transport provided by a line of railway cars coupled together and drawn by a locomotive', 'name': 'train_(railroad_vehicle)'}, {'frequency': 'r', 'synset': 'trampoline.n.01', 'synonyms': ['trampoline'], 'id': 1116, 'def': 'gymnastic apparatus consisting of a strong canvas sheet attached with springs to a metal frame', 'name': 'trampoline'}, {'frequency': 'f', 'synset': 'tray.n.01', 'synonyms': ['tray'], 'id': 1117, 'def': 'an open receptacle for holding or displaying or serving articles or food', 'name': 'tray'}, {'frequency': 'r', 'synset': 'trench_coat.n.01', 'synonyms': ['trench_coat'], 'id': 1118, 'def': 'a military style raincoat; belted with deep pockets', 'name': 'trench_coat'}, {'frequency': 'r', 'synset': 'triangle.n.05', 'synonyms': ['triangle_(musical_instrument)'], 'id': 1119, 'def': 'a percussion instrument consisting of a metal bar bent in the shape of an open triangle', 'name': 'triangle_(musical_instrument)'}, {'frequency': 'c', 'synset': 'tricycle.n.01', 'synonyms': ['tricycle'], 'id': 1120, 'def': 'a vehicle with three wheels that is moved by foot pedals', 'name': 'tricycle'}, {'frequency': 'f', 'synset': 'tripod.n.01', 'synonyms': ['tripod'], 'id': 1121, 'def': 'a three-legged rack used for support', 'name': 'tripod'}, {'frequency': 'f', 'synset': 'trouser.n.01', 'synonyms': ['trousers', 'pants_(clothing)'], 'id': 1122, 'def': 'a garment extending from the waist to the knee or ankle, covering each leg separately', 'name': 'trousers'}, {'frequency': 'f', 'synset': 'truck.n.01', 'synonyms': ['truck'], 'id': 1123, 'def': 'an automotive vehicle suitable for hauling', 'name': 'truck'}, {'frequency': 'r', 'synset': 'truffle.n.03', 'synonyms': ['truffle_(chocolate)', 'chocolate_truffle'], 'id': 1124, 'def': 'creamy chocolate candy', 'name': 'truffle_(chocolate)'}, {'frequency': 'c', 'synset': 'trunk.n.02', 'synonyms': ['trunk'], 'id': 1125, 'def': 'luggage consisting of a large strong case used when traveling or for storage', 'name': 'trunk'}, {'frequency': 'r', 'synset': 'tub.n.02', 'synonyms': ['vat'], 'id': 1126, 'def': 'a large vessel for holding or storing liquids', 'name': 'vat'}, {'frequency': 'c', 'synset': 'turban.n.01', 'synonyms': ['turban'], 'id': 1127, 'def': 'a traditional headdress consisting of a long scarf wrapped around the head', 'name': 'turban'}, {'frequency': 'c', 'synset': 'turkey.n.04', 'synonyms': ['turkey_(food)'], 'id': 1128, 'def': 'flesh of large domesticated fowl usually roasted', 'name': 'turkey_(food)'}, {'frequency': 'r', 'synset': 'turnip.n.01', 'synonyms': ['turnip'], 'id': 1129, 'def': 'widely cultivated plant having a large fleshy edible white or yellow root', 'name': 'turnip'}, {'frequency': 'c', 'synset': 'turtle.n.02', 'synonyms': ['turtle'], 'id': 1130, 'def': 'any of various aquatic and land reptiles having a bony shell and flipper-like limbs for swimming', 'name': 'turtle'}, {'frequency': 'c', 'synset': 'turtleneck.n.01', 'synonyms': ['turtleneck_(clothing)', 'polo-neck'], 'id': 1131, 'def': 'a sweater or jersey with a high close-fitting collar', 'name': 'turtleneck_(clothing)'}, {'frequency': 'c', 'synset': 'typewriter.n.01', 'synonyms': ['typewriter'], 'id': 1132, 'def': 'hand-operated character printer for printing written messages one character at a time', 'name': 'typewriter'}, {'frequency': 'f', 'synset': 'umbrella.n.01', 'synonyms': ['umbrella'], 'id': 1133, 'def': 'a lightweight handheld collapsible canopy', 'name': 'umbrella'}, {'frequency': 'f', 'synset': 'underwear.n.01', 'synonyms': ['underwear', 'underclothes', 'underclothing', 'underpants'], 'id': 1134, 'def': 'undergarment worn next to the skin and under the outer garments', 'name': 'underwear'}, {'frequency': 'r', 'synset': 'unicycle.n.01', 'synonyms': ['unicycle'], 'id': 1135, 'def': 'a vehicle with a single wheel that is driven by pedals', 'name': 'unicycle'}, {'frequency': 'f', 'synset': 'urinal.n.01', 'synonyms': ['urinal'], 'id': 1136, 'def': 'a plumbing fixture (usually attached to the wall) used by men to urinate', 'name': 'urinal'}, {'frequency': 'c', 'synset': 'urn.n.01', 'synonyms': ['urn'], 'id': 1137, 'def': 'a large vase that usually has a pedestal or feet', 'name': 'urn'}, {'frequency': 'c', 'synset': 'vacuum.n.04', 'synonyms': ['vacuum_cleaner'], 'id': 1138, 'def': 'an electrical home appliance that cleans by suction', 'name': 'vacuum_cleaner'}, {'frequency': 'f', 'synset': 'vase.n.01', 'synonyms': ['vase'], 'id': 1139, 'def': 'an open jar of glass or porcelain used as an ornament or to hold flowers', 'name': 'vase'}, {'frequency': 'c', 'synset': 'vending_machine.n.01', 'synonyms': ['vending_machine'], 'id': 1140, 'def': 'a slot machine for selling goods', 'name': 'vending_machine'}, {'frequency': 'f', 'synset': 'vent.n.01', 'synonyms': ['vent', 'blowhole', 'air_vent'], 'id': 1141, 'def': 'a hole for the escape of gas or air', 'name': 'vent'}, {'frequency': 'f', 'synset': 'vest.n.01', 'synonyms': ['vest', 'waistcoat'], 'id': 1142, 'def': "a man's sleeveless garment worn underneath a coat", 'name': 'vest'}, {'frequency': 'c', 'synset': 'videotape.n.01', 'synonyms': ['videotape'], 'id': 1143, 'def': 'a video recording made on magnetic tape', 'name': 'videotape'}, {'frequency': 'r', 'synset': 'vinegar.n.01', 'synonyms': ['vinegar'], 'id': 1144, 'def': 'sour-tasting liquid produced usually by oxidation of the alcohol in wine or cider and used as a condiment or food preservative', 'name': 'vinegar'}, {'frequency': 'r', 'synset': 'violin.n.01', 'synonyms': ['violin', 'fiddle'], 'id': 1145, 'def': 'bowed stringed instrument that is the highest member of the violin family', 'name': 'violin'}, {'frequency': 'r', 'synset': 'vodka.n.01', 'synonyms': ['vodka'], 'id': 1146, 'def': 'unaged colorless liquor originating in Russia', 'name': 'vodka'}, {'frequency': 'c', 'synset': 'volleyball.n.02', 'synonyms': ['volleyball'], 'id': 1147, 'def': 'an inflated ball used in playing volleyball', 'name': 'volleyball'}, {'frequency': 'r', 'synset': 'vulture.n.01', 'synonyms': ['vulture'], 'id': 1148, 'def': 'any of various large birds of prey having naked heads and weak claws and feeding chiefly on carrion', 'name': 'vulture'}, {'frequency': 'c', 'synset': 'waffle.n.01', 'synonyms': ['waffle'], 'id': 1149, 'def': 'pancake batter baked in a waffle iron', 'name': 'waffle'}, {'frequency': 'r', 'synset': 'waffle_iron.n.01', 'synonyms': ['waffle_iron'], 'id': 1150, 'def': 'a kitchen appliance for baking waffles', 'name': 'waffle_iron'}, {'frequency': 'c', 'synset': 'wagon.n.01', 'synonyms': ['wagon'], 'id': 1151, 'def': 'any of various kinds of wheeled vehicles drawn by an animal or a tractor', 'name': 'wagon'}, {'frequency': 'c', 'synset': 'wagon_wheel.n.01', 'synonyms': ['wagon_wheel'], 'id': 1152, 'def': 'a wheel of a wagon', 'name': 'wagon_wheel'}, {'frequency': 'c', 'synset': 'walking_stick.n.01', 'synonyms': ['walking_stick'], 'id': 1153, 'def': 'a stick carried in the hand for support in walking', 'name': 'walking_stick'}, {'frequency': 'c', 'synset': 'wall_clock.n.01', 'synonyms': ['wall_clock'], 'id': 1154, 'def': 'a clock mounted on a wall', 'name': 'wall_clock'}, {'frequency': 'f', 'synset': 'wall_socket.n.01', 'synonyms': ['wall_socket', 'wall_plug', 'electric_outlet', 'electrical_outlet', 'outlet', 'electric_receptacle'], 'id': 1155, 'def': 'receptacle providing a place in a wiring system where current can be taken to run electrical devices', 'name': 'wall_socket'}, {'frequency': 'f', 'synset': 'wallet.n.01', 'synonyms': ['wallet', 'billfold'], 'id': 1156, 'def': 'a pocket-size case for holding papers and paper money', 'name': 'wallet'}, {'frequency': 'r', 'synset': 'walrus.n.01', 'synonyms': ['walrus'], 'id': 1157, 'def': 'either of two large northern marine mammals having ivory tusks and tough hide over thick blubber', 'name': 'walrus'}, {'frequency': 'r', 'synset': 'wardrobe.n.01', 'synonyms': ['wardrobe'], 'id': 1158, 'def': 'a tall piece of furniture that provides storage space for clothes; has a door and rails or hooks for hanging clothes', 'name': 'wardrobe'}, {'frequency': 'r', 'synset': 'washbasin.n.01', 'synonyms': ['washbasin', 'basin_(for_washing)', 'washbowl', 'washstand', 'handbasin'], 'id': 1159, 'def': 'a bathroom sink that is permanently installed and connected to a water supply and drainpipe; where you can wash your hands and face', 'name': 'washbasin'}, {'frequency': 'c', 'synset': 'washer.n.03', 'synonyms': ['automatic_washer', 'washing_machine'], 'id': 1160, 'def': 'a home appliance for washing clothes and linens automatically', 'name': 'automatic_washer'}, {'frequency': 'f', 'synset': 'watch.n.01', 'synonyms': ['watch', 'wristwatch'], 'id': 1161, 'def': 'a small, portable timepiece', 'name': 'watch'}, {'frequency': 'f', 'synset': 'water_bottle.n.01', 'synonyms': ['water_bottle'], 'id': 1162, 'def': 'a bottle for holding water', 'name': 'water_bottle'}, {'frequency': 'c', 'synset': 'water_cooler.n.01', 'synonyms': ['water_cooler'], 'id': 1163, 'def': 'a device for cooling and dispensing drinking water', 'name': 'water_cooler'}, {'frequency': 'c', 'synset': 'water_faucet.n.01', 'synonyms': ['water_faucet', 'water_tap', 'tap_(water_faucet)'], 'id': 1164, 'def': 'a faucet for drawing water from a pipe or cask', 'name': 'water_faucet'}, {'frequency': 'r', 'synset': 'water_heater.n.01', 'synonyms': ['water_heater', 'hot-water_heater'], 'id': 1165, 'def': 'a heater and storage tank to supply heated water', 'name': 'water_heater'}, {'frequency': 'c', 'synset': 'water_jug.n.01', 'synonyms': ['water_jug'], 'id': 1166, 'def': 'a jug that holds water', 'name': 'water_jug'}, {'frequency': 'r', 'synset': 'water_pistol.n.01', 'synonyms': ['water_gun', 'squirt_gun'], 'id': 1167, 'def': 'plaything consisting of a toy pistol that squirts water', 'name': 'water_gun'}, {'frequency': 'c', 'synset': 'water_scooter.n.01', 'synonyms': ['water_scooter', 'sea_scooter', 'jet_ski'], 'id': 1168, 'def': 'a motorboat resembling a motor scooter (NOT A SURFBOARD OR WATER SKI)', 'name': 'water_scooter'}, {'frequency': 'c', 'synset': 'water_ski.n.01', 'synonyms': ['water_ski'], 'id': 1169, 'def': 'broad ski for skimming over water towed by a speedboat (DO NOT MARK WATER)', 'name': 'water_ski'}, {'frequency': 'c', 'synset': 'water_tower.n.01', 'synonyms': ['water_tower'], 'id': 1170, 'def': 'a large reservoir for water', 'name': 'water_tower'}, {'frequency': 'c', 'synset': 'watering_can.n.01', 'synonyms': ['watering_can'], 'id': 1171, 'def': 'a container with a handle and a spout with a perforated nozzle; used to sprinkle water over plants', 'name': 'watering_can'}, {'frequency': 'f', 'synset': 'watermelon.n.02', 'synonyms': ['watermelon'], 'id': 1172, 'def': 'large oblong or roundish melon with a hard green rind and sweet watery red or occasionally yellowish pulp', 'name': 'watermelon'}, {'frequency': 'f', 'synset': 'weathervane.n.01', 'synonyms': ['weathervane', 'vane_(weathervane)', 'wind_vane'], 'id': 1173, 'def': 'mechanical device attached to an elevated structure; rotates freely to show the direction of the wind', 'name': 'weathervane'}, {'frequency': 'c', 'synset': 'webcam.n.01', 'synonyms': ['webcam'], 'id': 1174, 'def': 'a digital camera designed to take digital photographs and transmit them over the internet', 'name': 'webcam'}, {'frequency': 'c', 'synset': 'wedding_cake.n.01', 'synonyms': ['wedding_cake', 'bridecake'], 'id': 1175, 'def': 'a rich cake with two or more tiers and covered with frosting and decorations; served at a wedding reception', 'name': 'wedding_cake'}, {'frequency': 'c', 'synset': 'wedding_ring.n.01', 'synonyms': ['wedding_ring', 'wedding_band'], 'id': 1176, 'def': 'a ring given to the bride and/or groom at the wedding', 'name': 'wedding_ring'}, {'frequency': 'f', 'synset': 'wet_suit.n.01', 'synonyms': ['wet_suit'], 'id': 1177, 'def': 'a close-fitting garment made of a permeable material; worn in cold water to retain body heat', 'name': 'wet_suit'}, {'frequency': 'f', 'synset': 'wheel.n.01', 'synonyms': ['wheel'], 'id': 1178, 'def': 'a circular frame with spokes (or a solid disc) that can rotate on a shaft or axle', 'name': 'wheel'}, {'frequency': 'c', 'synset': 'wheelchair.n.01', 'synonyms': ['wheelchair'], 'id': 1179, 'def': 'a movable chair mounted on large wheels', 'name': 'wheelchair'}, {'frequency': 'c', 'synset': 'whipped_cream.n.01', 'synonyms': ['whipped_cream'], 'id': 1180, 'def': 'cream that has been beaten until light and fluffy', 'name': 'whipped_cream'}, {'frequency': 'c', 'synset': 'whistle.n.03', 'synonyms': ['whistle'], 'id': 1181, 'def': 'a small wind instrument that produces a whistling sound by blowing into it', 'name': 'whistle'}, {'frequency': 'c', 'synset': 'wig.n.01', 'synonyms': ['wig'], 'id': 1182, 'def': 'hairpiece covering the head and made of real or synthetic hair', 'name': 'wig'}, {'frequency': 'c', 'synset': 'wind_chime.n.01', 'synonyms': ['wind_chime'], 'id': 1183, 'def': 'a decorative arrangement of pieces of metal or glass or pottery that hang together loosely so the wind can cause them to tinkle', 'name': 'wind_chime'}, {'frequency': 'c', 'synset': 'windmill.n.01', 'synonyms': ['windmill'], 'id': 1184, 'def': 'A mill or turbine that is powered by wind', 'name': 'windmill'}, {'frequency': 'c', 'synset': 'window_box.n.01', 'synonyms': ['window_box_(for_plants)'], 'id': 1185, 'def': 'a container for growing plants on a windowsill', 'name': 'window_box_(for_plants)'}, {'frequency': 'f', 'synset': 'windshield_wiper.n.01', 'synonyms': ['windshield_wiper', 'windscreen_wiper', 'wiper_(for_windshield/screen)'], 'id': 1186, 'def': 'a mechanical device that cleans the windshield', 'name': 'windshield_wiper'}, {'frequency': 'c', 'synset': 'windsock.n.01', 'synonyms': ['windsock', 'air_sock', 'air-sleeve', 'wind_sleeve', 'wind_cone'], 'id': 1187, 'def': 'a truncated cloth cone mounted on a mast/pole; shows wind direction', 'name': 'windsock'}, {'frequency': 'f', 'synset': 'wine_bottle.n.01', 'synonyms': ['wine_bottle'], 'id': 1188, 'def': 'a bottle for holding wine', 'name': 'wine_bottle'}, {'frequency': 'c', 'synset': 'wine_bucket.n.01', 'synonyms': ['wine_bucket', 'wine_cooler'], 'id': 1189, 'def': 'a bucket of ice used to chill a bottle of wine', 'name': 'wine_bucket'}, {'frequency': 'f', 'synset': 'wineglass.n.01', 'synonyms': ['wineglass'], 'id': 1190, 'def': 'a glass that has a stem and in which wine is served', 'name': 'wineglass'}, {'frequency': 'f', 'synset': 'winker.n.02', 'synonyms': ['blinder_(for_horses)'], 'id': 1191, 'def': 'blinds that prevent a horse from seeing something on either side', 'name': 'blinder_(for_horses)'}, {'frequency': 'c', 'synset': 'wok.n.01', 'synonyms': ['wok'], 'id': 1192, 'def': 'pan with a convex bottom; used for frying in Chinese cooking', 'name': 'wok'}, {'frequency': 'r', 'synset': 'wolf.n.01', 'synonyms': ['wolf'], 'id': 1193, 'def': 'a wild carnivorous mammal of the dog family, living and hunting in packs', 'name': 'wolf'}, {'frequency': 'c', 'synset': 'wooden_spoon.n.02', 'synonyms': ['wooden_spoon'], 'id': 1194, 'def': 'a spoon made of wood', 'name': 'wooden_spoon'}, {'frequency': 'c', 'synset': 'wreath.n.01', 'synonyms': ['wreath'], 'id': 1195, 'def': 'an arrangement of flowers, leaves, or stems fastened in a ring', 'name': 'wreath'}, {'frequency': 'c', 'synset': 'wrench.n.03', 'synonyms': ['wrench', 'spanner'], 'id': 1196, 'def': 'a hand tool that is used to hold or twist a nut or bolt', 'name': 'wrench'}, {'frequency': 'f', 'synset': 'wristband.n.01', 'synonyms': ['wristband'], 'id': 1197, 'def': 'band consisting of a part of a sleeve that covers the wrist', 'name': 'wristband'}, {'frequency': 'f', 'synset': 'wristlet.n.01', 'synonyms': ['wristlet', 'wrist_band'], 'id': 1198, 'def': 'a band or bracelet worn around the wrist', 'name': 'wristlet'}, {'frequency': 'c', 'synset': 'yacht.n.01', 'synonyms': ['yacht'], 'id': 1199, 'def': 'an expensive vessel propelled by sail or power and used for cruising or racing', 'name': 'yacht'}, {'frequency': 'c', 'synset': 'yogurt.n.01', 'synonyms': ['yogurt', 'yoghurt', 'yoghourt'], 'id': 1200, 'def': 'a custard-like food made from curdled milk', 'name': 'yogurt'}, {'frequency': 'c', 'synset': 'yoke.n.07', 'synonyms': ['yoke_(animal_equipment)'], 'id': 1201, 'def': 'gear joining two animals at the neck; NOT egg yolk', 'name': 'yoke_(animal_equipment)'}, {'frequency': 'f', 'synset': 'zebra.n.01', 'synonyms': ['zebra'], 'id': 1202, 'def': 'any of several fleet black-and-white striped African equines', 'name': 'zebra'}, {'frequency': 'c', 'synset': 'zucchini.n.02', 'synonyms': ['zucchini', 'courgette'], 'id': 1203, 'def': 'small cucumber-shaped vegetable marrow; typically dark green', 'name': 'zucchini'}] # noqa # fmt: on ================================================ FILE: detectron2/detectron2/data/datasets/lvis_v1_category_image_count.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # Autogen with # with open("lvis_v1_train.json", "r") as f: # a = json.load(f) # c = a["categories"] # for x in c: # del x["name"] # del x["instance_count"] # del x["def"] # del x["synonyms"] # del x["frequency"] # del x["synset"] # LVIS_CATEGORY_IMAGE_COUNT = repr(c) + " # noqa" # with open("/tmp/lvis_category_image_count.py", "wt") as f: # f.write(f"LVIS_CATEGORY_IMAGE_COUNT = {LVIS_CATEGORY_IMAGE_COUNT}") # Then paste the contents of that file below # fmt: off LVIS_CATEGORY_IMAGE_COUNT = [{'id': 1, 'image_count': 64}, {'id': 2, 'image_count': 364}, {'id': 3, 'image_count': 1911}, {'id': 4, 'image_count': 149}, {'id': 5, 'image_count': 29}, {'id': 6, 'image_count': 26}, {'id': 7, 'image_count': 59}, {'id': 8, 'image_count': 22}, {'id': 9, 'image_count': 12}, {'id': 10, 'image_count': 28}, {'id': 11, 'image_count': 505}, {'id': 12, 'image_count': 1207}, {'id': 13, 'image_count': 4}, {'id': 14, 'image_count': 10}, {'id': 15, 'image_count': 500}, {'id': 16, 'image_count': 33}, {'id': 17, 'image_count': 3}, {'id': 18, 'image_count': 44}, {'id': 19, 'image_count': 561}, {'id': 20, 'image_count': 8}, {'id': 21, 'image_count': 9}, {'id': 22, 'image_count': 33}, {'id': 23, 'image_count': 1883}, {'id': 24, 'image_count': 98}, {'id': 25, 'image_count': 70}, {'id': 26, 'image_count': 46}, {'id': 27, 'image_count': 117}, {'id': 28, 'image_count': 41}, {'id': 29, 'image_count': 1395}, {'id': 30, 'image_count': 7}, {'id': 31, 'image_count': 1}, {'id': 32, 'image_count': 314}, {'id': 33, 'image_count': 31}, {'id': 34, 'image_count': 1905}, {'id': 35, 'image_count': 1859}, {'id': 36, 'image_count': 1623}, {'id': 37, 'image_count': 47}, {'id': 38, 'image_count': 3}, {'id': 39, 'image_count': 3}, {'id': 40, 'image_count': 1}, {'id': 41, 'image_count': 305}, {'id': 42, 'image_count': 6}, {'id': 43, 'image_count': 210}, {'id': 44, 'image_count': 36}, {'id': 45, 'image_count': 1787}, {'id': 46, 'image_count': 17}, {'id': 47, 'image_count': 51}, {'id': 48, 'image_count': 138}, {'id': 49, 'image_count': 3}, {'id': 50, 'image_count': 1470}, {'id': 51, 'image_count': 3}, {'id': 52, 'image_count': 2}, {'id': 53, 'image_count': 186}, {'id': 54, 'image_count': 76}, {'id': 55, 'image_count': 26}, {'id': 56, 'image_count': 303}, {'id': 57, 'image_count': 738}, {'id': 58, 'image_count': 1799}, {'id': 59, 'image_count': 1934}, {'id': 60, 'image_count': 1609}, {'id': 61, 'image_count': 1622}, {'id': 62, 'image_count': 41}, {'id': 63, 'image_count': 4}, {'id': 64, 'image_count': 11}, {'id': 65, 'image_count': 270}, {'id': 66, 'image_count': 349}, {'id': 67, 'image_count': 42}, {'id': 68, 'image_count': 823}, {'id': 69, 'image_count': 6}, {'id': 70, 'image_count': 48}, {'id': 71, 'image_count': 3}, {'id': 72, 'image_count': 42}, {'id': 73, 'image_count': 24}, {'id': 74, 'image_count': 16}, {'id': 75, 'image_count': 605}, {'id': 76, 'image_count': 646}, {'id': 77, 'image_count': 1765}, {'id': 78, 'image_count': 2}, {'id': 79, 'image_count': 125}, {'id': 80, 'image_count': 1420}, {'id': 81, 'image_count': 140}, {'id': 82, 'image_count': 4}, {'id': 83, 'image_count': 322}, {'id': 84, 'image_count': 60}, {'id': 85, 'image_count': 2}, {'id': 86, 'image_count': 231}, {'id': 87, 'image_count': 333}, {'id': 88, 'image_count': 1941}, {'id': 89, 'image_count': 367}, {'id': 90, 'image_count': 1922}, {'id': 91, 'image_count': 18}, {'id': 92, 'image_count': 81}, {'id': 93, 'image_count': 1}, {'id': 94, 'image_count': 1852}, {'id': 95, 'image_count': 430}, {'id': 96, 'image_count': 247}, {'id': 97, 'image_count': 94}, {'id': 98, 'image_count': 21}, {'id': 99, 'image_count': 1821}, {'id': 100, 'image_count': 16}, {'id': 101, 'image_count': 12}, {'id': 102, 'image_count': 25}, {'id': 103, 'image_count': 41}, {'id': 104, 'image_count': 244}, {'id': 105, 'image_count': 7}, {'id': 106, 'image_count': 1}, {'id': 107, 'image_count': 40}, {'id': 108, 'image_count': 40}, {'id': 109, 'image_count': 104}, {'id': 110, 'image_count': 1671}, {'id': 111, 'image_count': 49}, {'id': 112, 'image_count': 243}, {'id': 113, 'image_count': 2}, {'id': 114, 'image_count': 242}, {'id': 115, 'image_count': 271}, {'id': 116, 'image_count': 104}, {'id': 117, 'image_count': 8}, {'id': 118, 'image_count': 1758}, {'id': 119, 'image_count': 1}, {'id': 120, 'image_count': 48}, {'id': 121, 'image_count': 14}, {'id': 122, 'image_count': 40}, {'id': 123, 'image_count': 1}, {'id': 124, 'image_count': 37}, {'id': 125, 'image_count': 1510}, {'id': 126, 'image_count': 6}, {'id': 127, 'image_count': 1903}, {'id': 128, 'image_count': 70}, {'id': 129, 'image_count': 86}, {'id': 130, 'image_count': 7}, {'id': 131, 'image_count': 5}, {'id': 132, 'image_count': 1406}, {'id': 133, 'image_count': 1901}, {'id': 134, 'image_count': 15}, {'id': 135, 'image_count': 28}, {'id': 136, 'image_count': 6}, {'id': 137, 'image_count': 494}, {'id': 138, 'image_count': 234}, {'id': 139, 'image_count': 1922}, {'id': 140, 'image_count': 1}, {'id': 141, 'image_count': 35}, {'id': 142, 'image_count': 5}, {'id': 143, 'image_count': 1828}, {'id': 144, 'image_count': 8}, {'id': 145, 'image_count': 63}, {'id': 146, 'image_count': 1668}, {'id': 147, 'image_count': 4}, {'id': 148, 'image_count': 95}, {'id': 149, 'image_count': 17}, {'id': 150, 'image_count': 1567}, {'id': 151, 'image_count': 2}, {'id': 152, 'image_count': 103}, {'id': 153, 'image_count': 50}, {'id': 154, 'image_count': 1309}, {'id': 155, 'image_count': 6}, {'id': 156, 'image_count': 92}, {'id': 157, 'image_count': 19}, {'id': 158, 'image_count': 37}, {'id': 159, 'image_count': 4}, {'id': 160, 'image_count': 709}, {'id': 161, 'image_count': 9}, {'id': 162, 'image_count': 82}, {'id': 163, 'image_count': 15}, {'id': 164, 'image_count': 3}, {'id': 165, 'image_count': 61}, {'id': 166, 'image_count': 51}, {'id': 167, 'image_count': 5}, {'id': 168, 'image_count': 13}, {'id': 169, 'image_count': 642}, {'id': 170, 'image_count': 24}, {'id': 171, 'image_count': 255}, {'id': 172, 'image_count': 9}, {'id': 173, 'image_count': 1808}, {'id': 174, 'image_count': 31}, {'id': 175, 'image_count': 158}, {'id': 176, 'image_count': 80}, {'id': 177, 'image_count': 1884}, {'id': 178, 'image_count': 158}, {'id': 179, 'image_count': 2}, {'id': 180, 'image_count': 12}, {'id': 181, 'image_count': 1659}, {'id': 182, 'image_count': 7}, {'id': 183, 'image_count': 834}, {'id': 184, 'image_count': 57}, {'id': 185, 'image_count': 174}, {'id': 186, 'image_count': 95}, {'id': 187, 'image_count': 27}, {'id': 188, 'image_count': 22}, {'id': 189, 'image_count': 1391}, {'id': 190, 'image_count': 90}, {'id': 191, 'image_count': 40}, {'id': 192, 'image_count': 445}, {'id': 193, 'image_count': 21}, {'id': 194, 'image_count': 1132}, {'id': 195, 'image_count': 177}, {'id': 196, 'image_count': 4}, {'id': 197, 'image_count': 17}, {'id': 198, 'image_count': 84}, {'id': 199, 'image_count': 55}, {'id': 200, 'image_count': 30}, {'id': 201, 'image_count': 25}, {'id': 202, 'image_count': 2}, {'id': 203, 'image_count': 125}, {'id': 204, 'image_count': 1135}, {'id': 205, 'image_count': 19}, {'id': 206, 'image_count': 72}, {'id': 207, 'image_count': 1926}, {'id': 208, 'image_count': 159}, {'id': 209, 'image_count': 7}, {'id': 210, 'image_count': 1}, {'id': 211, 'image_count': 13}, {'id': 212, 'image_count': 35}, {'id': 213, 'image_count': 18}, {'id': 214, 'image_count': 8}, {'id': 215, 'image_count': 6}, {'id': 216, 'image_count': 35}, {'id': 217, 'image_count': 1222}, {'id': 218, 'image_count': 103}, {'id': 219, 'image_count': 28}, {'id': 220, 'image_count': 63}, {'id': 221, 'image_count': 28}, {'id': 222, 'image_count': 5}, {'id': 223, 'image_count': 7}, {'id': 224, 'image_count': 14}, {'id': 225, 'image_count': 1918}, {'id': 226, 'image_count': 133}, {'id': 227, 'image_count': 16}, {'id': 228, 'image_count': 27}, {'id': 229, 'image_count': 110}, {'id': 230, 'image_count': 1895}, {'id': 231, 'image_count': 4}, {'id': 232, 'image_count': 1927}, {'id': 233, 'image_count': 8}, {'id': 234, 'image_count': 1}, {'id': 235, 'image_count': 263}, {'id': 236, 'image_count': 10}, {'id': 237, 'image_count': 2}, {'id': 238, 'image_count': 3}, {'id': 239, 'image_count': 87}, {'id': 240, 'image_count': 9}, {'id': 241, 'image_count': 71}, {'id': 242, 'image_count': 13}, {'id': 243, 'image_count': 18}, {'id': 244, 'image_count': 2}, {'id': 245, 'image_count': 5}, {'id': 246, 'image_count': 45}, {'id': 247, 'image_count': 1}, {'id': 248, 'image_count': 23}, {'id': 249, 'image_count': 32}, {'id': 250, 'image_count': 4}, {'id': 251, 'image_count': 1}, {'id': 252, 'image_count': 858}, {'id': 253, 'image_count': 661}, {'id': 254, 'image_count': 168}, {'id': 255, 'image_count': 210}, {'id': 256, 'image_count': 65}, {'id': 257, 'image_count': 4}, {'id': 258, 'image_count': 2}, {'id': 259, 'image_count': 159}, {'id': 260, 'image_count': 31}, {'id': 261, 'image_count': 811}, {'id': 262, 'image_count': 1}, {'id': 263, 'image_count': 42}, {'id': 264, 'image_count': 27}, {'id': 265, 'image_count': 2}, {'id': 266, 'image_count': 5}, {'id': 267, 'image_count': 95}, {'id': 268, 'image_count': 32}, {'id': 269, 'image_count': 1}, {'id': 270, 'image_count': 1}, {'id': 271, 'image_count': 1844}, {'id': 272, 'image_count': 897}, {'id': 273, 'image_count': 31}, {'id': 274, 'image_count': 23}, {'id': 275, 'image_count': 1}, {'id': 276, 'image_count': 202}, {'id': 277, 'image_count': 746}, {'id': 278, 'image_count': 44}, {'id': 279, 'image_count': 14}, {'id': 280, 'image_count': 26}, {'id': 281, 'image_count': 1}, {'id': 282, 'image_count': 2}, {'id': 283, 'image_count': 25}, {'id': 284, 'image_count': 238}, {'id': 285, 'image_count': 592}, {'id': 286, 'image_count': 26}, {'id': 287, 'image_count': 5}, {'id': 288, 'image_count': 42}, {'id': 289, 'image_count': 13}, {'id': 290, 'image_count': 46}, {'id': 291, 'image_count': 1}, {'id': 292, 'image_count': 8}, {'id': 293, 'image_count': 34}, {'id': 294, 'image_count': 5}, {'id': 295, 'image_count': 1}, {'id': 296, 'image_count': 1871}, {'id': 297, 'image_count': 717}, {'id': 298, 'image_count': 1010}, {'id': 299, 'image_count': 679}, {'id': 300, 'image_count': 3}, {'id': 301, 'image_count': 4}, {'id': 302, 'image_count': 1}, {'id': 303, 'image_count': 166}, {'id': 304, 'image_count': 2}, {'id': 305, 'image_count': 266}, {'id': 306, 'image_count': 101}, {'id': 307, 'image_count': 6}, {'id': 308, 'image_count': 14}, {'id': 309, 'image_count': 133}, {'id': 310, 'image_count': 2}, {'id': 311, 'image_count': 38}, {'id': 312, 'image_count': 95}, {'id': 313, 'image_count': 1}, {'id': 314, 'image_count': 12}, {'id': 315, 'image_count': 49}, {'id': 316, 'image_count': 5}, {'id': 317, 'image_count': 5}, {'id': 318, 'image_count': 16}, {'id': 319, 'image_count': 216}, {'id': 320, 'image_count': 12}, {'id': 321, 'image_count': 1}, {'id': 322, 'image_count': 54}, {'id': 323, 'image_count': 5}, {'id': 324, 'image_count': 245}, {'id': 325, 'image_count': 12}, {'id': 326, 'image_count': 7}, {'id': 327, 'image_count': 35}, {'id': 328, 'image_count': 36}, {'id': 329, 'image_count': 32}, {'id': 330, 'image_count': 1027}, {'id': 331, 'image_count': 10}, {'id': 332, 'image_count': 12}, {'id': 333, 'image_count': 1}, {'id': 334, 'image_count': 67}, {'id': 335, 'image_count': 71}, {'id': 336, 'image_count': 30}, {'id': 337, 'image_count': 48}, {'id': 338, 'image_count': 249}, {'id': 339, 'image_count': 13}, {'id': 340, 'image_count': 29}, {'id': 341, 'image_count': 14}, {'id': 342, 'image_count': 236}, {'id': 343, 'image_count': 15}, {'id': 344, 'image_count': 1521}, {'id': 345, 'image_count': 25}, {'id': 346, 'image_count': 249}, {'id': 347, 'image_count': 139}, {'id': 348, 'image_count': 2}, {'id': 349, 'image_count': 2}, {'id': 350, 'image_count': 1890}, {'id': 351, 'image_count': 1240}, {'id': 352, 'image_count': 1}, {'id': 353, 'image_count': 9}, {'id': 354, 'image_count': 1}, {'id': 355, 'image_count': 3}, {'id': 356, 'image_count': 11}, {'id': 357, 'image_count': 4}, {'id': 358, 'image_count': 236}, {'id': 359, 'image_count': 44}, {'id': 360, 'image_count': 19}, {'id': 361, 'image_count': 1100}, {'id': 362, 'image_count': 7}, {'id': 363, 'image_count': 69}, {'id': 364, 'image_count': 2}, {'id': 365, 'image_count': 8}, {'id': 366, 'image_count': 5}, {'id': 367, 'image_count': 227}, {'id': 368, 'image_count': 6}, {'id': 369, 'image_count': 106}, {'id': 370, 'image_count': 81}, {'id': 371, 'image_count': 17}, {'id': 372, 'image_count': 134}, {'id': 373, 'image_count': 312}, {'id': 374, 'image_count': 8}, {'id': 375, 'image_count': 271}, {'id': 376, 'image_count': 2}, {'id': 377, 'image_count': 103}, {'id': 378, 'image_count': 1938}, {'id': 379, 'image_count': 574}, {'id': 380, 'image_count': 120}, {'id': 381, 'image_count': 2}, {'id': 382, 'image_count': 2}, {'id': 383, 'image_count': 13}, {'id': 384, 'image_count': 29}, {'id': 385, 'image_count': 1710}, {'id': 386, 'image_count': 66}, {'id': 387, 'image_count': 1008}, {'id': 388, 'image_count': 1}, {'id': 389, 'image_count': 3}, {'id': 390, 'image_count': 1942}, {'id': 391, 'image_count': 19}, {'id': 392, 'image_count': 1488}, {'id': 393, 'image_count': 46}, {'id': 394, 'image_count': 106}, {'id': 395, 'image_count': 115}, {'id': 396, 'image_count': 19}, {'id': 397, 'image_count': 2}, {'id': 398, 'image_count': 1}, {'id': 399, 'image_count': 28}, {'id': 400, 'image_count': 9}, {'id': 401, 'image_count': 192}, {'id': 402, 'image_count': 12}, {'id': 403, 'image_count': 21}, {'id': 404, 'image_count': 247}, {'id': 405, 'image_count': 6}, {'id': 406, 'image_count': 64}, {'id': 407, 'image_count': 7}, {'id': 408, 'image_count': 40}, {'id': 409, 'image_count': 542}, {'id': 410, 'image_count': 2}, {'id': 411, 'image_count': 1898}, {'id': 412, 'image_count': 36}, {'id': 413, 'image_count': 4}, {'id': 414, 'image_count': 1}, {'id': 415, 'image_count': 191}, {'id': 416, 'image_count': 6}, {'id': 417, 'image_count': 41}, {'id': 418, 'image_count': 39}, {'id': 419, 'image_count': 46}, {'id': 420, 'image_count': 1}, {'id': 421, 'image_count': 1451}, {'id': 422, 'image_count': 1878}, {'id': 423, 'image_count': 11}, {'id': 424, 'image_count': 82}, {'id': 425, 'image_count': 18}, {'id': 426, 'image_count': 1}, {'id': 427, 'image_count': 7}, {'id': 428, 'image_count': 3}, {'id': 429, 'image_count': 575}, {'id': 430, 'image_count': 1907}, {'id': 431, 'image_count': 8}, {'id': 432, 'image_count': 4}, {'id': 433, 'image_count': 32}, {'id': 434, 'image_count': 11}, {'id': 435, 'image_count': 4}, {'id': 436, 'image_count': 54}, {'id': 437, 'image_count': 202}, {'id': 438, 'image_count': 32}, {'id': 439, 'image_count': 3}, {'id': 440, 'image_count': 130}, {'id': 441, 'image_count': 119}, {'id': 442, 'image_count': 141}, {'id': 443, 'image_count': 29}, {'id': 444, 'image_count': 525}, {'id': 445, 'image_count': 1323}, {'id': 446, 'image_count': 2}, {'id': 447, 'image_count': 113}, {'id': 448, 'image_count': 16}, {'id': 449, 'image_count': 7}, {'id': 450, 'image_count': 35}, {'id': 451, 'image_count': 1908}, {'id': 452, 'image_count': 353}, {'id': 453, 'image_count': 18}, {'id': 454, 'image_count': 14}, {'id': 455, 'image_count': 77}, {'id': 456, 'image_count': 8}, {'id': 457, 'image_count': 37}, {'id': 458, 'image_count': 1}, {'id': 459, 'image_count': 346}, {'id': 460, 'image_count': 19}, {'id': 461, 'image_count': 1779}, {'id': 462, 'image_count': 23}, {'id': 463, 'image_count': 25}, {'id': 464, 'image_count': 67}, {'id': 465, 'image_count': 19}, {'id': 466, 'image_count': 28}, {'id': 467, 'image_count': 4}, {'id': 468, 'image_count': 27}, {'id': 469, 'image_count': 1861}, {'id': 470, 'image_count': 11}, {'id': 471, 'image_count': 13}, {'id': 472, 'image_count': 13}, {'id': 473, 'image_count': 32}, {'id': 474, 'image_count': 1767}, {'id': 475, 'image_count': 42}, {'id': 476, 'image_count': 17}, {'id': 477, 'image_count': 128}, {'id': 478, 'image_count': 1}, {'id': 479, 'image_count': 9}, {'id': 480, 'image_count': 10}, {'id': 481, 'image_count': 4}, {'id': 482, 'image_count': 9}, {'id': 483, 'image_count': 18}, {'id': 484, 'image_count': 41}, {'id': 485, 'image_count': 28}, {'id': 486, 'image_count': 3}, {'id': 487, 'image_count': 65}, {'id': 488, 'image_count': 9}, {'id': 489, 'image_count': 23}, {'id': 490, 'image_count': 24}, {'id': 491, 'image_count': 1}, {'id': 492, 'image_count': 2}, {'id': 493, 'image_count': 59}, {'id': 494, 'image_count': 48}, {'id': 495, 'image_count': 17}, {'id': 496, 'image_count': 1877}, {'id': 497, 'image_count': 18}, {'id': 498, 'image_count': 1920}, {'id': 499, 'image_count': 50}, {'id': 500, 'image_count': 1890}, {'id': 501, 'image_count': 99}, {'id': 502, 'image_count': 1530}, {'id': 503, 'image_count': 3}, {'id': 504, 'image_count': 11}, {'id': 505, 'image_count': 19}, {'id': 506, 'image_count': 3}, {'id': 507, 'image_count': 63}, {'id': 508, 'image_count': 5}, {'id': 509, 'image_count': 6}, {'id': 510, 'image_count': 233}, {'id': 511, 'image_count': 54}, {'id': 512, 'image_count': 36}, {'id': 513, 'image_count': 10}, {'id': 514, 'image_count': 124}, {'id': 515, 'image_count': 101}, {'id': 516, 'image_count': 3}, {'id': 517, 'image_count': 363}, {'id': 518, 'image_count': 3}, {'id': 519, 'image_count': 30}, {'id': 520, 'image_count': 18}, {'id': 521, 'image_count': 199}, {'id': 522, 'image_count': 97}, {'id': 523, 'image_count': 32}, {'id': 524, 'image_count': 121}, {'id': 525, 'image_count': 16}, {'id': 526, 'image_count': 12}, {'id': 527, 'image_count': 2}, {'id': 528, 'image_count': 214}, {'id': 529, 'image_count': 48}, {'id': 530, 'image_count': 26}, {'id': 531, 'image_count': 13}, {'id': 532, 'image_count': 4}, {'id': 533, 'image_count': 11}, {'id': 534, 'image_count': 123}, {'id': 535, 'image_count': 7}, {'id': 536, 'image_count': 200}, {'id': 537, 'image_count': 91}, {'id': 538, 'image_count': 9}, {'id': 539, 'image_count': 72}, {'id': 540, 'image_count': 1886}, {'id': 541, 'image_count': 4}, {'id': 542, 'image_count': 1}, {'id': 543, 'image_count': 1}, {'id': 544, 'image_count': 1932}, {'id': 545, 'image_count': 4}, {'id': 546, 'image_count': 56}, {'id': 547, 'image_count': 854}, {'id': 548, 'image_count': 755}, {'id': 549, 'image_count': 1843}, {'id': 550, 'image_count': 96}, {'id': 551, 'image_count': 7}, {'id': 552, 'image_count': 74}, {'id': 553, 'image_count': 66}, {'id': 554, 'image_count': 57}, {'id': 555, 'image_count': 44}, {'id': 556, 'image_count': 1905}, {'id': 557, 'image_count': 4}, {'id': 558, 'image_count': 90}, {'id': 559, 'image_count': 1635}, {'id': 560, 'image_count': 8}, {'id': 561, 'image_count': 5}, {'id': 562, 'image_count': 50}, {'id': 563, 'image_count': 545}, {'id': 564, 'image_count': 20}, {'id': 565, 'image_count': 193}, {'id': 566, 'image_count': 285}, {'id': 567, 'image_count': 3}, {'id': 568, 'image_count': 1}, {'id': 569, 'image_count': 1904}, {'id': 570, 'image_count': 294}, {'id': 571, 'image_count': 3}, {'id': 572, 'image_count': 5}, {'id': 573, 'image_count': 24}, {'id': 574, 'image_count': 2}, {'id': 575, 'image_count': 2}, {'id': 576, 'image_count': 16}, {'id': 577, 'image_count': 8}, {'id': 578, 'image_count': 154}, {'id': 579, 'image_count': 66}, {'id': 580, 'image_count': 1}, {'id': 581, 'image_count': 24}, {'id': 582, 'image_count': 1}, {'id': 583, 'image_count': 4}, {'id': 584, 'image_count': 75}, {'id': 585, 'image_count': 6}, {'id': 586, 'image_count': 126}, {'id': 587, 'image_count': 24}, {'id': 588, 'image_count': 22}, {'id': 589, 'image_count': 1872}, {'id': 590, 'image_count': 16}, {'id': 591, 'image_count': 423}, {'id': 592, 'image_count': 1927}, {'id': 593, 'image_count': 38}, {'id': 594, 'image_count': 3}, {'id': 595, 'image_count': 1945}, {'id': 596, 'image_count': 35}, {'id': 597, 'image_count': 1}, {'id': 598, 'image_count': 13}, {'id': 599, 'image_count': 9}, {'id': 600, 'image_count': 14}, {'id': 601, 'image_count': 37}, {'id': 602, 'image_count': 3}, {'id': 603, 'image_count': 4}, {'id': 604, 'image_count': 100}, {'id': 605, 'image_count': 195}, {'id': 606, 'image_count': 1}, {'id': 607, 'image_count': 12}, {'id': 608, 'image_count': 24}, {'id': 609, 'image_count': 489}, {'id': 610, 'image_count': 10}, {'id': 611, 'image_count': 1689}, {'id': 612, 'image_count': 42}, {'id': 613, 'image_count': 81}, {'id': 614, 'image_count': 894}, {'id': 615, 'image_count': 1868}, {'id': 616, 'image_count': 7}, {'id': 617, 'image_count': 1567}, {'id': 618, 'image_count': 10}, {'id': 619, 'image_count': 8}, {'id': 620, 'image_count': 7}, {'id': 621, 'image_count': 629}, {'id': 622, 'image_count': 89}, {'id': 623, 'image_count': 15}, {'id': 624, 'image_count': 134}, {'id': 625, 'image_count': 4}, {'id': 626, 'image_count': 1802}, {'id': 627, 'image_count': 595}, {'id': 628, 'image_count': 1210}, {'id': 629, 'image_count': 48}, {'id': 630, 'image_count': 418}, {'id': 631, 'image_count': 1846}, {'id': 632, 'image_count': 5}, {'id': 633, 'image_count': 221}, {'id': 634, 'image_count': 10}, {'id': 635, 'image_count': 7}, {'id': 636, 'image_count': 76}, {'id': 637, 'image_count': 22}, {'id': 638, 'image_count': 10}, {'id': 639, 'image_count': 341}, {'id': 640, 'image_count': 1}, {'id': 641, 'image_count': 705}, {'id': 642, 'image_count': 1900}, {'id': 643, 'image_count': 188}, {'id': 644, 'image_count': 227}, {'id': 645, 'image_count': 861}, {'id': 646, 'image_count': 6}, {'id': 647, 'image_count': 115}, {'id': 648, 'image_count': 5}, {'id': 649, 'image_count': 43}, {'id': 650, 'image_count': 14}, {'id': 651, 'image_count': 6}, {'id': 652, 'image_count': 15}, {'id': 653, 'image_count': 1167}, {'id': 654, 'image_count': 15}, {'id': 655, 'image_count': 994}, {'id': 656, 'image_count': 28}, {'id': 657, 'image_count': 2}, {'id': 658, 'image_count': 338}, {'id': 659, 'image_count': 334}, {'id': 660, 'image_count': 15}, {'id': 661, 'image_count': 102}, {'id': 662, 'image_count': 1}, {'id': 663, 'image_count': 8}, {'id': 664, 'image_count': 1}, {'id': 665, 'image_count': 1}, {'id': 666, 'image_count': 28}, {'id': 667, 'image_count': 91}, {'id': 668, 'image_count': 260}, {'id': 669, 'image_count': 131}, {'id': 670, 'image_count': 128}, {'id': 671, 'image_count': 3}, {'id': 672, 'image_count': 10}, {'id': 673, 'image_count': 39}, {'id': 674, 'image_count': 2}, {'id': 675, 'image_count': 925}, {'id': 676, 'image_count': 354}, {'id': 677, 'image_count': 31}, {'id': 678, 'image_count': 10}, {'id': 679, 'image_count': 215}, {'id': 680, 'image_count': 71}, {'id': 681, 'image_count': 43}, {'id': 682, 'image_count': 28}, {'id': 683, 'image_count': 34}, {'id': 684, 'image_count': 16}, {'id': 685, 'image_count': 273}, {'id': 686, 'image_count': 2}, {'id': 687, 'image_count': 999}, {'id': 688, 'image_count': 4}, {'id': 689, 'image_count': 107}, {'id': 690, 'image_count': 2}, {'id': 691, 'image_count': 1}, {'id': 692, 'image_count': 454}, {'id': 693, 'image_count': 9}, {'id': 694, 'image_count': 1901}, {'id': 695, 'image_count': 61}, {'id': 696, 'image_count': 91}, {'id': 697, 'image_count': 46}, {'id': 698, 'image_count': 1402}, {'id': 699, 'image_count': 74}, {'id': 700, 'image_count': 421}, {'id': 701, 'image_count': 226}, {'id': 702, 'image_count': 10}, {'id': 703, 'image_count': 1720}, {'id': 704, 'image_count': 261}, {'id': 705, 'image_count': 1337}, {'id': 706, 'image_count': 293}, {'id': 707, 'image_count': 62}, {'id': 708, 'image_count': 814}, {'id': 709, 'image_count': 407}, {'id': 710, 'image_count': 6}, {'id': 711, 'image_count': 16}, {'id': 712, 'image_count': 7}, {'id': 713, 'image_count': 1791}, {'id': 714, 'image_count': 2}, {'id': 715, 'image_count': 1915}, {'id': 716, 'image_count': 1940}, {'id': 717, 'image_count': 13}, {'id': 718, 'image_count': 16}, {'id': 719, 'image_count': 448}, {'id': 720, 'image_count': 12}, {'id': 721, 'image_count': 18}, {'id': 722, 'image_count': 4}, {'id': 723, 'image_count': 71}, {'id': 724, 'image_count': 189}, {'id': 725, 'image_count': 74}, {'id': 726, 'image_count': 103}, {'id': 727, 'image_count': 3}, {'id': 728, 'image_count': 110}, {'id': 729, 'image_count': 5}, {'id': 730, 'image_count': 9}, {'id': 731, 'image_count': 15}, {'id': 732, 'image_count': 25}, {'id': 733, 'image_count': 7}, {'id': 734, 'image_count': 647}, {'id': 735, 'image_count': 824}, {'id': 736, 'image_count': 100}, {'id': 737, 'image_count': 47}, {'id': 738, 'image_count': 121}, {'id': 739, 'image_count': 731}, {'id': 740, 'image_count': 73}, {'id': 741, 'image_count': 49}, {'id': 742, 'image_count': 23}, {'id': 743, 'image_count': 4}, {'id': 744, 'image_count': 62}, {'id': 745, 'image_count': 118}, {'id': 746, 'image_count': 99}, {'id': 747, 'image_count': 40}, {'id': 748, 'image_count': 1036}, {'id': 749, 'image_count': 105}, {'id': 750, 'image_count': 21}, {'id': 751, 'image_count': 229}, {'id': 752, 'image_count': 7}, {'id': 753, 'image_count': 72}, {'id': 754, 'image_count': 9}, {'id': 755, 'image_count': 10}, {'id': 756, 'image_count': 328}, {'id': 757, 'image_count': 468}, {'id': 758, 'image_count': 1}, {'id': 759, 'image_count': 2}, {'id': 760, 'image_count': 24}, {'id': 761, 'image_count': 11}, {'id': 762, 'image_count': 72}, {'id': 763, 'image_count': 17}, {'id': 764, 'image_count': 10}, {'id': 765, 'image_count': 17}, {'id': 766, 'image_count': 489}, {'id': 767, 'image_count': 47}, {'id': 768, 'image_count': 93}, {'id': 769, 'image_count': 1}, {'id': 770, 'image_count': 12}, {'id': 771, 'image_count': 228}, {'id': 772, 'image_count': 5}, {'id': 773, 'image_count': 76}, {'id': 774, 'image_count': 71}, {'id': 775, 'image_count': 30}, {'id': 776, 'image_count': 109}, {'id': 777, 'image_count': 14}, {'id': 778, 'image_count': 1}, {'id': 779, 'image_count': 8}, {'id': 780, 'image_count': 26}, {'id': 781, 'image_count': 339}, {'id': 782, 'image_count': 153}, {'id': 783, 'image_count': 2}, {'id': 784, 'image_count': 3}, {'id': 785, 'image_count': 8}, {'id': 786, 'image_count': 47}, {'id': 787, 'image_count': 8}, {'id': 788, 'image_count': 6}, {'id': 789, 'image_count': 116}, {'id': 790, 'image_count': 69}, {'id': 791, 'image_count': 13}, {'id': 792, 'image_count': 6}, {'id': 793, 'image_count': 1928}, {'id': 794, 'image_count': 79}, {'id': 795, 'image_count': 14}, {'id': 796, 'image_count': 7}, {'id': 797, 'image_count': 20}, {'id': 798, 'image_count': 114}, {'id': 799, 'image_count': 221}, {'id': 800, 'image_count': 502}, {'id': 801, 'image_count': 62}, {'id': 802, 'image_count': 87}, {'id': 803, 'image_count': 4}, {'id': 804, 'image_count': 1912}, {'id': 805, 'image_count': 7}, {'id': 806, 'image_count': 186}, {'id': 807, 'image_count': 18}, {'id': 808, 'image_count': 4}, {'id': 809, 'image_count': 3}, {'id': 810, 'image_count': 7}, {'id': 811, 'image_count': 1413}, {'id': 812, 'image_count': 7}, {'id': 813, 'image_count': 12}, {'id': 814, 'image_count': 248}, {'id': 815, 'image_count': 4}, {'id': 816, 'image_count': 1881}, {'id': 817, 'image_count': 529}, {'id': 818, 'image_count': 1932}, {'id': 819, 'image_count': 50}, {'id': 820, 'image_count': 3}, {'id': 821, 'image_count': 28}, {'id': 822, 'image_count': 10}, {'id': 823, 'image_count': 5}, {'id': 824, 'image_count': 5}, {'id': 825, 'image_count': 18}, {'id': 826, 'image_count': 14}, {'id': 827, 'image_count': 1890}, {'id': 828, 'image_count': 660}, {'id': 829, 'image_count': 8}, {'id': 830, 'image_count': 25}, {'id': 831, 'image_count': 10}, {'id': 832, 'image_count': 218}, {'id': 833, 'image_count': 36}, {'id': 834, 'image_count': 16}, {'id': 835, 'image_count': 808}, {'id': 836, 'image_count': 479}, {'id': 837, 'image_count': 1404}, {'id': 838, 'image_count': 307}, {'id': 839, 'image_count': 57}, {'id': 840, 'image_count': 28}, {'id': 841, 'image_count': 80}, {'id': 842, 'image_count': 11}, {'id': 843, 'image_count': 92}, {'id': 844, 'image_count': 20}, {'id': 845, 'image_count': 194}, {'id': 846, 'image_count': 23}, {'id': 847, 'image_count': 52}, {'id': 848, 'image_count': 673}, {'id': 849, 'image_count': 2}, {'id': 850, 'image_count': 2}, {'id': 851, 'image_count': 1}, {'id': 852, 'image_count': 2}, {'id': 853, 'image_count': 8}, {'id': 854, 'image_count': 80}, {'id': 855, 'image_count': 3}, {'id': 856, 'image_count': 3}, {'id': 857, 'image_count': 15}, {'id': 858, 'image_count': 2}, {'id': 859, 'image_count': 10}, {'id': 860, 'image_count': 386}, {'id': 861, 'image_count': 65}, {'id': 862, 'image_count': 3}, {'id': 863, 'image_count': 35}, {'id': 864, 'image_count': 5}, {'id': 865, 'image_count': 180}, {'id': 866, 'image_count': 99}, {'id': 867, 'image_count': 49}, {'id': 868, 'image_count': 28}, {'id': 869, 'image_count': 1}, {'id': 870, 'image_count': 52}, {'id': 871, 'image_count': 36}, {'id': 872, 'image_count': 70}, {'id': 873, 'image_count': 6}, {'id': 874, 'image_count': 29}, {'id': 875, 'image_count': 24}, {'id': 876, 'image_count': 1115}, {'id': 877, 'image_count': 61}, {'id': 878, 'image_count': 18}, {'id': 879, 'image_count': 18}, {'id': 880, 'image_count': 665}, {'id': 881, 'image_count': 1096}, {'id': 882, 'image_count': 29}, {'id': 883, 'image_count': 8}, {'id': 884, 'image_count': 14}, {'id': 885, 'image_count': 1622}, {'id': 886, 'image_count': 2}, {'id': 887, 'image_count': 3}, {'id': 888, 'image_count': 32}, {'id': 889, 'image_count': 55}, {'id': 890, 'image_count': 1}, {'id': 891, 'image_count': 10}, {'id': 892, 'image_count': 10}, {'id': 893, 'image_count': 47}, {'id': 894, 'image_count': 3}, {'id': 895, 'image_count': 29}, {'id': 896, 'image_count': 342}, {'id': 897, 'image_count': 25}, {'id': 898, 'image_count': 1469}, {'id': 899, 'image_count': 521}, {'id': 900, 'image_count': 347}, {'id': 901, 'image_count': 35}, {'id': 902, 'image_count': 7}, {'id': 903, 'image_count': 207}, {'id': 904, 'image_count': 108}, {'id': 905, 'image_count': 2}, {'id': 906, 'image_count': 34}, {'id': 907, 'image_count': 12}, {'id': 908, 'image_count': 10}, {'id': 909, 'image_count': 13}, {'id': 910, 'image_count': 361}, {'id': 911, 'image_count': 1023}, {'id': 912, 'image_count': 782}, {'id': 913, 'image_count': 2}, {'id': 914, 'image_count': 5}, {'id': 915, 'image_count': 247}, {'id': 916, 'image_count': 221}, {'id': 917, 'image_count': 4}, {'id': 918, 'image_count': 8}, {'id': 919, 'image_count': 158}, {'id': 920, 'image_count': 3}, {'id': 921, 'image_count': 752}, {'id': 922, 'image_count': 64}, {'id': 923, 'image_count': 707}, {'id': 924, 'image_count': 143}, {'id': 925, 'image_count': 1}, {'id': 926, 'image_count': 49}, {'id': 927, 'image_count': 126}, {'id': 928, 'image_count': 76}, {'id': 929, 'image_count': 11}, {'id': 930, 'image_count': 11}, {'id': 931, 'image_count': 4}, {'id': 932, 'image_count': 39}, {'id': 933, 'image_count': 11}, {'id': 934, 'image_count': 13}, {'id': 935, 'image_count': 91}, {'id': 936, 'image_count': 14}, {'id': 937, 'image_count': 5}, {'id': 938, 'image_count': 3}, {'id': 939, 'image_count': 10}, {'id': 940, 'image_count': 18}, {'id': 941, 'image_count': 9}, {'id': 942, 'image_count': 6}, {'id': 943, 'image_count': 951}, {'id': 944, 'image_count': 2}, {'id': 945, 'image_count': 1}, {'id': 946, 'image_count': 19}, {'id': 947, 'image_count': 1942}, {'id': 948, 'image_count': 1916}, {'id': 949, 'image_count': 139}, {'id': 950, 'image_count': 43}, {'id': 951, 'image_count': 1969}, {'id': 952, 'image_count': 5}, {'id': 953, 'image_count': 134}, {'id': 954, 'image_count': 74}, {'id': 955, 'image_count': 381}, {'id': 956, 'image_count': 1}, {'id': 957, 'image_count': 381}, {'id': 958, 'image_count': 6}, {'id': 959, 'image_count': 1826}, {'id': 960, 'image_count': 28}, {'id': 961, 'image_count': 1635}, {'id': 962, 'image_count': 1967}, {'id': 963, 'image_count': 16}, {'id': 964, 'image_count': 1926}, {'id': 965, 'image_count': 1789}, {'id': 966, 'image_count': 401}, {'id': 967, 'image_count': 1968}, {'id': 968, 'image_count': 1167}, {'id': 969, 'image_count': 1}, {'id': 970, 'image_count': 56}, {'id': 971, 'image_count': 17}, {'id': 972, 'image_count': 1}, {'id': 973, 'image_count': 58}, {'id': 974, 'image_count': 9}, {'id': 975, 'image_count': 8}, {'id': 976, 'image_count': 1124}, {'id': 977, 'image_count': 31}, {'id': 978, 'image_count': 16}, {'id': 979, 'image_count': 491}, {'id': 980, 'image_count': 432}, {'id': 981, 'image_count': 1945}, {'id': 982, 'image_count': 1899}, {'id': 983, 'image_count': 5}, {'id': 984, 'image_count': 28}, {'id': 985, 'image_count': 7}, {'id': 986, 'image_count': 146}, {'id': 987, 'image_count': 1}, {'id': 988, 'image_count': 25}, {'id': 989, 'image_count': 22}, {'id': 990, 'image_count': 1}, {'id': 991, 'image_count': 10}, {'id': 992, 'image_count': 9}, {'id': 993, 'image_count': 308}, {'id': 994, 'image_count': 4}, {'id': 995, 'image_count': 1969}, {'id': 996, 'image_count': 45}, {'id': 997, 'image_count': 12}, {'id': 998, 'image_count': 1}, {'id': 999, 'image_count': 85}, {'id': 1000, 'image_count': 1127}, {'id': 1001, 'image_count': 11}, {'id': 1002, 'image_count': 60}, {'id': 1003, 'image_count': 1}, {'id': 1004, 'image_count': 16}, {'id': 1005, 'image_count': 1}, {'id': 1006, 'image_count': 65}, {'id': 1007, 'image_count': 13}, {'id': 1008, 'image_count': 655}, {'id': 1009, 'image_count': 51}, {'id': 1010, 'image_count': 1}, {'id': 1011, 'image_count': 673}, {'id': 1012, 'image_count': 5}, {'id': 1013, 'image_count': 36}, {'id': 1014, 'image_count': 54}, {'id': 1015, 'image_count': 5}, {'id': 1016, 'image_count': 8}, {'id': 1017, 'image_count': 305}, {'id': 1018, 'image_count': 297}, {'id': 1019, 'image_count': 1053}, {'id': 1020, 'image_count': 223}, {'id': 1021, 'image_count': 1037}, {'id': 1022, 'image_count': 63}, {'id': 1023, 'image_count': 1881}, {'id': 1024, 'image_count': 507}, {'id': 1025, 'image_count': 333}, {'id': 1026, 'image_count': 1911}, {'id': 1027, 'image_count': 1765}, {'id': 1028, 'image_count': 1}, {'id': 1029, 'image_count': 5}, {'id': 1030, 'image_count': 1}, {'id': 1031, 'image_count': 9}, {'id': 1032, 'image_count': 2}, {'id': 1033, 'image_count': 151}, {'id': 1034, 'image_count': 82}, {'id': 1035, 'image_count': 1931}, {'id': 1036, 'image_count': 41}, {'id': 1037, 'image_count': 1895}, {'id': 1038, 'image_count': 24}, {'id': 1039, 'image_count': 22}, {'id': 1040, 'image_count': 35}, {'id': 1041, 'image_count': 69}, {'id': 1042, 'image_count': 962}, {'id': 1043, 'image_count': 588}, {'id': 1044, 'image_count': 21}, {'id': 1045, 'image_count': 825}, {'id': 1046, 'image_count': 52}, {'id': 1047, 'image_count': 5}, {'id': 1048, 'image_count': 5}, {'id': 1049, 'image_count': 5}, {'id': 1050, 'image_count': 1860}, {'id': 1051, 'image_count': 56}, {'id': 1052, 'image_count': 1582}, {'id': 1053, 'image_count': 7}, {'id': 1054, 'image_count': 2}, {'id': 1055, 'image_count': 1562}, {'id': 1056, 'image_count': 1885}, {'id': 1057, 'image_count': 1}, {'id': 1058, 'image_count': 5}, {'id': 1059, 'image_count': 137}, {'id': 1060, 'image_count': 1094}, {'id': 1061, 'image_count': 134}, {'id': 1062, 'image_count': 29}, {'id': 1063, 'image_count': 22}, {'id': 1064, 'image_count': 522}, {'id': 1065, 'image_count': 50}, {'id': 1066, 'image_count': 68}, {'id': 1067, 'image_count': 16}, {'id': 1068, 'image_count': 40}, {'id': 1069, 'image_count': 35}, {'id': 1070, 'image_count': 135}, {'id': 1071, 'image_count': 1413}, {'id': 1072, 'image_count': 772}, {'id': 1073, 'image_count': 50}, {'id': 1074, 'image_count': 1015}, {'id': 1075, 'image_count': 1}, {'id': 1076, 'image_count': 65}, {'id': 1077, 'image_count': 1900}, {'id': 1078, 'image_count': 1302}, {'id': 1079, 'image_count': 1977}, {'id': 1080, 'image_count': 2}, {'id': 1081, 'image_count': 29}, {'id': 1082, 'image_count': 36}, {'id': 1083, 'image_count': 138}, {'id': 1084, 'image_count': 4}, {'id': 1085, 'image_count': 67}, {'id': 1086, 'image_count': 26}, {'id': 1087, 'image_count': 25}, {'id': 1088, 'image_count': 33}, {'id': 1089, 'image_count': 37}, {'id': 1090, 'image_count': 50}, {'id': 1091, 'image_count': 270}, {'id': 1092, 'image_count': 12}, {'id': 1093, 'image_count': 316}, {'id': 1094, 'image_count': 41}, {'id': 1095, 'image_count': 224}, {'id': 1096, 'image_count': 105}, {'id': 1097, 'image_count': 1925}, {'id': 1098, 'image_count': 1021}, {'id': 1099, 'image_count': 1213}, {'id': 1100, 'image_count': 172}, {'id': 1101, 'image_count': 28}, {'id': 1102, 'image_count': 745}, {'id': 1103, 'image_count': 187}, {'id': 1104, 'image_count': 147}, {'id': 1105, 'image_count': 136}, {'id': 1106, 'image_count': 34}, {'id': 1107, 'image_count': 41}, {'id': 1108, 'image_count': 636}, {'id': 1109, 'image_count': 570}, {'id': 1110, 'image_count': 1149}, {'id': 1111, 'image_count': 61}, {'id': 1112, 'image_count': 1890}, {'id': 1113, 'image_count': 18}, {'id': 1114, 'image_count': 143}, {'id': 1115, 'image_count': 1517}, {'id': 1116, 'image_count': 7}, {'id': 1117, 'image_count': 943}, {'id': 1118, 'image_count': 6}, {'id': 1119, 'image_count': 1}, {'id': 1120, 'image_count': 11}, {'id': 1121, 'image_count': 101}, {'id': 1122, 'image_count': 1909}, {'id': 1123, 'image_count': 800}, {'id': 1124, 'image_count': 1}, {'id': 1125, 'image_count': 44}, {'id': 1126, 'image_count': 3}, {'id': 1127, 'image_count': 44}, {'id': 1128, 'image_count': 31}, {'id': 1129, 'image_count': 7}, {'id': 1130, 'image_count': 20}, {'id': 1131, 'image_count': 11}, {'id': 1132, 'image_count': 13}, {'id': 1133, 'image_count': 1924}, {'id': 1134, 'image_count': 113}, {'id': 1135, 'image_count': 2}, {'id': 1136, 'image_count': 139}, {'id': 1137, 'image_count': 12}, {'id': 1138, 'image_count': 37}, {'id': 1139, 'image_count': 1866}, {'id': 1140, 'image_count': 47}, {'id': 1141, 'image_count': 1468}, {'id': 1142, 'image_count': 729}, {'id': 1143, 'image_count': 24}, {'id': 1144, 'image_count': 1}, {'id': 1145, 'image_count': 10}, {'id': 1146, 'image_count': 3}, {'id': 1147, 'image_count': 14}, {'id': 1148, 'image_count': 4}, {'id': 1149, 'image_count': 29}, {'id': 1150, 'image_count': 4}, {'id': 1151, 'image_count': 70}, {'id': 1152, 'image_count': 46}, {'id': 1153, 'image_count': 14}, {'id': 1154, 'image_count': 48}, {'id': 1155, 'image_count': 1855}, {'id': 1156, 'image_count': 113}, {'id': 1157, 'image_count': 1}, {'id': 1158, 'image_count': 1}, {'id': 1159, 'image_count': 10}, {'id': 1160, 'image_count': 54}, {'id': 1161, 'image_count': 1923}, {'id': 1162, 'image_count': 630}, {'id': 1163, 'image_count': 31}, {'id': 1164, 'image_count': 69}, {'id': 1165, 'image_count': 7}, {'id': 1166, 'image_count': 11}, {'id': 1167, 'image_count': 1}, {'id': 1168, 'image_count': 30}, {'id': 1169, 'image_count': 50}, {'id': 1170, 'image_count': 45}, {'id': 1171, 'image_count': 28}, {'id': 1172, 'image_count': 114}, {'id': 1173, 'image_count': 193}, {'id': 1174, 'image_count': 21}, {'id': 1175, 'image_count': 91}, {'id': 1176, 'image_count': 31}, {'id': 1177, 'image_count': 1469}, {'id': 1178, 'image_count': 1924}, {'id': 1179, 'image_count': 87}, {'id': 1180, 'image_count': 77}, {'id': 1181, 'image_count': 11}, {'id': 1182, 'image_count': 47}, {'id': 1183, 'image_count': 21}, {'id': 1184, 'image_count': 47}, {'id': 1185, 'image_count': 70}, {'id': 1186, 'image_count': 1838}, {'id': 1187, 'image_count': 19}, {'id': 1188, 'image_count': 531}, {'id': 1189, 'image_count': 11}, {'id': 1190, 'image_count': 941}, {'id': 1191, 'image_count': 113}, {'id': 1192, 'image_count': 26}, {'id': 1193, 'image_count': 5}, {'id': 1194, 'image_count': 56}, {'id': 1195, 'image_count': 73}, {'id': 1196, 'image_count': 32}, {'id': 1197, 'image_count': 128}, {'id': 1198, 'image_count': 623}, {'id': 1199, 'image_count': 12}, {'id': 1200, 'image_count': 52}, {'id': 1201, 'image_count': 11}, {'id': 1202, 'image_count': 1674}, {'id': 1203, 'image_count': 81}] # noqa # fmt: on ================================================ FILE: detectron2/detectron2/data/datasets/pascal_voc.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import os import xml.etree.ElementTree as ET from typing import List, Tuple, Union from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.structures import BoxMode from detectron2.utils.file_io import PathManager __all__ = ["load_voc_instances", "register_pascal_voc"] # fmt: off CLASS_NAMES = ( "aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor" ) # fmt: on def load_voc_instances(dirname: str, split: str, class_names: Union[List[str], Tuple[str, ...]]): """ Load Pascal VOC detection annotations to Detectron2 format. Args: dirname: Contain "Annotations", "ImageSets", "JPEGImages" split (str): one of "train", "test", "val", "trainval" class_names: list or tuple of class names """ with PathManager.open(os.path.join(dirname, "ImageSets", "Main", split + ".txt")) as f: fileids = np.loadtxt(f, dtype=np.str) # Needs to read many small annotation files. Makes sense at local annotation_dirname = PathManager.get_local_path(os.path.join(dirname, "Annotations/")) dicts = [] for fileid in fileids: anno_file = os.path.join(annotation_dirname, fileid + ".xml") jpeg_file = os.path.join(dirname, "JPEGImages", fileid + ".jpg") with PathManager.open(anno_file) as f: tree = ET.parse(f) r = { "file_name": jpeg_file, "image_id": fileid, "height": int(tree.findall("./size/height")[0].text), "width": int(tree.findall("./size/width")[0].text), } instances = [] for obj in tree.findall("object"): cls = obj.find("name").text # We include "difficult" samples in training. # Based on limited experiments, they don't hurt accuracy. # difficult = int(obj.find("difficult").text) # if difficult == 1: # continue bbox = obj.find("bndbox") bbox = [float(bbox.find(x).text) for x in ["xmin", "ymin", "xmax", "ymax"]] # Original annotations are integers in the range [1, W or H] # Assuming they mean 1-based pixel indices (inclusive), # a box with annotation (xmin=1, xmax=W) covers the whole image. # In coordinate space this is represented by (xmin=0, xmax=W) bbox[0] -= 1.0 bbox[1] -= 1.0 instances.append( {"category_id": class_names.index(cls), "bbox": bbox, "bbox_mode": BoxMode.XYXY_ABS} ) r["annotations"] = instances dicts.append(r) return dicts def register_pascal_voc(name, dirname, split, year, class_names=CLASS_NAMES): DatasetCatalog.register(name, lambda: load_voc_instances(dirname, split, class_names)) MetadataCatalog.get(name).set( thing_classes=list(class_names), dirname=dirname, year=year, split=split ) ================================================ FILE: detectron2/detectron2/data/datasets/register_coco.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .coco import register_coco_instances # noqa from .coco_panoptic import register_coco_panoptic_separated # noqa ================================================ FILE: detectron2/detectron2/data/detection_utils.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. """ Common data processing utilities that are used in a typical object detection data pipeline. """ import logging import numpy as np from typing import List, Union import pycocotools.mask as mask_util import torch from PIL import Image from detectron2.structures import ( BitMasks, Boxes, BoxMode, Instances, Keypoints, PolygonMasks, RotatedBoxes, polygons_to_bitmask, ) from detectron2.utils.file_io import PathManager from . import transforms as T from .catalog import MetadataCatalog __all__ = [ "SizeMismatchError", "convert_image_to_rgb", "check_image_size", "transform_proposals", "transform_instance_annotations", "annotations_to_instances", "annotations_to_instances_rotated", "build_augmentation", "build_transform_gen", "create_keypoint_hflip_indices", "filter_empty_instances", "read_image", ] class SizeMismatchError(ValueError): """ When loaded image has difference width/height compared with annotation. """ # https://en.wikipedia.org/wiki/YUV#SDTV_with_BT.601 _M_RGB2YUV = [[0.299, 0.587, 0.114], [-0.14713, -0.28886, 0.436], [0.615, -0.51499, -0.10001]] _M_YUV2RGB = [[1.0, 0.0, 1.13983], [1.0, -0.39465, -0.58060], [1.0, 2.03211, 0.0]] # https://www.exiv2.org/tags.html _EXIF_ORIENT = 274 # exif 'Orientation' tag def convert_PIL_to_numpy(image, format): """ Convert PIL image to numpy array of target format. Args: image (PIL.Image): a PIL image format (str): the format of output image Returns: (np.ndarray): also see `read_image` """ if format is not None: # PIL only supports RGB, so convert to RGB and flip channels over below conversion_format = format if format in ["BGR", "YUV-BT.601"]: conversion_format = "RGB" image = image.convert(conversion_format) image = np.asarray(image) # PIL squeezes out the channel dimension for "L", so make it HWC if format == "L": image = np.expand_dims(image, -1) # handle formats not supported by PIL elif format == "BGR": # flip channels if needed image = image[:, :, ::-1] elif format == "YUV-BT.601": image = image / 255.0 image = np.dot(image, np.array(_M_RGB2YUV).T) return image def convert_image_to_rgb(image, format): """ Convert an image from given format to RGB. Args: image (np.ndarray or Tensor): an HWC image format (str): the format of input image, also see `read_image` Returns: (np.ndarray): (H,W,3) RGB image in 0-255 range, can be either float or uint8 """ if isinstance(image, torch.Tensor): image = image.cpu().numpy() if format == "BGR": image = image[:, :, [2, 1, 0]] elif format == "YUV-BT.601": image = np.dot(image, np.array(_M_YUV2RGB).T) image = image * 255.0 else: if format == "L": image = image[:, :, 0] image = image.astype(np.uint8) image = np.asarray(Image.fromarray(image, mode=format).convert("RGB")) return image def _apply_exif_orientation(image): """ Applies the exif orientation correctly. This code exists per the bug: https://github.com/python-pillow/Pillow/issues/3973 with the function `ImageOps.exif_transpose`. The Pillow source raises errors with various methods, especially `tobytes` Function based on: https://github.com/wkentaro/labelme/blob/v4.5.4/labelme/utils/image.py#L59 https://github.com/python-pillow/Pillow/blob/7.1.2/src/PIL/ImageOps.py#L527 Args: image (PIL.Image): a PIL image Returns: (PIL.Image): the PIL image with exif orientation applied, if applicable """ if not hasattr(image, "getexif"): return image try: exif = image.getexif() except Exception: # https://github.com/facebookresearch/detectron2/issues/1885 exif = None if exif is None: return image orientation = exif.get(_EXIF_ORIENT) method = { 2: Image.FLIP_LEFT_RIGHT, 3: Image.ROTATE_180, 4: Image.FLIP_TOP_BOTTOM, 5: Image.TRANSPOSE, 6: Image.ROTATE_270, 7: Image.TRANSVERSE, 8: Image.ROTATE_90, }.get(orientation) if method is not None: return image.transpose(method) return image def read_image(file_name, format=None): """ Read an image into the given format. Will apply rotation and flipping if the image has such exif information. Args: file_name (str): image file path format (str): one of the supported image modes in PIL, or "BGR" or "YUV-BT.601". Returns: image (np.ndarray): an HWC image in the given format, which is 0-255, uint8 for supported image modes in PIL or "BGR"; float (0-1 for Y) for YUV-BT.601. """ with PathManager.open(file_name, "rb") as f: image = Image.open(f) # work around this bug: https://github.com/python-pillow/Pillow/issues/3973 image = _apply_exif_orientation(image) return convert_PIL_to_numpy(image, format) def check_image_size(dataset_dict, image): """ Raise an error if the image does not match the size specified in the dict. """ if "width" in dataset_dict or "height" in dataset_dict: image_wh = (image.shape[1], image.shape[0]) expected_wh = (dataset_dict["width"], dataset_dict["height"]) if not image_wh == expected_wh: raise SizeMismatchError( "Mismatched image shape{}, got {}, expect {}.".format( " for image " + dataset_dict["file_name"] if "file_name" in dataset_dict else "", image_wh, expected_wh, ) + " Please check the width/height in your annotation." ) # To ensure bbox always remap to original image size if "width" not in dataset_dict: dataset_dict["width"] = image.shape[1] if "height" not in dataset_dict: dataset_dict["height"] = image.shape[0] def transform_proposals(dataset_dict, image_shape, transforms, *, proposal_topk, min_box_size=0): """ Apply transformations to the proposals in dataset_dict, if any. Args: dataset_dict (dict): a dict read from the dataset, possibly contains fields "proposal_boxes", "proposal_objectness_logits", "proposal_bbox_mode" image_shape (tuple): height, width transforms (TransformList): proposal_topk (int): only keep top-K scoring proposals min_box_size (int): proposals with either side smaller than this threshold are removed The input dict is modified in-place, with abovementioned keys removed. A new key "proposals" will be added. Its value is an `Instances` object which contains the transformed proposals in its field "proposal_boxes" and "objectness_logits". """ if "proposal_boxes" in dataset_dict: # Transform proposal boxes boxes = transforms.apply_box( BoxMode.convert( dataset_dict.pop("proposal_boxes"), dataset_dict.pop("proposal_bbox_mode"), BoxMode.XYXY_ABS, ) ) boxes = Boxes(boxes) objectness_logits = torch.as_tensor( dataset_dict.pop("proposal_objectness_logits").astype("float32") ) boxes.clip(image_shape) keep = boxes.nonempty(threshold=min_box_size) boxes = boxes[keep] objectness_logits = objectness_logits[keep] proposals = Instances(image_shape) proposals.proposal_boxes = boxes[:proposal_topk] proposals.objectness_logits = objectness_logits[:proposal_topk] dataset_dict["proposals"] = proposals def transform_instance_annotations( annotation, transforms, image_size, *, keypoint_hflip_indices=None ): """ Apply transforms to box, segmentation and keypoints annotations of a single instance. It will use `transforms.apply_box` for the box, and `transforms.apply_coords` for segmentation polygons & keypoints. If you need anything more specially designed for each data structure, you'll need to implement your own version of this function or the transforms. Args: annotation (dict): dict of instance annotations for a single instance. It will be modified in-place. transforms (TransformList or list[Transform]): image_size (tuple): the height, width of the transformed image keypoint_hflip_indices (ndarray[int]): see `create_keypoint_hflip_indices`. Returns: dict: the same input dict with fields "bbox", "segmentation", "keypoints" transformed according to `transforms`. The "bbox_mode" field will be set to XYXY_ABS. """ if isinstance(transforms, (tuple, list)): transforms = T.TransformList(transforms) # bbox is 1d (per-instance bounding box) bbox = BoxMode.convert(annotation["bbox"], annotation["bbox_mode"], BoxMode.XYXY_ABS) # clip transformed bbox to image size bbox = transforms.apply_box(np.array([bbox]))[0].clip(min=0) annotation["bbox"] = np.minimum(bbox, list(image_size + image_size)[::-1]) annotation["bbox_mode"] = BoxMode.XYXY_ABS if "segmentation" in annotation: # each instance contains 1 or more polygons segm = annotation["segmentation"] if isinstance(segm, list): # polygons polygons = [np.asarray(p).reshape(-1, 2) for p in segm] annotation["segmentation"] = [ p.reshape(-1) for p in transforms.apply_polygons(polygons) ] elif isinstance(segm, dict): # RLE mask = mask_util.decode(segm) mask = transforms.apply_segmentation(mask) assert tuple(mask.shape[:2]) == image_size annotation["segmentation"] = mask else: raise ValueError( "Cannot transform segmentation of type '{}'!" "Supported types are: polygons as list[list[float] or ndarray]," " COCO-style RLE as a dict.".format(type(segm)) ) if "keypoints" in annotation: keypoints = transform_keypoint_annotations( annotation["keypoints"], transforms, image_size, keypoint_hflip_indices ) annotation["keypoints"] = keypoints return annotation def transform_keypoint_annotations(keypoints, transforms, image_size, keypoint_hflip_indices=None): """ Transform keypoint annotations of an image. If a keypoint is transformed out of image boundary, it will be marked "unlabeled" (visibility=0) Args: keypoints (list[float]): Nx3 float in Detectron2's Dataset format. Each point is represented by (x, y, visibility). transforms (TransformList): image_size (tuple): the height, width of the transformed image keypoint_hflip_indices (ndarray[int]): see `create_keypoint_hflip_indices`. When `transforms` includes horizontal flip, will use the index mapping to flip keypoints. """ # (N*3,) -> (N, 3) keypoints = np.asarray(keypoints, dtype="float64").reshape(-1, 3) keypoints_xy = transforms.apply_coords(keypoints[:, :2]) # Set all out-of-boundary points to "unlabeled" inside = (keypoints_xy >= np.array([0, 0])) & (keypoints_xy <= np.array(image_size[::-1])) inside = inside.all(axis=1) keypoints[:, :2] = keypoints_xy keypoints[:, 2][~inside] = 0 # This assumes that HorizFlipTransform is the only one that does flip do_hflip = sum(isinstance(t, T.HFlipTransform) for t in transforms.transforms) % 2 == 1 # Alternative way: check if probe points was horizontally flipped. # probe = np.asarray([[0.0, 0.0], [image_width, 0.0]]) # probe_aug = transforms.apply_coords(probe.copy()) # do_hflip = np.sign(probe[1][0] - probe[0][0]) != np.sign(probe_aug[1][0] - probe_aug[0][0]) # noqa # If flipped, swap each keypoint with its opposite-handed equivalent if do_hflip: if keypoint_hflip_indices is None: raise ValueError("Cannot flip keypoints without providing flip indices!") if len(keypoints) != len(keypoint_hflip_indices): raise ValueError( "Keypoint data has {} points, but metadata " "contains {} points!".format(len(keypoints), len(keypoint_hflip_indices)) ) keypoints = keypoints[np.asarray(keypoint_hflip_indices, dtype=np.int32), :] # Maintain COCO convention that if visibility == 0 (unlabeled), then x, y = 0 keypoints[keypoints[:, 2] == 0] = 0 return keypoints def annotations_to_instances(annos, image_size, mask_format="polygon"): """ Create an :class:`Instances` object used by the models, from instance annotations in the dataset dict. Args: annos (list[dict]): a list of instance annotations in one image, each element for one instance. image_size (tuple): height, width Returns: Instances: It will contain fields "gt_boxes", "gt_classes", "gt_masks", "gt_keypoints", if they can be obtained from `annos`. This is the format that builtin models expect. """ boxes = ( np.stack( [BoxMode.convert(obj["bbox"], obj["bbox_mode"], BoxMode.XYXY_ABS) for obj in annos] ) if len(annos) else np.zeros((0, 4)) ) target = Instances(image_size) target.gt_boxes = Boxes(boxes) classes = [int(obj["category_id"]) for obj in annos] classes = torch.tensor(classes, dtype=torch.int64) target.gt_classes = classes if len(annos) and "segmentation" in annos[0]: segms = [obj["segmentation"] for obj in annos] if mask_format == "polygon": try: masks = PolygonMasks(segms) except ValueError as e: raise ValueError( "Failed to use mask_format=='polygon' from the given annotations!" ) from e else: assert mask_format == "bitmask", mask_format masks = [] for segm in segms: if isinstance(segm, list): # polygon masks.append(polygons_to_bitmask(segm, *image_size)) elif isinstance(segm, dict): # COCO RLE masks.append(mask_util.decode(segm)) elif isinstance(segm, np.ndarray): assert segm.ndim == 2, "Expect segmentation of 2 dimensions, got {}.".format( segm.ndim ) # mask array masks.append(segm) else: raise ValueError( "Cannot convert segmentation of type '{}' to BitMasks!" "Supported types are: polygons as list[list[float] or ndarray]," " COCO-style RLE as a dict, or a binary segmentation mask " " in a 2D numpy array of shape HxW.".format(type(segm)) ) # torch.from_numpy does not support array with negative stride. masks = BitMasks( torch.stack([torch.from_numpy(np.ascontiguousarray(x)) for x in masks]) ) target.gt_masks = masks if len(annos) and "keypoints" in annos[0]: kpts = [obj.get("keypoints", []) for obj in annos] target.gt_keypoints = Keypoints(kpts) return target def annotations_to_instances_rotated(annos, image_size): """ Create an :class:`Instances` object used by the models, from instance annotations in the dataset dict. Compared to `annotations_to_instances`, this function is for rotated boxes only Args: annos (list[dict]): a list of instance annotations in one image, each element for one instance. image_size (tuple): height, width Returns: Instances: Containing fields "gt_boxes", "gt_classes", if they can be obtained from `annos`. This is the format that builtin models expect. """ boxes = [obj["bbox"] for obj in annos] target = Instances(image_size) boxes = target.gt_boxes = RotatedBoxes(boxes) boxes.clip(image_size) classes = [obj["category_id"] for obj in annos] classes = torch.tensor(classes, dtype=torch.int64) target.gt_classes = classes return target def filter_empty_instances( instances, by_box=True, by_mask=True, box_threshold=1e-5, return_mask=False ): """ Filter out empty instances in an `Instances` object. Args: instances (Instances): by_box (bool): whether to filter out instances with empty boxes by_mask (bool): whether to filter out instances with empty masks box_threshold (float): minimum width and height to be considered non-empty return_mask (bool): whether to return boolean mask of filtered instances Returns: Instances: the filtered instances. tensor[bool], optional: boolean mask of filtered instances """ assert by_box or by_mask r = [] if by_box: r.append(instances.gt_boxes.nonempty(threshold=box_threshold)) if instances.has("gt_masks") and by_mask: r.append(instances.gt_masks.nonempty()) # TODO: can also filter visible keypoints if not r: return instances m = r[0] for x in r[1:]: m = m & x if return_mask: return instances[m], m return instances[m] def create_keypoint_hflip_indices(dataset_names: Union[str, List[str]]) -> List[int]: """ Args: dataset_names: list of dataset names Returns: list[int]: a list of size=#keypoints, storing the horizontally-flipped keypoint indices. """ if isinstance(dataset_names, str): dataset_names = [dataset_names] check_metadata_consistency("keypoint_names", dataset_names) check_metadata_consistency("keypoint_flip_map", dataset_names) meta = MetadataCatalog.get(dataset_names[0]) names = meta.keypoint_names # TODO flip -> hflip flip_map = dict(meta.keypoint_flip_map) flip_map.update({v: k for k, v in flip_map.items()}) flipped_names = [i if i not in flip_map else flip_map[i] for i in names] flip_indices = [names.index(i) for i in flipped_names] return flip_indices def get_fed_loss_cls_weights(dataset_names: Union[str, List[str]], freq_weight_power=1.0): """ Get frequency weight for each class sorted by class id. We now calcualte freqency weight using image_count to the power freq_weight_power. Args: dataset_names: list of dataset names freq_weight_power: power value """ if isinstance(dataset_names, str): dataset_names = [dataset_names] check_metadata_consistency("class_image_count", dataset_names) meta = MetadataCatalog.get(dataset_names[0]) class_freq_meta = meta.class_image_count class_freq = torch.tensor( [c["image_count"] for c in sorted(class_freq_meta, key=lambda x: x["id"])] ) class_freq_weight = class_freq.float() ** freq_weight_power return class_freq_weight def gen_crop_transform_with_instance(crop_size, image_size, instance): """ Generate a CropTransform so that the cropping region contains the center of the given instance. Args: crop_size (tuple): h, w in pixels image_size (tuple): h, w instance (dict): an annotation dict of one instance, in Detectron2's dataset format. """ crop_size = np.asarray(crop_size, dtype=np.int32) bbox = BoxMode.convert(instance["bbox"], instance["bbox_mode"], BoxMode.XYXY_ABS) center_yx = (bbox[1] + bbox[3]) * 0.5, (bbox[0] + bbox[2]) * 0.5 assert ( image_size[0] >= center_yx[0] and image_size[1] >= center_yx[1] ), "The annotation bounding box is outside of the image!" assert ( image_size[0] >= crop_size[0] and image_size[1] >= crop_size[1] ), "Crop size is larger than image size!" min_yx = np.maximum(np.floor(center_yx).astype(np.int32) - crop_size, 0) max_yx = np.maximum(np.asarray(image_size, dtype=np.int32) - crop_size, 0) max_yx = np.minimum(max_yx, np.ceil(center_yx).astype(np.int32)) y0 = np.random.randint(min_yx[0], max_yx[0] + 1) x0 = np.random.randint(min_yx[1], max_yx[1] + 1) return T.CropTransform(x0, y0, crop_size[1], crop_size[0]) def check_metadata_consistency(key, dataset_names): """ Check that the datasets have consistent metadata. Args: key (str): a metadata key dataset_names (list[str]): a list of dataset names Raises: AttributeError: if the key does not exist in the metadata ValueError: if the given datasets do not have the same metadata values defined by key """ if len(dataset_names) == 0: return logger = logging.getLogger(__name__) entries_per_dataset = [getattr(MetadataCatalog.get(d), key) for d in dataset_names] for idx, entry in enumerate(entries_per_dataset): if entry != entries_per_dataset[0]: logger.error( "Metadata '{}' for dataset '{}' is '{}'".format(key, dataset_names[idx], str(entry)) ) logger.error( "Metadata '{}' for dataset '{}' is '{}'".format( key, dataset_names[0], str(entries_per_dataset[0]) ) ) raise ValueError("Datasets have different metadata '{}'!".format(key)) def build_augmentation(cfg, is_train): """ Create a list of default :class:`Augmentation` from config. Now it includes resizing and flipping. Returns: list[Augmentation] """ if is_train: min_size = cfg.INPUT.MIN_SIZE_TRAIN max_size = cfg.INPUT.MAX_SIZE_TRAIN sample_style = cfg.INPUT.MIN_SIZE_TRAIN_SAMPLING else: min_size = cfg.INPUT.MIN_SIZE_TEST max_size = cfg.INPUT.MAX_SIZE_TEST sample_style = "choice" augmentation = [T.ResizeShortestEdge(min_size, max_size, sample_style)] if is_train and cfg.INPUT.RANDOM_FLIP != "none": augmentation.append( T.RandomFlip( horizontal=cfg.INPUT.RANDOM_FLIP == "horizontal", vertical=cfg.INPUT.RANDOM_FLIP == "vertical", ) ) return augmentation build_transform_gen = build_augmentation """ Alias for backward-compatibility. """ ================================================ FILE: detectron2/detectron2/data/samplers/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .distributed_sampler import ( InferenceSampler, RandomSubsetTrainingSampler, RepeatFactorTrainingSampler, TrainingSampler, ) from .grouped_batch_sampler import GroupedBatchSampler __all__ = [ "GroupedBatchSampler", "TrainingSampler", "RandomSubsetTrainingSampler", "InferenceSampler", "RepeatFactorTrainingSampler", ] ================================================ FILE: detectron2/detectron2/data/samplers/distributed_sampler.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import itertools import logging import math from collections import defaultdict from typing import Optional import torch from torch.utils.data.sampler import Sampler from detectron2.utils import comm logger = logging.getLogger(__name__) class TrainingSampler(Sampler): """ In training, we only care about the "infinite stream" of training data. So this sampler produces an infinite stream of indices and all workers cooperate to correctly shuffle the indices and sample different indices. The samplers in each worker effectively produces `indices[worker_id::num_workers]` where `indices` is an infinite stream of indices consisting of `shuffle(range(size)) + shuffle(range(size)) + ...` (if shuffle is True) or `range(size) + range(size) + ...` (if shuffle is False) Note that this sampler does not shard based on pytorch DataLoader worker id. A sampler passed to pytorch DataLoader is used only with map-style dataset and will not be executed inside workers. But if this sampler is used in a way that it gets execute inside a dataloader worker, then extra work needs to be done to shard its outputs based on worker id. This is required so that workers don't produce identical data. :class:`ToIterableDataset` implements this logic. This note is true for all samplers in detectron2. """ def __init__(self, size: int, shuffle: bool = True, seed: Optional[int] = None): """ Args: size (int): the total number of data of the underlying dataset to sample from shuffle (bool): whether to shuffle the indices or not seed (int): the initial seed of the shuffle. Must be the same across all workers. If None, will use a random seed shared among workers (require synchronization among all workers). """ if not isinstance(size, int): raise TypeError(f"TrainingSampler(size=) expects an int. Got type {type(size)}.") if size <= 0: raise ValueError(f"TrainingSampler(size=) expects a positive int. Got {size}.") self._size = size self._shuffle = shuffle if seed is None: seed = comm.shared_random_seed() self._seed = int(seed) self._rank = comm.get_rank() self._world_size = comm.get_world_size() def __iter__(self): start = self._rank yield from itertools.islice(self._infinite_indices(), start, None, self._world_size) def _infinite_indices(self): g = torch.Generator() g.manual_seed(self._seed) while True: if self._shuffle: yield from torch.randperm(self._size, generator=g).tolist() else: yield from torch.arange(self._size).tolist() class RandomSubsetTrainingSampler(TrainingSampler): """ Similar to TrainingSampler, but only sample a random subset of indices. This is useful when you want to estimate the accuracy vs data-number curves by training the model with different subset_ratio. """ def __init__( self, size: int, subset_ratio: float, shuffle: bool = True, seed_shuffle: Optional[int] = None, seed_subset: Optional[int] = None, ): """ Args: size (int): the total number of data of the underlying dataset to sample from subset_ratio (float): the ratio of subset data to sample from the underlying dataset shuffle (bool): whether to shuffle the indices or not seed_shuffle (int): the initial seed of the shuffle. Must be the same across all workers. If None, will use a random seed shared among workers (require synchronization among all workers). seed_subset (int): the seed to randomize the subset to be sampled. Must be the same across all workers. If None, will use a random seed shared among workers (require synchronization among all workers). """ super().__init__(size=size, shuffle=shuffle, seed=seed_shuffle) assert 0.0 < subset_ratio <= 1.0 self._size_subset = int(size * subset_ratio) assert self._size_subset > 0 if seed_subset is None: seed_subset = comm.shared_random_seed() self._seed_subset = int(seed_subset) # randomly generate the subset indexes to be sampled from g = torch.Generator() g.manual_seed(self._seed_subset) indexes_randperm = torch.randperm(self._size, generator=g) self._indexes_subset = indexes_randperm[: self._size_subset] logger.info("Using RandomSubsetTrainingSampler......") logger.info(f"Randomly sample {self._size_subset} data from the original {self._size} data") def _infinite_indices(self): g = torch.Generator() g.manual_seed(self._seed) # self._seed equals seed_shuffle from __init__() while True: if self._shuffle: # generate a random permutation to shuffle self._indexes_subset randperm = torch.randperm(self._size_subset, generator=g) yield from self._indexes_subset[randperm].tolist() else: yield from self._indexes_subset.tolist() class RepeatFactorTrainingSampler(Sampler): """ Similar to TrainingSampler, but a sample may appear more times than others based on its "repeat factor". This is suitable for training on class imbalanced datasets like LVIS. """ def __init__(self, repeat_factors, *, shuffle=True, seed=None): """ Args: repeat_factors (Tensor): a float vector, the repeat factor for each indice. When it's full of ones, it is equivalent to ``TrainingSampler(len(repeat_factors), ...)``. shuffle (bool): whether to shuffle the indices or not seed (int): the initial seed of the shuffle. Must be the same across all workers. If None, will use a random seed shared among workers (require synchronization among all workers). """ self._shuffle = shuffle if seed is None: seed = comm.shared_random_seed() self._seed = int(seed) self._rank = comm.get_rank() self._world_size = comm.get_world_size() # Split into whole number (_int_part) and fractional (_frac_part) parts. self._int_part = torch.trunc(repeat_factors) self._frac_part = repeat_factors - self._int_part @staticmethod def repeat_factors_from_category_frequency(dataset_dicts, repeat_thresh): """ Compute (fractional) per-image repeat factors based on category frequency. The repeat factor for an image is a function of the frequency of the rarest category labeled in that image. The "frequency of category c" in [0, 1] is defined as the fraction of images in the training set (without repeats) in which category c appears. See :paper:`lvis` (>= v2) Appendix B.2. Args: dataset_dicts (list[dict]): annotations in Detectron2 dataset format. repeat_thresh (float): frequency threshold below which data is repeated. If the frequency is half of `repeat_thresh`, the image will be repeated twice. Returns: torch.Tensor: the i-th element is the repeat factor for the dataset image at index i. """ # 1. For each category c, compute the fraction of images that contain it: f(c) category_freq = defaultdict(int) for dataset_dict in dataset_dicts: # For each image (without repeats) cat_ids = {ann["category_id"] for ann in dataset_dict["annotations"]} for cat_id in cat_ids: category_freq[cat_id] += 1 num_images = len(dataset_dicts) for k, v in category_freq.items(): category_freq[k] = v / num_images # 2. For each category c, compute the category-level repeat factor: # r(c) = max(1, sqrt(t / f(c))) category_rep = { cat_id: max(1.0, math.sqrt(repeat_thresh / cat_freq)) for cat_id, cat_freq in category_freq.items() } # 3. For each image I, compute the image-level repeat factor: # r(I) = max_{c in I} r(c) rep_factors = [] for dataset_dict in dataset_dicts: cat_ids = {ann["category_id"] for ann in dataset_dict["annotations"]} rep_factor = max({category_rep[cat_id] for cat_id in cat_ids}, default=1.0) rep_factors.append(rep_factor) return torch.tensor(rep_factors, dtype=torch.float32) def _get_epoch_indices(self, generator): """ Create a list of dataset indices (with repeats) to use for one epoch. Args: generator (torch.Generator): pseudo random number generator used for stochastic rounding. Returns: torch.Tensor: list of dataset indices to use in one epoch. Each index is repeated based on its calculated repeat factor. """ # Since repeat factors are fractional, we use stochastic rounding so # that the target repeat factor is achieved in expectation over the # course of training rands = torch.rand(len(self._frac_part), generator=generator) rep_factors = self._int_part + (rands < self._frac_part).float() # Construct a list of indices in which we repeat images as specified indices = [] for dataset_index, rep_factor in enumerate(rep_factors): indices.extend([dataset_index] * int(rep_factor.item())) return torch.tensor(indices, dtype=torch.int64) def __iter__(self): start = self._rank yield from itertools.islice(self._infinite_indices(), start, None, self._world_size) def _infinite_indices(self): g = torch.Generator() g.manual_seed(self._seed) while True: # Sample indices with repeats determined by stochastic rounding; each # "epoch" may have a slightly different size due to the rounding. indices = self._get_epoch_indices(g) if self._shuffle: randperm = torch.randperm(len(indices), generator=g) yield from indices[randperm].tolist() else: yield from indices.tolist() class InferenceSampler(Sampler): """ Produce indices for inference across all workers. Inference needs to run on the __exact__ set of samples, therefore when the total number of samples is not divisible by the number of workers, this sampler produces different number of samples on different workers. """ def __init__(self, size: int): """ Args: size (int): the total number of data of the underlying dataset to sample from """ self._size = size assert size > 0 self._rank = comm.get_rank() self._world_size = comm.get_world_size() self._local_indices = self._get_local_indices(size, self._world_size, self._rank) @staticmethod def _get_local_indices(total_size, world_size, rank): shard_size = total_size // world_size left = total_size % world_size shard_sizes = [shard_size + int(r < left) for r in range(world_size)] begin = sum(shard_sizes[:rank]) end = min(sum(shard_sizes[: rank + 1]), total_size) return range(begin, end) def __iter__(self): yield from self._local_indices def __len__(self): return len(self._local_indices) ================================================ FILE: detectron2/detectron2/data/samplers/grouped_batch_sampler.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from torch.utils.data.sampler import BatchSampler, Sampler class GroupedBatchSampler(BatchSampler): """ Wraps another sampler to yield a mini-batch of indices. It enforces that the batch only contain elements from the same group. It also tries to provide mini-batches which follows an ordering which is as close as possible to the ordering from the original sampler. """ def __init__(self, sampler, group_ids, batch_size): """ Args: sampler (Sampler): Base sampler. group_ids (list[int]): If the sampler produces indices in range [0, N), `group_ids` must be a list of `N` ints which contains the group id of each sample. The group ids must be a set of integers in the range [0, num_groups). batch_size (int): Size of mini-batch. """ if not isinstance(sampler, Sampler): raise ValueError( "sampler should be an instance of " "torch.utils.data.Sampler, but got sampler={}".format(sampler) ) self.sampler = sampler self.group_ids = np.asarray(group_ids) assert self.group_ids.ndim == 1 self.batch_size = batch_size groups = np.unique(self.group_ids).tolist() # buffer the indices of each group until batch size is reached self.buffer_per_group = {k: [] for k in groups} def __iter__(self): for idx in self.sampler: group_id = self.group_ids[idx] group_buffer = self.buffer_per_group[group_id] group_buffer.append(idx) if len(group_buffer) == self.batch_size: yield group_buffer[:] # yield a copy of the list del group_buffer[:] def __len__(self): raise NotImplementedError("len() of GroupedBatchSampler is not well-defined.") ================================================ FILE: detectron2/detectron2/data/transforms/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from fvcore.transforms.transform import Transform, TransformList # order them first from fvcore.transforms.transform import * from .transform import * from .augmentation import * from .augmentation_impl import * __all__ = [k for k in globals().keys() if not k.startswith("_")] from detectron2.utils.env import fixup_module_metadata fixup_module_metadata(__name__, globals(), __all__) del fixup_module_metadata ================================================ FILE: detectron2/detectron2/data/transforms/augmentation.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import inspect import numpy as np import pprint from typing import Any, List, Optional, Tuple, Union from fvcore.transforms.transform import Transform, TransformList """ See "Data Augmentation" tutorial for an overview of the system: https://detectron2.readthedocs.io/tutorials/augmentation.html """ __all__ = [ "Augmentation", "AugmentationList", "AugInput", "TransformGen", "apply_transform_gens", "StandardAugInput", "apply_augmentations", ] def _check_img_dtype(img): assert isinstance(img, np.ndarray), "[Augmentation] Needs an numpy array, but got a {}!".format( type(img) ) assert not isinstance(img.dtype, np.integer) or ( img.dtype == np.uint8 ), "[Augmentation] Got image of type {}, use uint8 or floating points instead!".format( img.dtype ) assert img.ndim in [2, 3], img.ndim def _get_aug_input_args(aug, aug_input) -> List[Any]: """ Get the arguments to be passed to ``aug.get_transform`` from the input ``aug_input``. """ if aug.input_args is None: # Decide what attributes are needed automatically prms = list(inspect.signature(aug.get_transform).parameters.items()) # The default behavior is: if there is one parameter, then its "image" # (work automatically for majority of use cases, and also avoid BC breaking), # Otherwise, use the argument names. if len(prms) == 1: names = ("image",) else: names = [] for name, prm in prms: if prm.kind in ( inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD, ): raise TypeError( f""" \ The default implementation of `{type(aug)}.__call__` does not allow \ `{type(aug)}.get_transform` to use variable-length arguments (*args, **kwargs)! \ If arguments are unknown, reimplement `__call__` instead. \ """ ) names.append(name) aug.input_args = tuple(names) args = [] for f in aug.input_args: try: args.append(getattr(aug_input, f)) except AttributeError as e: raise AttributeError( f"{type(aug)}.get_transform needs input attribute '{f}', " f"but it is not an attribute of {type(aug_input)}!" ) from e return args class Augmentation: """ Augmentation defines (often random) policies/strategies to generate :class:`Transform` from data. It is often used for pre-processing of input data. A "policy" that generates a :class:`Transform` may, in the most general case, need arbitrary information from input data in order to determine what transforms to apply. Therefore, each :class:`Augmentation` instance defines the arguments needed by its :meth:`get_transform` method. When called with the positional arguments, the :meth:`get_transform` method executes the policy. Note that :class:`Augmentation` defines the policies to create a :class:`Transform`, but not how to execute the actual transform operations to those data. Its :meth:`__call__` method will use :meth:`AugInput.transform` to execute the transform. The returned `Transform` object is meant to describe deterministic transformation, which means it can be re-applied on associated data, e.g. the geometry of an image and its segmentation masks need to be transformed together. (If such re-application is not needed, then determinism is not a crucial requirement.) """ input_args: Optional[Tuple[str]] = None """ Stores the attribute names needed by :meth:`get_transform`, e.g. ``("image", "sem_seg")``. By default, it is just a tuple of argument names in :meth:`self.get_transform`, which often only contain "image". As long as the argument name convention is followed, there is no need for users to touch this attribute. """ def _init(self, params=None): if params: for k, v in params.items(): if k != "self" and not k.startswith("_"): setattr(self, k, v) def get_transform(self, *args) -> Transform: """ Execute the policy based on input data, and decide what transform to apply to inputs. Args: args: Any fixed-length positional arguments. By default, the name of the arguments should exist in the :class:`AugInput` to be used. Returns: Transform: Returns the deterministic transform to apply to the input. Examples: :: class MyAug: # if a policy needs to know both image and semantic segmentation def get_transform(image, sem_seg) -> T.Transform: pass tfm: Transform = MyAug().get_transform(image, sem_seg) new_image = tfm.apply_image(image) Notes: Users can freely use arbitrary new argument names in custom :meth:`get_transform` method, as long as they are available in the input data. In detectron2 we use the following convention: * image: (H,W) or (H,W,C) ndarray of type uint8 in range [0, 255], or floating point in range [0, 1] or [0, 255]. * boxes: (N,4) ndarray of float32. It represents the instance bounding boxes of N instances. Each is in XYXY format in unit of absolute coordinates. * sem_seg: (H,W) ndarray of type uint8. Each element is an integer label of pixel. We do not specify convention for other types and do not include builtin :class:`Augmentation` that uses other types in detectron2. """ raise NotImplementedError def __call__(self, aug_input) -> Transform: """ Augment the given `aug_input` **in-place**, and return the transform that's used. This method will be called to apply the augmentation. In most augmentation, it is enough to use the default implementation, which calls :meth:`get_transform` using the inputs. But a subclass can overwrite it to have more complicated logic. Args: aug_input (AugInput): an object that has attributes needed by this augmentation (defined by ``self.get_transform``). Its ``transform`` method will be called to in-place transform it. Returns: Transform: the transform that is applied on the input. """ args = _get_aug_input_args(self, aug_input) tfm = self.get_transform(*args) assert isinstance(tfm, (Transform, TransformList)), ( f"{type(self)}.get_transform must return an instance of Transform! " f"Got {type(tfm)} instead." ) aug_input.transform(tfm) return tfm def _rand_range(self, low=1.0, high=None, size=None): """ Uniform float random number between low and high. """ if high is None: low, high = 0, low if size is None: size = [] return np.random.uniform(low, high, size) def __repr__(self): """ Produce something like: "MyAugmentation(field1={self.field1}, field2={self.field2})" """ try: sig = inspect.signature(self.__init__) classname = type(self).__name__ argstr = [] for name, param in sig.parameters.items(): assert ( param.kind != param.VAR_POSITIONAL and param.kind != param.VAR_KEYWORD ), "The default __repr__ doesn't support *args or **kwargs" assert hasattr(self, name), ( "Attribute {} not found! " "Default __repr__ only works if attributes match the constructor.".format(name) ) attr = getattr(self, name) default = param.default if default is attr: continue attr_str = pprint.pformat(attr) if "\n" in attr_str: # don't show it if pformat decides to use >1 lines attr_str = "..." argstr.append("{}={}".format(name, attr_str)) return "{}({})".format(classname, ", ".join(argstr)) except AssertionError: return super().__repr__() __str__ = __repr__ class _TransformToAug(Augmentation): def __init__(self, tfm: Transform): self.tfm = tfm def get_transform(self, *args): return self.tfm def __repr__(self): return repr(self.tfm) __str__ = __repr__ def _transform_to_aug(tfm_or_aug): """ Wrap Transform into Augmentation. Private, used internally to implement augmentations. """ assert isinstance(tfm_or_aug, (Transform, Augmentation)), tfm_or_aug if isinstance(tfm_or_aug, Augmentation): return tfm_or_aug else: return _TransformToAug(tfm_or_aug) class AugmentationList(Augmentation): """ Apply a sequence of augmentations. It has ``__call__`` method to apply the augmentations. Note that :meth:`get_transform` method is impossible (will throw error if called) for :class:`AugmentationList`, because in order to apply a sequence of augmentations, the kth augmentation must be applied first, to provide inputs needed by the (k+1)th augmentation. """ def __init__(self, augs): """ Args: augs (list[Augmentation or Transform]): """ super().__init__() self.augs = [_transform_to_aug(x) for x in augs] def __call__(self, aug_input) -> Transform: tfms = [] for x in self.augs: tfm = x(aug_input) tfms.append(tfm) return TransformList(tfms) def __repr__(self): msgs = [str(x) for x in self.augs] return "AugmentationList[{}]".format(", ".join(msgs)) __str__ = __repr__ class AugInput: """ Input that can be used with :meth:`Augmentation.__call__`. This is a standard implementation for the majority of use cases. This class provides the standard attributes **"image", "boxes", "sem_seg"** defined in :meth:`__init__` and they may be needed by different augmentations. Most augmentation policies do not need attributes beyond these three. After applying augmentations to these attributes (using :meth:`AugInput.transform`), the returned transforms can then be used to transform other data structures that users have. Examples: :: input = AugInput(image, boxes=boxes) tfms = augmentation(input) transformed_image = input.image transformed_boxes = input.boxes transformed_other_data = tfms.apply_other(other_data) An extended project that works with new data types may implement augmentation policies that need other inputs. An algorithm may need to transform inputs in a way different from the standard approach defined in this class. In those rare situations, users can implement a class similar to this class, that satify the following condition: * The input must provide access to these data in the form of attribute access (``getattr``). For example, if an :class:`Augmentation` to be applied needs "image" and "sem_seg" arguments, its input must have the attribute "image" and "sem_seg". * The input must have a ``transform(tfm: Transform) -> None`` method which in-place transforms all its attributes. """ # TODO maybe should support more builtin data types here def __init__( self, image: np.ndarray, *, boxes: Optional[np.ndarray] = None, sem_seg: Optional[np.ndarray] = None, ): """ Args: image (ndarray): (H,W) or (H,W,C) ndarray of type uint8 in range [0, 255], or floating point in range [0, 1] or [0, 255]. The meaning of C is up to users. boxes (ndarray or None): Nx4 float32 boxes in XYXY_ABS mode sem_seg (ndarray or None): HxW uint8 semantic segmentation mask. Each element is an integer label of pixel. """ _check_img_dtype(image) self.image = image self.boxes = boxes self.sem_seg = sem_seg def transform(self, tfm: Transform) -> None: """ In-place transform all attributes of this class. By "in-place", it means after calling this method, accessing an attribute such as ``self.image`` will return transformed data. """ self.image = tfm.apply_image(self.image) if self.boxes is not None: self.boxes = tfm.apply_box(self.boxes) if self.sem_seg is not None: self.sem_seg = tfm.apply_segmentation(self.sem_seg) def apply_augmentations( self, augmentations: List[Union[Augmentation, Transform]] ) -> TransformList: """ Equivalent of ``AugmentationList(augmentations)(self)`` """ return AugmentationList(augmentations)(self) def apply_augmentations(augmentations: List[Union[Transform, Augmentation]], inputs): """ Use ``T.AugmentationList(augmentations)(inputs)`` instead. """ if isinstance(inputs, np.ndarray): # handle the common case of image-only Augmentation, also for backward compatibility image_only = True inputs = AugInput(inputs) else: image_only = False tfms = inputs.apply_augmentations(augmentations) return inputs.image if image_only else inputs, tfms apply_transform_gens = apply_augmentations """ Alias for backward-compatibility. """ TransformGen = Augmentation """ Alias for Augmentation, since it is something that generates :class:`Transform`s """ StandardAugInput = AugInput """ Alias for compatibility. It's not worth the complexity to have two classes. """ ================================================ FILE: detectron2/detectron2/data/transforms/augmentation_impl.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. """ Implement many useful :class:`Augmentation`. """ import numpy as np import sys from typing import Tuple import torch from fvcore.transforms.transform import ( BlendTransform, CropTransform, HFlipTransform, NoOpTransform, PadTransform, Transform, TransformList, VFlipTransform, ) from PIL import Image from .augmentation import Augmentation, _transform_to_aug from .transform import ExtentTransform, ResizeTransform, RotationTransform __all__ = [ "FixedSizeCrop", "RandomApply", "RandomBrightness", "RandomContrast", "RandomCrop", "RandomExtent", "RandomFlip", "RandomSaturation", "RandomLighting", "RandomRotation", "Resize", "ResizeScale", "ResizeShortestEdge", "RandomCrop_CategoryAreaConstraint", ] class RandomApply(Augmentation): """ Randomly apply an augmentation with a given probability. """ def __init__(self, tfm_or_aug, prob=0.5): """ Args: tfm_or_aug (Transform, Augmentation): the transform or augmentation to be applied. It can either be a `Transform` or `Augmentation` instance. prob (float): probability between 0.0 and 1.0 that the wrapper transformation is applied """ super().__init__() self.aug = _transform_to_aug(tfm_or_aug) assert 0.0 <= prob <= 1.0, f"Probablity must be between 0.0 and 1.0 (given: {prob})" self.prob = prob def get_transform(self, *args): do = self._rand_range() < self.prob if do: return self.aug.get_transform(*args) else: return NoOpTransform() def __call__(self, aug_input): do = self._rand_range() < self.prob if do: return self.aug(aug_input) else: return NoOpTransform() class RandomFlip(Augmentation): """ Flip the image horizontally or vertically with the given probability. """ def __init__(self, prob=0.5, *, horizontal=True, vertical=False): """ Args: prob (float): probability of flip. horizontal (boolean): whether to apply horizontal flipping vertical (boolean): whether to apply vertical flipping """ super().__init__() if horizontal and vertical: raise ValueError("Cannot do both horiz and vert. Please use two Flip instead.") if not horizontal and not vertical: raise ValueError("At least one of horiz or vert has to be True!") self._init(locals()) def get_transform(self, image): h, w = image.shape[:2] do = self._rand_range() < self.prob if do: if self.horizontal: return HFlipTransform(w) elif self.vertical: return VFlipTransform(h) else: return NoOpTransform() class Resize(Augmentation): """Resize image to a fixed target size""" def __init__(self, shape, interp=Image.BILINEAR): """ Args: shape: (h, w) tuple or a int interp: PIL interpolation method """ if isinstance(shape, int): shape = (shape, shape) shape = tuple(shape) self._init(locals()) def get_transform(self, image): return ResizeTransform( image.shape[0], image.shape[1], self.shape[0], self.shape[1], self.interp ) class ResizeShortestEdge(Augmentation): """ Resize the image while keeping the aspect ratio unchanged. It attempts to scale the shorter edge to the given `short_edge_length`, as long as the longer edge does not exceed `max_size`. If `max_size` is reached, then downscale so that the longer edge does not exceed max_size. """ @torch.jit.unused def __init__( self, short_edge_length, max_size=sys.maxsize, sample_style="range", interp=Image.BILINEAR ): """ Args: short_edge_length (list[int]): If ``sample_style=="range"``, a [min, max] interval from which to sample the shortest edge length. If ``sample_style=="choice"``, a list of shortest edge lengths to sample from. max_size (int): maximum allowed longest edge length. sample_style (str): either "range" or "choice". """ super().__init__() assert sample_style in ["range", "choice"], sample_style self.is_range = sample_style == "range" if isinstance(short_edge_length, int): short_edge_length = (short_edge_length, short_edge_length) if self.is_range: assert len(short_edge_length) == 2, ( "short_edge_length must be two values using 'range' sample style." f" Got {short_edge_length}!" ) self._init(locals()) @torch.jit.unused def get_transform(self, image): h, w = image.shape[:2] if self.is_range: size = np.random.randint(self.short_edge_length[0], self.short_edge_length[1] + 1) else: size = np.random.choice(self.short_edge_length) if size == 0: return NoOpTransform() newh, neww = ResizeShortestEdge.get_output_shape(h, w, size, self.max_size) return ResizeTransform(h, w, newh, neww, self.interp) @staticmethod def get_output_shape( oldh: int, oldw: int, short_edge_length: int, max_size: int ) -> Tuple[int, int]: """ Compute the output size given input size and target short edge length. """ h, w = oldh, oldw size = short_edge_length * 1.0 scale = size / min(h, w) if h < w: newh, neww = size, scale * w else: newh, neww = scale * h, size if max(newh, neww) > max_size: scale = max_size * 1.0 / max(newh, neww) newh = newh * scale neww = neww * scale neww = int(neww + 0.5) newh = int(newh + 0.5) return (newh, neww) class ResizeScale(Augmentation): """ Takes target size as input and randomly scales the given target size between `min_scale` and `max_scale`. It then scales the input image such that it fits inside the scaled target box, keeping the aspect ratio constant. This implements the resize part of the Google's 'resize_and_crop' data augmentation: https://github.com/tensorflow/tpu/blob/master/models/official/detection/utils/input_utils.py#L127 """ def __init__( self, min_scale: float, max_scale: float, target_height: int, target_width: int, interp: int = Image.BILINEAR, ): """ Args: min_scale: minimum image scale range. max_scale: maximum image scale range. target_height: target image height. target_width: target image width. interp: image interpolation method. """ super().__init__() self._init(locals()) def _get_resize(self, image: np.ndarray, scale: float) -> Transform: input_size = image.shape[:2] # Compute new target size given a scale. target_size = (self.target_height, self.target_width) target_scale_size = np.multiply(target_size, scale) # Compute actual rescaling applied to input image and output size. output_scale = np.minimum( target_scale_size[0] / input_size[0], target_scale_size[1] / input_size[1] ) output_size = np.round(np.multiply(input_size, output_scale)).astype(int) return ResizeTransform( input_size[0], input_size[1], output_size[0], output_size[1], self.interp ) def get_transform(self, image: np.ndarray) -> Transform: random_scale = np.random.uniform(self.min_scale, self.max_scale) return self._get_resize(image, random_scale) class RandomRotation(Augmentation): """ This method returns a copy of this image, rotated the given number of degrees counter clockwise around the given center. """ def __init__(self, angle, expand=True, center=None, sample_style="range", interp=None): """ Args: angle (list[float]): If ``sample_style=="range"``, a [min, max] interval from which to sample the angle (in degrees). If ``sample_style=="choice"``, a list of angles to sample from expand (bool): choose if the image should be resized to fit the whole rotated image (default), or simply cropped center (list[[float, float]]): If ``sample_style=="range"``, a [[minx, miny], [maxx, maxy]] relative interval from which to sample the center, [0, 0] being the top left of the image and [1, 1] the bottom right. If ``sample_style=="choice"``, a list of centers to sample from Default: None, which means that the center of rotation is the center of the image center has no effect if expand=True because it only affects shifting """ super().__init__() assert sample_style in ["range", "choice"], sample_style self.is_range = sample_style == "range" if isinstance(angle, (float, int)): angle = (angle, angle) if center is not None and isinstance(center[0], (float, int)): center = (center, center) self._init(locals()) def get_transform(self, image): h, w = image.shape[:2] center = None if self.is_range: angle = np.random.uniform(self.angle[0], self.angle[1]) if self.center is not None: center = ( np.random.uniform(self.center[0][0], self.center[1][0]), np.random.uniform(self.center[0][1], self.center[1][1]), ) else: angle = np.random.choice(self.angle) if self.center is not None: center = np.random.choice(self.center) if center is not None: center = (w * center[0], h * center[1]) # Convert to absolute coordinates if angle % 360 == 0: return NoOpTransform() return RotationTransform(h, w, angle, expand=self.expand, center=center, interp=self.interp) class FixedSizeCrop(Augmentation): """ If `crop_size` is smaller than the input image size, then it uses a random crop of the crop size. If `crop_size` is larger than the input image size, then it pads the right and the bottom of the image to the crop size if `pad` is True, otherwise it returns the smaller image. """ def __init__(self, crop_size: Tuple[int], pad: bool = True, pad_value: float = 128.0): """ Args: crop_size: target image (height, width). pad: if True, will pad images smaller than `crop_size` up to `crop_size` pad_value: the padding value. """ super().__init__() self._init(locals()) def _get_crop(self, image: np.ndarray) -> Transform: # Compute the image scale and scaled size. input_size = image.shape[:2] output_size = self.crop_size # Add random crop if the image is scaled up. max_offset = np.subtract(input_size, output_size) max_offset = np.maximum(max_offset, 0) offset = np.multiply(max_offset, np.random.uniform(0.0, 1.0)) offset = np.round(offset).astype(int) return CropTransform( offset[1], offset[0], output_size[1], output_size[0], input_size[1], input_size[0] ) def _get_pad(self, image: np.ndarray) -> Transform: # Compute the image scale and scaled size. input_size = image.shape[:2] output_size = self.crop_size # Add padding if the image is scaled down. pad_size = np.subtract(output_size, input_size) pad_size = np.maximum(pad_size, 0) original_size = np.minimum(input_size, output_size) return PadTransform( 0, 0, pad_size[1], pad_size[0], original_size[1], original_size[0], self.pad_value ) def get_transform(self, image: np.ndarray) -> TransformList: transforms = [self._get_crop(image)] if self.pad: transforms.append(self._get_pad(image)) return TransformList(transforms) class RandomCrop(Augmentation): """ Randomly crop a rectangle region out of an image. """ def __init__(self, crop_type: str, crop_size): """ Args: crop_type (str): one of "relative_range", "relative", "absolute", "absolute_range". crop_size (tuple[float, float]): two floats, explained below. - "relative": crop a (H * crop_size[0], W * crop_size[1]) region from an input image of size (H, W). crop size should be in (0, 1] - "relative_range": uniformly sample two values from [crop_size[0], 1] and [crop_size[1]], 1], and use them as in "relative" crop type. - "absolute" crop a (crop_size[0], crop_size[1]) region from input image. crop_size must be smaller than the input image size. - "absolute_range", for an input of size (H, W), uniformly sample H_crop in [crop_size[0], min(H, crop_size[1])] and W_crop in [crop_size[0], min(W, crop_size[1])]. Then crop a region (H_crop, W_crop). """ # TODO style of relative_range and absolute_range are not consistent: # one takes (h, w) but another takes (min, max) super().__init__() assert crop_type in ["relative_range", "relative", "absolute", "absolute_range"] self._init(locals()) def get_transform(self, image): h, w = image.shape[:2] croph, cropw = self.get_crop_size((h, w)) assert h >= croph and w >= cropw, "Shape computation in {} has bugs.".format(self) h0 = np.random.randint(h - croph + 1) w0 = np.random.randint(w - cropw + 1) return CropTransform(w0, h0, cropw, croph) def get_crop_size(self, image_size): """ Args: image_size (tuple): height, width Returns: crop_size (tuple): height, width in absolute pixels """ h, w = image_size if self.crop_type == "relative": ch, cw = self.crop_size return int(h * ch + 0.5), int(w * cw + 0.5) elif self.crop_type == "relative_range": crop_size = np.asarray(self.crop_size, dtype=np.float32) ch, cw = crop_size + np.random.rand(2) * (1 - crop_size) return int(h * ch + 0.5), int(w * cw + 0.5) elif self.crop_type == "absolute": return (min(self.crop_size[0], h), min(self.crop_size[1], w)) elif self.crop_type == "absolute_range": assert self.crop_size[0] <= self.crop_size[1] ch = np.random.randint(min(h, self.crop_size[0]), min(h, self.crop_size[1]) + 1) cw = np.random.randint(min(w, self.crop_size[0]), min(w, self.crop_size[1]) + 1) return ch, cw else: raise NotImplementedError("Unknown crop type {}".format(self.crop_type)) class RandomCrop_CategoryAreaConstraint(Augmentation): """ Similar to :class:`RandomCrop`, but find a cropping window such that no single category occupies a ratio of more than `single_category_max_area` in semantic segmentation ground truth, which can cause unstability in training. The function attempts to find such a valid cropping window for at most 10 times. """ def __init__( self, crop_type: str, crop_size, single_category_max_area: float = 1.0, ignored_category: int = None, ): """ Args: crop_type, crop_size: same as in :class:`RandomCrop` single_category_max_area: the maximum allowed area ratio of a category. Set to 1.0 to disable ignored_category: allow this category in the semantic segmentation ground truth to exceed the area ratio. Usually set to the category that's ignored in training. """ self.crop_aug = RandomCrop(crop_type, crop_size) self._init(locals()) def get_transform(self, image, sem_seg): if self.single_category_max_area >= 1.0: return self.crop_aug.get_transform(image) else: h, w = sem_seg.shape for _ in range(10): crop_size = self.crop_aug.get_crop_size((h, w)) y0 = np.random.randint(h - crop_size[0] + 1) x0 = np.random.randint(w - crop_size[1] + 1) sem_seg_temp = sem_seg[y0 : y0 + crop_size[0], x0 : x0 + crop_size[1]] labels, cnt = np.unique(sem_seg_temp, return_counts=True) if self.ignored_category is not None: cnt = cnt[labels != self.ignored_category] if len(cnt) > 1 and np.max(cnt) < np.sum(cnt) * self.single_category_max_area: break crop_tfm = CropTransform(x0, y0, crop_size[1], crop_size[0]) return crop_tfm class RandomExtent(Augmentation): """ Outputs an image by cropping a random "subrect" of the source image. The subrect can be parameterized to include pixels outside the source image, in which case they will be set to zeros (i.e. black). The size of the output image will vary with the size of the random subrect. """ def __init__(self, scale_range, shift_range): """ Args: output_size (h, w): Dimensions of output image scale_range (l, h): Range of input-to-output size scaling factor shift_range (x, y): Range of shifts of the cropped subrect. The rect is shifted by [w / 2 * Uniform(-x, x), h / 2 * Uniform(-y, y)], where (w, h) is the (width, height) of the input image. Set each component to zero to crop at the image's center. """ super().__init__() self._init(locals()) def get_transform(self, image): img_h, img_w = image.shape[:2] # Initialize src_rect to fit the input image. src_rect = np.array([-0.5 * img_w, -0.5 * img_h, 0.5 * img_w, 0.5 * img_h]) # Apply a random scaling to the src_rect. src_rect *= np.random.uniform(self.scale_range[0], self.scale_range[1]) # Apply a random shift to the coordinates origin. src_rect[0::2] += self.shift_range[0] * img_w * (np.random.rand() - 0.5) src_rect[1::2] += self.shift_range[1] * img_h * (np.random.rand() - 0.5) # Map src_rect coordinates into image coordinates (center at corner). src_rect[0::2] += 0.5 * img_w src_rect[1::2] += 0.5 * img_h return ExtentTransform( src_rect=(src_rect[0], src_rect[1], src_rect[2], src_rect[3]), output_size=(int(src_rect[3] - src_rect[1]), int(src_rect[2] - src_rect[0])), ) class RandomContrast(Augmentation): """ Randomly transforms image contrast. Contrast intensity is uniformly sampled in (intensity_min, intensity_max). - intensity < 1 will reduce contrast - intensity = 1 will preserve the input image - intensity > 1 will increase contrast See: https://pillow.readthedocs.io/en/3.0.x/reference/ImageEnhance.html """ def __init__(self, intensity_min, intensity_max): """ Args: intensity_min (float): Minimum augmentation intensity_max (float): Maximum augmentation """ super().__init__() self._init(locals()) def get_transform(self, image): w = np.random.uniform(self.intensity_min, self.intensity_max) return BlendTransform(src_image=image.mean(), src_weight=1 - w, dst_weight=w) class RandomBrightness(Augmentation): """ Randomly transforms image brightness. Brightness intensity is uniformly sampled in (intensity_min, intensity_max). - intensity < 1 will reduce brightness - intensity = 1 will preserve the input image - intensity > 1 will increase brightness See: https://pillow.readthedocs.io/en/3.0.x/reference/ImageEnhance.html """ def __init__(self, intensity_min, intensity_max): """ Args: intensity_min (float): Minimum augmentation intensity_max (float): Maximum augmentation """ super().__init__() self._init(locals()) def get_transform(self, image): w = np.random.uniform(self.intensity_min, self.intensity_max) return BlendTransform(src_image=0, src_weight=1 - w, dst_weight=w) class RandomSaturation(Augmentation): """ Randomly transforms saturation of an RGB image. Input images are assumed to have 'RGB' channel order. Saturation intensity is uniformly sampled in (intensity_min, intensity_max). - intensity < 1 will reduce saturation (make the image more grayscale) - intensity = 1 will preserve the input image - intensity > 1 will increase saturation See: https://pillow.readthedocs.io/en/3.0.x/reference/ImageEnhance.html """ def __init__(self, intensity_min, intensity_max): """ Args: intensity_min (float): Minimum augmentation (1 preserves input). intensity_max (float): Maximum augmentation (1 preserves input). """ super().__init__() self._init(locals()) def get_transform(self, image): assert image.shape[-1] == 3, "RandomSaturation only works on RGB images" w = np.random.uniform(self.intensity_min, self.intensity_max) grayscale = image.dot([0.299, 0.587, 0.114])[:, :, np.newaxis] return BlendTransform(src_image=grayscale, src_weight=1 - w, dst_weight=w) class RandomLighting(Augmentation): """ The "lighting" augmentation described in AlexNet, using fixed PCA over ImageNet. Input images are assumed to have 'RGB' channel order. The degree of color jittering is randomly sampled via a normal distribution, with standard deviation given by the scale parameter. """ def __init__(self, scale): """ Args: scale (float): Standard deviation of principal component weighting. """ super().__init__() self._init(locals()) self.eigen_vecs = np.array( [[-0.5675, 0.7192, 0.4009], [-0.5808, -0.0045, -0.8140], [-0.5836, -0.6948, 0.4203]] ) self.eigen_vals = np.array([0.2175, 0.0188, 0.0045]) def get_transform(self, image): assert image.shape[-1] == 3, "RandomLighting only works on RGB images" weights = np.random.normal(scale=self.scale, size=3) return BlendTransform( src_image=self.eigen_vecs.dot(weights * self.eigen_vals), src_weight=1.0, dst_weight=1.0 ) ================================================ FILE: detectron2/detectron2/data/transforms/transform.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. """ See "Data Augmentation" tutorial for an overview of the system: https://detectron2.readthedocs.io/tutorials/augmentation.html """ import numpy as np import torch import torch.nn.functional as F from fvcore.transforms.transform import ( CropTransform, HFlipTransform, NoOpTransform, Transform, TransformList, ) from PIL import Image try: import cv2 # noqa except ImportError: # OpenCV is an optional dependency at the moment pass __all__ = [ "ExtentTransform", "ResizeTransform", "RotationTransform", "ColorTransform", "PILColorTransform", ] class ExtentTransform(Transform): """ Extracts a subregion from the source image and scales it to the output size. The fill color is used to map pixels from the source rect that fall outside the source image. See: https://pillow.readthedocs.io/en/latest/PIL.html#PIL.ImageTransform.ExtentTransform """ def __init__(self, src_rect, output_size, interp=Image.LINEAR, fill=0): """ Args: src_rect (x0, y0, x1, y1): src coordinates output_size (h, w): dst image size interp: PIL interpolation methods fill: Fill color used when src_rect extends outside image """ super().__init__() self._set_attributes(locals()) def apply_image(self, img, interp=None): h, w = self.output_size if len(img.shape) > 2 and img.shape[2] == 1: pil_image = Image.fromarray(img[:, :, 0], mode="L") else: pil_image = Image.fromarray(img) pil_image = pil_image.transform( size=(w, h), method=Image.EXTENT, data=self.src_rect, resample=interp if interp else self.interp, fill=self.fill, ) ret = np.asarray(pil_image) if len(img.shape) > 2 and img.shape[2] == 1: ret = np.expand_dims(ret, -1) return ret def apply_coords(self, coords): # Transform image center from source coordinates into output coordinates # and then map the new origin to the corner of the output image. h, w = self.output_size x0, y0, x1, y1 = self.src_rect new_coords = coords.astype(np.float32) new_coords[:, 0] -= 0.5 * (x0 + x1) new_coords[:, 1] -= 0.5 * (y0 + y1) new_coords[:, 0] *= w / (x1 - x0) new_coords[:, 1] *= h / (y1 - y0) new_coords[:, 0] += 0.5 * w new_coords[:, 1] += 0.5 * h return new_coords def apply_segmentation(self, segmentation): segmentation = self.apply_image(segmentation, interp=Image.NEAREST) return segmentation class ResizeTransform(Transform): """ Resize the image to a target size. """ def __init__(self, h, w, new_h, new_w, interp=None): """ Args: h, w (int): original image size new_h, new_w (int): new image size interp: PIL interpolation methods, defaults to bilinear. """ # TODO decide on PIL vs opencv super().__init__() if interp is None: interp = Image.BILINEAR self._set_attributes(locals()) def apply_image(self, img, interp=None): assert img.shape[:2] == (self.h, self.w) assert len(img.shape) <= 4 interp_method = interp if interp is not None else self.interp if img.dtype == np.uint8: if len(img.shape) > 2 and img.shape[2] == 1: pil_image = Image.fromarray(img[:, :, 0], mode="L") else: pil_image = Image.fromarray(img) pil_image = pil_image.resize((self.new_w, self.new_h), interp_method) ret = np.asarray(pil_image) if len(img.shape) > 2 and img.shape[2] == 1: ret = np.expand_dims(ret, -1) else: # PIL only supports uint8 if any(x < 0 for x in img.strides): img = np.ascontiguousarray(img) img = torch.from_numpy(img) shape = list(img.shape) shape_4d = shape[:2] + [1] * (4 - len(shape)) + shape[2:] img = img.view(shape_4d).permute(2, 3, 0, 1) # hw(c) -> nchw _PIL_RESIZE_TO_INTERPOLATE_MODE = { Image.NEAREST: "nearest", Image.BILINEAR: "bilinear", Image.BICUBIC: "bicubic", } mode = _PIL_RESIZE_TO_INTERPOLATE_MODE[interp_method] align_corners = None if mode == "nearest" else False img = F.interpolate( img, (self.new_h, self.new_w), mode=mode, align_corners=align_corners ) shape[:2] = (self.new_h, self.new_w) ret = img.permute(2, 3, 0, 1).view(shape).numpy() # nchw -> hw(c) return ret def apply_coords(self, coords): coords[:, 0] = coords[:, 0] * (self.new_w * 1.0 / self.w) coords[:, 1] = coords[:, 1] * (self.new_h * 1.0 / self.h) return coords def apply_segmentation(self, segmentation): segmentation = self.apply_image(segmentation, interp=Image.NEAREST) return segmentation def inverse(self): return ResizeTransform(self.new_h, self.new_w, self.h, self.w, self.interp) class RotationTransform(Transform): """ This method returns a copy of this image, rotated the given number of degrees counter clockwise around its center. """ def __init__(self, h, w, angle, expand=True, center=None, interp=None): """ Args: h, w (int): original image size angle (float): degrees for rotation expand (bool): choose if the image should be resized to fit the whole rotated image (default), or simply cropped center (tuple (width, height)): coordinates of the rotation center if left to None, the center will be fit to the center of each image center has no effect if expand=True because it only affects shifting interp: cv2 interpolation method, default cv2.INTER_LINEAR """ super().__init__() image_center = np.array((w / 2, h / 2)) if center is None: center = image_center if interp is None: interp = cv2.INTER_LINEAR abs_cos, abs_sin = (abs(np.cos(np.deg2rad(angle))), abs(np.sin(np.deg2rad(angle)))) if expand: # find the new width and height bounds bound_w, bound_h = np.rint( [h * abs_sin + w * abs_cos, h * abs_cos + w * abs_sin] ).astype(int) else: bound_w, bound_h = w, h self._set_attributes(locals()) self.rm_coords = self.create_rotation_matrix() # Needed because of this problem https://github.com/opencv/opencv/issues/11784 self.rm_image = self.create_rotation_matrix(offset=-0.5) def apply_image(self, img, interp=None): """ img should be a numpy array, formatted as Height * Width * Nchannels """ if len(img) == 0 or self.angle % 360 == 0: return img assert img.shape[:2] == (self.h, self.w) interp = interp if interp is not None else self.interp return cv2.warpAffine(img, self.rm_image, (self.bound_w, self.bound_h), flags=interp) def apply_coords(self, coords): """ coords should be a N * 2 array-like, containing N couples of (x, y) points """ coords = np.asarray(coords, dtype=float) if len(coords) == 0 or self.angle % 360 == 0: return coords return cv2.transform(coords[:, np.newaxis, :], self.rm_coords)[:, 0, :] def apply_segmentation(self, segmentation): segmentation = self.apply_image(segmentation, interp=cv2.INTER_NEAREST) return segmentation def create_rotation_matrix(self, offset=0): center = (self.center[0] + offset, self.center[1] + offset) rm = cv2.getRotationMatrix2D(tuple(center), self.angle, 1) if self.expand: # Find the coordinates of the center of rotation in the new image # The only point for which we know the future coordinates is the center of the image rot_im_center = cv2.transform(self.image_center[None, None, :] + offset, rm)[0, 0, :] new_center = np.array([self.bound_w / 2, self.bound_h / 2]) + offset - rot_im_center # shift the rotation center to the new coordinates rm[:, 2] += new_center return rm def inverse(self): """ The inverse is to rotate it back with expand, and crop to get the original shape. """ if not self.expand: # Not possible to inverse if a part of the image is lost raise NotImplementedError() rotation = RotationTransform( self.bound_h, self.bound_w, -self.angle, True, None, self.interp ) crop = CropTransform( (rotation.bound_w - self.w) // 2, (rotation.bound_h - self.h) // 2, self.w, self.h ) return TransformList([rotation, crop]) class ColorTransform(Transform): """ Generic wrapper for any photometric transforms. These transformations should only affect the color space and not the coordinate space of the image (e.g. annotation coordinates such as bounding boxes should not be changed) """ def __init__(self, op): """ Args: op (Callable): operation to be applied to the image, which takes in an ndarray and returns an ndarray. """ if not callable(op): raise ValueError("op parameter should be callable") super().__init__() self._set_attributes(locals()) def apply_image(self, img): return self.op(img) def apply_coords(self, coords): return coords def inverse(self): return NoOpTransform() def apply_segmentation(self, segmentation): return segmentation class PILColorTransform(ColorTransform): """ Generic wrapper for PIL Photometric image transforms, which affect the color space and not the coordinate space of the image """ def __init__(self, op): """ Args: op (Callable): operation to be applied to the image, which takes in a PIL Image and returns a transformed PIL Image. For reference on possible operations see: - https://pillow.readthedocs.io/en/stable/ """ if not callable(op): raise ValueError("op parameter should be callable") super().__init__(op) def apply_image(self, img): img = Image.fromarray(img) return np.asarray(super().apply_image(img)) def HFlip_rotated_box(transform, rotated_boxes): """ Apply the horizontal flip transform on rotated boxes. Args: rotated_boxes (ndarray): Nx5 floating point array of (x_center, y_center, width, height, angle_degrees) format in absolute coordinates. """ # Transform x_center rotated_boxes[:, 0] = transform.width - rotated_boxes[:, 0] # Transform angle rotated_boxes[:, 4] = -rotated_boxes[:, 4] return rotated_boxes def Resize_rotated_box(transform, rotated_boxes): """ Apply the resizing transform on rotated boxes. For details of how these (approximation) formulas are derived, please refer to :meth:`RotatedBoxes.scale`. Args: rotated_boxes (ndarray): Nx5 floating point array of (x_center, y_center, width, height, angle_degrees) format in absolute coordinates. """ scale_factor_x = transform.new_w * 1.0 / transform.w scale_factor_y = transform.new_h * 1.0 / transform.h rotated_boxes[:, 0] *= scale_factor_x rotated_boxes[:, 1] *= scale_factor_y theta = rotated_boxes[:, 4] * np.pi / 180.0 c = np.cos(theta) s = np.sin(theta) rotated_boxes[:, 2] *= np.sqrt(np.square(scale_factor_x * c) + np.square(scale_factor_y * s)) rotated_boxes[:, 3] *= np.sqrt(np.square(scale_factor_x * s) + np.square(scale_factor_y * c)) rotated_boxes[:, 4] = np.arctan2(scale_factor_x * s, scale_factor_y * c) * 180 / np.pi return rotated_boxes HFlipTransform.register_type("rotated_box", HFlip_rotated_box) ResizeTransform.register_type("rotated_box", Resize_rotated_box) # not necessary any more with latest fvcore NoOpTransform.register_type("rotated_box", lambda t, x: x) ================================================ FILE: detectron2/detectron2/engine/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .launch import * from .train_loop import * __all__ = [k for k in globals().keys() if not k.startswith("_")] # prefer to let hooks and defaults live in separate namespaces (therefore not in __all__) # but still make them available here from .hooks import * from .defaults import * ================================================ FILE: detectron2/detectron2/engine/defaults.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. """ This file contains components with some default boilerplate logic user may need in training / testing. They will not work for everyone, but many users may find them useful. The behavior of functions/classes in this file is subject to change, since they are meant to represent the "common default behavior" people need in their projects. """ import argparse import logging import os import sys import weakref from collections import OrderedDict from typing import Optional import torch from fvcore.nn.precise_bn import get_bn_modules from omegaconf import OmegaConf from torch.nn.parallel import DistributedDataParallel import detectron2.data.transforms as T from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import CfgNode, LazyConfig from detectron2.data import ( MetadataCatalog, build_detection_test_loader, build_detection_train_loader, ) from detectron2.evaluation import ( DatasetEvaluator, inference_on_dataset, print_csv_format, verify_results, ) from detectron2.modeling import build_model from detectron2.solver import build_lr_scheduler, build_optimizer from detectron2.utils import comm from detectron2.utils.collect_env import collect_env_info from detectron2.utils.env import seed_all_rng from detectron2.utils.events import CommonMetricPrinter, JSONWriter, TensorboardXWriter from detectron2.utils.file_io import PathManager from detectron2.utils.logger import setup_logger from . import hooks from .train_loop import AMPTrainer, SimpleTrainer, TrainerBase __all__ = [ "create_ddp_model", "default_argument_parser", "default_setup", "default_writers", "DefaultPredictor", "DefaultTrainer", ] def create_ddp_model(model, *, fp16_compression=False, **kwargs): """ Create a DistributedDataParallel model if there are >1 processes. Args: model: a torch.nn.Module fp16_compression: add fp16 compression hooks to the ddp object. See more at https://pytorch.org/docs/stable/ddp_comm_hooks.html#torch.distributed.algorithms.ddp_comm_hooks.default_hooks.fp16_compress_hook kwargs: other arguments of :module:`torch.nn.parallel.DistributedDataParallel`. """ # noqa if comm.get_world_size() == 1: return model if "device_ids" not in kwargs: kwargs["device_ids"] = [comm.get_local_rank()] ddp = DistributedDataParallel(model, **kwargs) if fp16_compression: from torch.distributed.algorithms.ddp_comm_hooks import default as comm_hooks ddp.register_comm_hook(state=None, hook=comm_hooks.fp16_compress_hook) return ddp def default_argument_parser(epilog=None): """ Create a parser with some common arguments used by detectron2 users. Args: epilog (str): epilog passed to ArgumentParser describing the usage. Returns: argparse.ArgumentParser: """ parser = argparse.ArgumentParser( epilog=epilog or f""" Examples: Run on single machine: $ {sys.argv[0]} --num-gpus 8 --config-file cfg.yaml Change some config options: $ {sys.argv[0]} --config-file cfg.yaml MODEL.WEIGHTS /path/to/weight.pth SOLVER.BASE_LR 0.001 Run on multiple machines: (machine0)$ {sys.argv[0]} --machine-rank 0 --num-machines 2 --dist-url [--other-flags] (machine1)$ {sys.argv[0]} --machine-rank 1 --num-machines 2 --dist-url [--other-flags] """, formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument("--config-file", default="", metavar="FILE", help="path to config file") parser.add_argument( "--resume", action="store_true", help="Whether to attempt to resume from the checkpoint directory. " "See documentation of `DefaultTrainer.resume_or_load()` for what it means.", ) parser.add_argument("--eval-only", action="store_true", help="perform evaluation only") parser.add_argument("--num-gpus", type=int, default=1, help="number of gpus *per machine*") parser.add_argument("--num-machines", type=int, default=1, help="total number of machines") parser.add_argument( "--machine-rank", type=int, default=0, help="the rank of this machine (unique per machine)" ) # PyTorch still may leave orphan processes in multi-gpu training. # Therefore we use a deterministic way to obtain port, # so that users are aware of orphan processes by seeing the port occupied. port = 2**15 + 2**14 + hash(os.getuid() if sys.platform != "win32" else 1) % 2**14 parser.add_argument( "--dist-url", default="tcp://127.0.0.1:{}".format(port), help="initialization URL for pytorch distributed backend. See " "https://pytorch.org/docs/stable/distributed.html for details.", ) parser.add_argument( "opts", help=""" Modify config options at the end of the command. For Yacs configs, use space-separated "PATH.KEY VALUE" pairs. For python-based LazyConfig, use "path.key=value". """.strip(), default=None, nargs=argparse.REMAINDER, ) return parser def _try_get_key(cfg, *keys, default=None): """ Try select keys from cfg until the first key that exists. Otherwise return default. """ if isinstance(cfg, CfgNode): cfg = OmegaConf.create(cfg.dump()) for k in keys: none = object() p = OmegaConf.select(cfg, k, default=none) if p is not none: return p return default def _highlight(code, filename): try: import pygments except ImportError: return code from pygments.lexers import Python3Lexer, YamlLexer from pygments.formatters import Terminal256Formatter lexer = Python3Lexer() if filename.endswith(".py") else YamlLexer() code = pygments.highlight(code, lexer, Terminal256Formatter(style="monokai")) return code def default_setup(cfg, args): """ Perform some basic common setups at the beginning of a job, including: 1. Set up the detectron2 logger 2. Log basic information about environment, cmdline arguments, and config 3. Backup the config to the output directory Args: cfg (CfgNode or omegaconf.DictConfig): the full config to be used args (argparse.NameSpace): the command line arguments to be logged """ output_dir = _try_get_key(cfg, "OUTPUT_DIR", "output_dir", "train.output_dir") if comm.is_main_process() and output_dir: PathManager.mkdirs(output_dir) rank = comm.get_rank() setup_logger(output_dir, distributed_rank=rank, name="fvcore") logger = setup_logger(output_dir, distributed_rank=rank) logger.info("Rank of current process: {}. World size: {}".format(rank, comm.get_world_size())) logger.info("Environment info:\n" + collect_env_info()) logger.info("Command line arguments: " + str(args)) if hasattr(args, "config_file") and args.config_file != "": logger.info( "Contents of args.config_file={}:\n{}".format( args.config_file, _highlight(PathManager.open(args.config_file, "r").read(), args.config_file), ) ) if comm.is_main_process() and output_dir: # Note: some of our scripts may expect the existence of # config.yaml in output directory path = os.path.join(output_dir, "config.yaml") if isinstance(cfg, CfgNode): logger.info("Running with full config:\n{}".format(_highlight(cfg.dump(), ".yaml"))) with PathManager.open(path, "w") as f: f.write(cfg.dump()) else: LazyConfig.save(cfg, path) logger.info("Full config saved to {}".format(path)) # make sure each worker has a different, yet deterministic seed if specified seed = _try_get_key(cfg, "SEED", "train.seed", default=-1) seed_all_rng(None if seed < 0 else seed + rank) # cudnn benchmark has large overhead. It shouldn't be used considering the small size of # typical validation set. if not (hasattr(args, "eval_only") and args.eval_only): torch.backends.cudnn.benchmark = _try_get_key( cfg, "CUDNN_BENCHMARK", "train.cudnn_benchmark", default=False ) def default_writers(output_dir: str, max_iter: Optional[int] = None): """ Build a list of :class:`EventWriter` to be used. It now consists of a :class:`CommonMetricPrinter`, :class:`TensorboardXWriter` and :class:`JSONWriter`. Args: output_dir: directory to store JSON metrics and tensorboard events max_iter: the total number of iterations Returns: list[EventWriter]: a list of :class:`EventWriter` objects. """ PathManager.mkdirs(output_dir) return [ # It may not always print what you want to see, since it prints "common" metrics only. CommonMetricPrinter(max_iter), JSONWriter(os.path.join(output_dir, "metrics.json")), TensorboardXWriter(output_dir), ] class DefaultPredictor: """ Create a simple end-to-end predictor with the given config that runs on single device for a single input image. Compared to using the model directly, this class does the following additions: 1. Load checkpoint from `cfg.MODEL.WEIGHTS`. 2. Always take BGR image as the input and apply conversion defined by `cfg.INPUT.FORMAT`. 3. Apply resizing defined by `cfg.INPUT.{MIN,MAX}_SIZE_TEST`. 4. Take one input image and produce a single output, instead of a batch. This is meant for simple demo purposes, so it does the above steps automatically. This is not meant for benchmarks or running complicated inference logic. If you'd like to do anything more complicated, please refer to its source code as examples to build and use the model manually. Attributes: metadata (Metadata): the metadata of the underlying dataset, obtained from cfg.DATASETS.TEST. Examples: :: pred = DefaultPredictor(cfg) inputs = cv2.imread("input.jpg") outputs = pred(inputs) """ def __init__(self, cfg): self.cfg = cfg.clone() # cfg can be modified by model self.model = build_model(self.cfg) self.model.eval() if len(cfg.DATASETS.TEST): self.metadata = MetadataCatalog.get(cfg.DATASETS.TEST[0]) checkpointer = DetectionCheckpointer(self.model) checkpointer.load(cfg.MODEL.WEIGHTS) self.aug = T.ResizeShortestEdge( [cfg.INPUT.MIN_SIZE_TEST, cfg.INPUT.MIN_SIZE_TEST], cfg.INPUT.MAX_SIZE_TEST ) self.input_format = cfg.INPUT.FORMAT assert self.input_format in ["RGB", "BGR"], self.input_format def __call__(self, original_image): """ Args: original_image (np.ndarray): an image of shape (H, W, C) (in BGR order). Returns: predictions (dict): the output of the model for one image only. See :doc:`/tutorials/models` for details about the format. """ with torch.no_grad(): # https://github.com/sphinx-doc/sphinx/issues/4258 # Apply pre-processing to image. if self.input_format == "RGB": # whether the model expects BGR inputs or RGB original_image = original_image[:, :, ::-1] height, width = original_image.shape[:2] image = self.aug.get_transform(original_image).apply_image(original_image) image = torch.as_tensor(image.astype("float32").transpose(2, 0, 1)) inputs = {"image": image, "height": height, "width": width} predictions = self.model([inputs])[0] return predictions class DefaultTrainer(TrainerBase): """ A trainer with default training logic. It does the following: 1. Create a :class:`SimpleTrainer` using model, optimizer, dataloader defined by the given config. Create a LR scheduler defined by the config. 2. Load the last checkpoint or `cfg.MODEL.WEIGHTS`, if exists, when `resume_or_load` is called. 3. Register a few common hooks defined by the config. It is created to simplify the **standard model training workflow** and reduce code boilerplate for users who only need the standard training workflow, with standard features. It means this class makes *many assumptions* about your training logic that may easily become invalid in a new research. In fact, any assumptions beyond those made in the :class:`SimpleTrainer` are too much for research. The code of this class has been annotated about restrictive assumptions it makes. When they do not work for you, you're encouraged to: 1. Overwrite methods of this class, OR: 2. Use :class:`SimpleTrainer`, which only does minimal SGD training and nothing else. You can then add your own hooks if needed. OR: 3. Write your own training loop similar to `tools/plain_train_net.py`. See the :doc:`/tutorials/training` tutorials for more details. Note that the behavior of this class, like other functions/classes in this file, is not stable, since it is meant to represent the "common default behavior". It is only guaranteed to work well with the standard models and training workflow in detectron2. To obtain more stable behavior, write your own training logic with other public APIs. Examples: :: trainer = DefaultTrainer(cfg) trainer.resume_or_load() # load last checkpoint or MODEL.WEIGHTS trainer.train() Attributes: scheduler: checkpointer (DetectionCheckpointer): cfg (CfgNode): """ def __init__(self, cfg): """ Args: cfg (CfgNode): """ super().__init__() logger = logging.getLogger("detectron2") if not logger.isEnabledFor(logging.INFO): # setup_logger is not called for d2 setup_logger() cfg = DefaultTrainer.auto_scale_workers(cfg, comm.get_world_size()) # Assume these objects must be constructed in this order. model = self.build_model(cfg) optimizer = self.build_optimizer(cfg, model) data_loader = self.build_train_loader(cfg) model = create_ddp_model(model, broadcast_buffers=False) self._trainer = (AMPTrainer if cfg.SOLVER.AMP.ENABLED else SimpleTrainer)( model, data_loader, optimizer ) self.scheduler = self.build_lr_scheduler(cfg, optimizer) self.checkpointer = DetectionCheckpointer( # Assume you want to save checkpoints together with logs/statistics model, cfg.OUTPUT_DIR, trainer=weakref.proxy(self), ) self.start_iter = 0 self.max_iter = cfg.SOLVER.MAX_ITER self.cfg = cfg self.register_hooks(self.build_hooks()) def resume_or_load(self, resume=True): """ If `resume==True` and `cfg.OUTPUT_DIR` contains the last checkpoint (defined by a `last_checkpoint` file), resume from the file. Resuming means loading all available states (eg. optimizer and scheduler) and update iteration counter from the checkpoint. ``cfg.MODEL.WEIGHTS`` will not be used. Otherwise, this is considered as an independent training. The method will load model weights from the file `cfg.MODEL.WEIGHTS` (but will not load other states) and start from iteration 0. Args: resume (bool): whether to do resume or not """ self.checkpointer.resume_or_load(self.cfg.MODEL.WEIGHTS, resume=resume) if resume and self.checkpointer.has_checkpoint(): # The checkpoint stores the training iteration that just finished, thus we start # at the next iteration self.start_iter = self.iter + 1 def build_hooks(self): """ Build a list of default hooks, including timing, evaluation, checkpointing, lr scheduling, precise BN, writing events. Returns: list[HookBase]: """ cfg = self.cfg.clone() cfg.defrost() cfg.DATALOADER.NUM_WORKERS = 0 # save some memory and time for PreciseBN ret = [ hooks.IterationTimer(), hooks.LRScheduler(), hooks.PreciseBN( # Run at the same freq as (but before) evaluation. cfg.TEST.EVAL_PERIOD, self.model, # Build a new data loader to not affect training self.build_train_loader(cfg), cfg.TEST.PRECISE_BN.NUM_ITER, ) if cfg.TEST.PRECISE_BN.ENABLED and get_bn_modules(self.model) else None, ] # Do PreciseBN before checkpointer, because it updates the model and need to # be saved by checkpointer. # This is not always the best: if checkpointing has a different frequency, # some checkpoints may have more precise statistics than others. if comm.is_main_process(): ret.append(hooks.PeriodicCheckpointer(self.checkpointer, cfg.SOLVER.CHECKPOINT_PERIOD)) def test_and_save_results(): self._last_eval_results = self.test(self.cfg, self.model) return self._last_eval_results # Do evaluation after checkpointer, because then if it fails, # we can use the saved checkpoint to debug. ret.append(hooks.EvalHook(cfg.TEST.EVAL_PERIOD, test_and_save_results)) if comm.is_main_process(): # Here the default print/log frequency of each writer is used. # run writers in the end, so that evaluation metrics are written ret.append(hooks.PeriodicWriter(self.build_writers(), period=20)) return ret def build_writers(self): """ Build a list of writers to be used using :func:`default_writers()`. If you'd like a different list of writers, you can overwrite it in your trainer. Returns: list[EventWriter]: a list of :class:`EventWriter` objects. """ return default_writers(self.cfg.OUTPUT_DIR, self.max_iter) def train(self): """ Run training. Returns: OrderedDict of results, if evaluation is enabled. Otherwise None. """ super().train(self.start_iter, self.max_iter) if len(self.cfg.TEST.EXPECTED_RESULTS) and comm.is_main_process(): assert hasattr( self, "_last_eval_results" ), "No evaluation results obtained during training!" verify_results(self.cfg, self._last_eval_results) return self._last_eval_results def run_step(self): self._trainer.iter = self.iter self._trainer.run_step() def state_dict(self): ret = super().state_dict() ret["_trainer"] = self._trainer.state_dict() return ret def load_state_dict(self, state_dict): super().load_state_dict(state_dict) self._trainer.load_state_dict(state_dict["_trainer"]) @classmethod def build_model(cls, cfg): """ Returns: torch.nn.Module: It now calls :func:`detectron2.modeling.build_model`. Overwrite it if you'd like a different model. """ model = build_model(cfg) logger = logging.getLogger(__name__) logger.info("Model:\n{}".format(model)) return model @classmethod def build_optimizer(cls, cfg, model): """ Returns: torch.optim.Optimizer: It now calls :func:`detectron2.solver.build_optimizer`. Overwrite it if you'd like a different optimizer. """ return build_optimizer(cfg, model) @classmethod def build_lr_scheduler(cls, cfg, optimizer): """ It now calls :func:`detectron2.solver.build_lr_scheduler`. Overwrite it if you'd like a different scheduler. """ return build_lr_scheduler(cfg, optimizer) @classmethod def build_train_loader(cls, cfg): """ Returns: iterable It now calls :func:`detectron2.data.build_detection_train_loader`. Overwrite it if you'd like a different data loader. """ return build_detection_train_loader(cfg) @classmethod def build_test_loader(cls, cfg, dataset_name): """ Returns: iterable It now calls :func:`detectron2.data.build_detection_test_loader`. Overwrite it if you'd like a different data loader. """ return build_detection_test_loader(cfg, dataset_name) @classmethod def build_evaluator(cls, cfg, dataset_name): """ Returns: DatasetEvaluator or None It is not implemented by default. """ raise NotImplementedError( """ If you want DefaultTrainer to automatically run evaluation, please implement `build_evaluator()` in subclasses (see train_net.py for example). Alternatively, you can call evaluation functions yourself (see Colab balloon tutorial for example). """ ) @classmethod def test(cls, cfg, model, evaluators=None): """ Evaluate the given model. The given model is expected to already contain weights to evaluate. Args: cfg (CfgNode): model (nn.Module): evaluators (list[DatasetEvaluator] or None): if None, will call :meth:`build_evaluator`. Otherwise, must have the same length as ``cfg.DATASETS.TEST``. Returns: dict: a dict of result metrics """ logger = logging.getLogger(__name__) if isinstance(evaluators, DatasetEvaluator): evaluators = [evaluators] if evaluators is not None: assert len(cfg.DATASETS.TEST) == len(evaluators), "{} != {}".format( len(cfg.DATASETS.TEST), len(evaluators) ) results = OrderedDict() for idx, dataset_name in enumerate(cfg.DATASETS.TEST): data_loader = cls.build_test_loader(cfg, dataset_name) # When evaluators are passed in as arguments, # implicitly assume that evaluators can be created before data_loader. if evaluators is not None: evaluator = evaluators[idx] else: try: evaluator = cls.build_evaluator(cfg, dataset_name) except NotImplementedError: logger.warn( "No evaluator found. Use `DefaultTrainer.test(evaluators=)`, " "or implement its `build_evaluator` method." ) results[dataset_name] = {} continue results_i = inference_on_dataset(model, data_loader, evaluator) results[dataset_name] = results_i if comm.is_main_process(): assert isinstance( results_i, dict ), "Evaluator must return a dict on the main process. Got {} instead.".format( results_i ) logger.info("Evaluation results for {} in csv format:".format(dataset_name)) print_csv_format(results_i) if len(results) == 1: results = list(results.values())[0] return results @staticmethod def auto_scale_workers(cfg, num_workers: int): """ When the config is defined for certain number of workers (according to ``cfg.SOLVER.REFERENCE_WORLD_SIZE``) that's different from the number of workers currently in use, returns a new cfg where the total batch size is scaled so that the per-GPU batch size stays the same as the original ``IMS_PER_BATCH // REFERENCE_WORLD_SIZE``. Other config options are also scaled accordingly: * training steps and warmup steps are scaled inverse proportionally. * learning rate are scaled proportionally, following :paper:`ImageNet in 1h`. For example, with the original config like the following: .. code-block:: yaml IMS_PER_BATCH: 16 BASE_LR: 0.1 REFERENCE_WORLD_SIZE: 8 MAX_ITER: 5000 STEPS: (4000,) CHECKPOINT_PERIOD: 1000 When this config is used on 16 GPUs instead of the reference number 8, calling this method will return a new config with: .. code-block:: yaml IMS_PER_BATCH: 32 BASE_LR: 0.2 REFERENCE_WORLD_SIZE: 16 MAX_ITER: 2500 STEPS: (2000,) CHECKPOINT_PERIOD: 500 Note that both the original config and this new config can be trained on 16 GPUs. It's up to user whether to enable this feature (by setting ``REFERENCE_WORLD_SIZE``). Returns: CfgNode: a new config. Same as original if ``cfg.SOLVER.REFERENCE_WORLD_SIZE==0``. """ old_world_size = cfg.SOLVER.REFERENCE_WORLD_SIZE if old_world_size == 0 or old_world_size == num_workers: return cfg cfg = cfg.clone() frozen = cfg.is_frozen() cfg.defrost() assert ( cfg.SOLVER.IMS_PER_BATCH % old_world_size == 0 ), "Invalid REFERENCE_WORLD_SIZE in config!" scale = num_workers / old_world_size bs = cfg.SOLVER.IMS_PER_BATCH = int(round(cfg.SOLVER.IMS_PER_BATCH * scale)) lr = cfg.SOLVER.BASE_LR = cfg.SOLVER.BASE_LR * scale max_iter = cfg.SOLVER.MAX_ITER = int(round(cfg.SOLVER.MAX_ITER / scale)) warmup_iter = cfg.SOLVER.WARMUP_ITERS = int(round(cfg.SOLVER.WARMUP_ITERS / scale)) cfg.SOLVER.STEPS = tuple(int(round(s / scale)) for s in cfg.SOLVER.STEPS) cfg.TEST.EVAL_PERIOD = int(round(cfg.TEST.EVAL_PERIOD / scale)) cfg.SOLVER.CHECKPOINT_PERIOD = int(round(cfg.SOLVER.CHECKPOINT_PERIOD / scale)) cfg.SOLVER.REFERENCE_WORLD_SIZE = num_workers # maintain invariant logger = logging.getLogger(__name__) logger.info( f"Auto-scaling the config to batch_size={bs}, learning_rate={lr}, " f"max_iter={max_iter}, warmup={warmup_iter}." ) if frozen: cfg.freeze() return cfg # Access basic attributes from the underlying trainer for _attr in ["model", "data_loader", "optimizer"]: setattr( DefaultTrainer, _attr, property( # getter lambda self, x=_attr: getattr(self._trainer, x), # setter lambda self, value, x=_attr: setattr(self._trainer, x, value), ), ) ================================================ FILE: detectron2/detectron2/engine/hooks.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import datetime import itertools import logging import math import operator import os import tempfile import time import warnings from collections import Counter import torch from fvcore.common.checkpoint import Checkpointer from fvcore.common.checkpoint import PeriodicCheckpointer as _PeriodicCheckpointer from fvcore.common.param_scheduler import ParamScheduler from fvcore.common.timer import Timer from fvcore.nn.precise_bn import get_bn_modules, update_bn_stats import detectron2.utils.comm as comm from detectron2.evaluation.testing import flatten_results_dict from detectron2.solver import LRMultiplier from detectron2.utils.events import EventStorage, EventWriter from detectron2.utils.file_io import PathManager from .train_loop import HookBase __all__ = [ "CallbackHook", "IterationTimer", "PeriodicWriter", "PeriodicCheckpointer", "BestCheckpointer", "LRScheduler", "AutogradProfiler", "EvalHook", "PreciseBN", "TorchProfiler", "TorchMemoryStats", ] """ Implement some common hooks. """ class CallbackHook(HookBase): """ Create a hook using callback functions provided by the user. """ def __init__(self, *, before_train=None, after_train=None, before_step=None, after_step=None): """ Each argument is a function that takes one argument: the trainer. """ self._before_train = before_train self._before_step = before_step self._after_step = after_step self._after_train = after_train def before_train(self): if self._before_train: self._before_train(self.trainer) def after_train(self): if self._after_train: self._after_train(self.trainer) # The functions may be closures that hold reference to the trainer # Therefore, delete them to avoid circular reference. del self._before_train, self._after_train del self._before_step, self._after_step def before_step(self): if self._before_step: self._before_step(self.trainer) def after_step(self): if self._after_step: self._after_step(self.trainer) class IterationTimer(HookBase): """ Track the time spent for each iteration (each run_step call in the trainer). Print a summary in the end of training. This hook uses the time between the call to its :meth:`before_step` and :meth:`after_step` methods. Under the convention that :meth:`before_step` of all hooks should only take negligible amount of time, the :class:`IterationTimer` hook should be placed at the beginning of the list of hooks to obtain accurate timing. """ def __init__(self, warmup_iter=3): """ Args: warmup_iter (int): the number of iterations at the beginning to exclude from timing. """ self._warmup_iter = warmup_iter self._step_timer = Timer() self._start_time = time.perf_counter() self._total_timer = Timer() def before_train(self): self._start_time = time.perf_counter() self._total_timer.reset() self._total_timer.pause() def after_train(self): logger = logging.getLogger(__name__) total_time = time.perf_counter() - self._start_time total_time_minus_hooks = self._total_timer.seconds() hook_time = total_time - total_time_minus_hooks num_iter = self.trainer.storage.iter + 1 - self.trainer.start_iter - self._warmup_iter if num_iter > 0 and total_time_minus_hooks > 0: # Speed is meaningful only after warmup # NOTE this format is parsed by grep in some scripts logger.info( "Overall training speed: {} iterations in {} ({:.4f} s / it)".format( num_iter, str(datetime.timedelta(seconds=int(total_time_minus_hooks))), total_time_minus_hooks / num_iter, ) ) logger.info( "Total training time: {} ({} on hooks)".format( str(datetime.timedelta(seconds=int(total_time))), str(datetime.timedelta(seconds=int(hook_time))), ) ) def before_step(self): self._step_timer.reset() self._total_timer.resume() def after_step(self): # +1 because we're in after_step, the current step is done # but not yet counted iter_done = self.trainer.storage.iter - self.trainer.start_iter + 1 if iter_done >= self._warmup_iter: sec = self._step_timer.seconds() self.trainer.storage.put_scalars(time=sec) else: self._start_time = time.perf_counter() self._total_timer.reset() self._total_timer.pause() class PeriodicWriter(HookBase): """ Write events to EventStorage (by calling ``writer.write()``) periodically. It is executed every ``period`` iterations and after the last iteration. Note that ``period`` does not affect how data is smoothed by each writer. """ def __init__(self, writers, period=20): """ Args: writers (list[EventWriter]): a list of EventWriter objects period (int): """ self._writers = writers for w in writers: assert isinstance(w, EventWriter), w self._period = period def after_step(self): if (self.trainer.iter + 1) % self._period == 0 or ( self.trainer.iter == self.trainer.max_iter - 1 ): for writer in self._writers: writer.write() def after_train(self): for writer in self._writers: # If any new data is found (e.g. produced by other after_train), # write them before closing writer.write() writer.close() class PeriodicCheckpointer(_PeriodicCheckpointer, HookBase): """ Same as :class:`detectron2.checkpoint.PeriodicCheckpointer`, but as a hook. Note that when used as a hook, it is unable to save additional data other than what's defined by the given `checkpointer`. It is executed every ``period`` iterations and after the last iteration. """ def before_train(self): self.max_iter = self.trainer.max_iter def after_step(self): # No way to use **kwargs self.step(self.trainer.iter) class BestCheckpointer(HookBase): """ Checkpoints best weights based off given metric. This hook should be used in conjunction to and executed after the hook that produces the metric, e.g. `EvalHook`. """ def __init__( self, eval_period: int, checkpointer: Checkpointer, val_metric: str, mode: str = "max", file_prefix: str = "model_best", ) -> None: """ Args: eval_period (int): the period `EvalHook` is set to run. checkpointer: the checkpointer object used to save checkpoints. val_metric (str): validation metric to track for best checkpoint, e.g. "bbox/AP50" mode (str): one of {'max', 'min'}. controls whether the chosen val metric should be maximized or minimized, e.g. for "bbox/AP50" it should be "max" file_prefix (str): the prefix of checkpoint's filename, defaults to "model_best" """ self._logger = logging.getLogger(__name__) self._period = eval_period self._val_metric = val_metric assert mode in [ "max", "min", ], f'Mode "{mode}" to `BestCheckpointer` is unknown. It should be one of {"max", "min"}.' if mode == "max": self._compare = operator.gt else: self._compare = operator.lt self._checkpointer = checkpointer self._file_prefix = file_prefix self.best_metric = None self.best_iter = None def _update_best(self, val, iteration): if math.isnan(val) or math.isinf(val): return False self.best_metric = val self.best_iter = iteration return True def _best_checking(self): metric_tuple = self.trainer.storage.latest().get(self._val_metric) if metric_tuple is None: self._logger.warning( f"Given val metric {self._val_metric} does not seem to be computed/stored." "Will not be checkpointing based on it." ) return else: latest_metric, metric_iter = metric_tuple if self.best_metric is None: if self._update_best(latest_metric, metric_iter): additional_state = {"iteration": metric_iter} self._checkpointer.save(f"{self._file_prefix}", **additional_state) self._logger.info( f"Saved first model at {self.best_metric:0.5f} @ {self.best_iter} steps" ) elif self._compare(latest_metric, self.best_metric): additional_state = {"iteration": metric_iter} self._checkpointer.save(f"{self._file_prefix}", **additional_state) self._logger.info( f"Saved best model as latest eval score for {self._val_metric} is " f"{latest_metric:0.5f}, better than last best score " f"{self.best_metric:0.5f} @ iteration {self.best_iter}." ) self._update_best(latest_metric, metric_iter) else: self._logger.info( f"Not saving as latest eval score for {self._val_metric} is {latest_metric:0.5f}, " f"not better than best score {self.best_metric:0.5f} @ iteration {self.best_iter}." ) def after_step(self): # same conditions as `EvalHook` next_iter = self.trainer.iter + 1 if ( self._period > 0 and next_iter % self._period == 0 and next_iter != self.trainer.max_iter ): self._best_checking() def after_train(self): # same conditions as `EvalHook` if self.trainer.iter + 1 >= self.trainer.max_iter: self._best_checking() class LRScheduler(HookBase): """ A hook which executes a torch builtin LR scheduler and summarizes the LR. It is executed after every iteration. """ def __init__(self, optimizer=None, scheduler=None): """ Args: optimizer (torch.optim.Optimizer): scheduler (torch.optim.LRScheduler or fvcore.common.param_scheduler.ParamScheduler): if a :class:`ParamScheduler` object, it defines the multiplier over the base LR in the optimizer. If any argument is not given, will try to obtain it from the trainer. """ self._optimizer = optimizer self._scheduler = scheduler def before_train(self): self._optimizer = self._optimizer or self.trainer.optimizer if isinstance(self.scheduler, ParamScheduler): self._scheduler = LRMultiplier( self._optimizer, self.scheduler, self.trainer.max_iter, last_iter=self.trainer.iter - 1, ) self._best_param_group_id = LRScheduler.get_best_param_group_id(self._optimizer) @staticmethod def get_best_param_group_id(optimizer): # NOTE: some heuristics on what LR to summarize # summarize the param group with most parameters largest_group = max(len(g["params"]) for g in optimizer.param_groups) if largest_group == 1: # If all groups have one parameter, # then find the most common initial LR, and use it for summary lr_count = Counter([g["lr"] for g in optimizer.param_groups]) lr = lr_count.most_common()[0][0] for i, g in enumerate(optimizer.param_groups): if g["lr"] == lr: return i else: for i, g in enumerate(optimizer.param_groups): if len(g["params"]) == largest_group: return i def after_step(self): lr = self._optimizer.param_groups[self._best_param_group_id]["lr"] self.trainer.storage.put_scalar("lr", lr, smoothing_hint=False) self.scheduler.step() @property def scheduler(self): return self._scheduler or self.trainer.scheduler def state_dict(self): if isinstance(self.scheduler, torch.optim.lr_scheduler._LRScheduler): return self.scheduler.state_dict() return {} def load_state_dict(self, state_dict): if isinstance(self.scheduler, torch.optim.lr_scheduler._LRScheduler): logger = logging.getLogger(__name__) logger.info("Loading scheduler from state_dict ...") self.scheduler.load_state_dict(state_dict) class TorchProfiler(HookBase): """ A hook which runs `torch.profiler.profile`. Examples: :: hooks.TorchProfiler( lambda trainer: 10 < trainer.iter < 20, self.cfg.OUTPUT_DIR ) The above example will run the profiler for iteration 10~20 and dump results to ``OUTPUT_DIR``. We did not profile the first few iterations because they are typically slower than the rest. The result files can be loaded in the ``chrome://tracing`` page in chrome browser, and the tensorboard visualizations can be visualized using ``tensorboard --logdir OUTPUT_DIR/log`` """ def __init__(self, enable_predicate, output_dir, *, activities=None, save_tensorboard=True): """ Args: enable_predicate (callable[trainer -> bool]): a function which takes a trainer, and returns whether to enable the profiler. It will be called once every step, and can be used to select which steps to profile. output_dir (str): the output directory to dump tracing files. activities (iterable): same as in `torch.profiler.profile`. save_tensorboard (bool): whether to save tensorboard visualizations at (output_dir)/log/ """ self._enable_predicate = enable_predicate self._activities = activities self._output_dir = output_dir self._save_tensorboard = save_tensorboard def before_step(self): if self._enable_predicate(self.trainer): if self._save_tensorboard: on_trace_ready = torch.profiler.tensorboard_trace_handler( os.path.join( self._output_dir, "log", "profiler-tensorboard-iter{}".format(self.trainer.iter), ), f"worker{comm.get_rank()}", ) else: on_trace_ready = None self._profiler = torch.profiler.profile( activities=self._activities, on_trace_ready=on_trace_ready, record_shapes=True, profile_memory=True, with_stack=True, with_flops=True, ) self._profiler.__enter__() else: self._profiler = None def after_step(self): if self._profiler is None: return self._profiler.__exit__(None, None, None) if not self._save_tensorboard: PathManager.mkdirs(self._output_dir) out_file = os.path.join( self._output_dir, "profiler-trace-iter{}.json".format(self.trainer.iter) ) if "://" not in out_file: self._profiler.export_chrome_trace(out_file) else: # Support non-posix filesystems with tempfile.TemporaryDirectory(prefix="detectron2_profiler") as d: tmp_file = os.path.join(d, "tmp.json") self._profiler.export_chrome_trace(tmp_file) with open(tmp_file) as f: content = f.read() with PathManager.open(out_file, "w") as f: f.write(content) class AutogradProfiler(TorchProfiler): """ A hook which runs `torch.autograd.profiler.profile`. Examples: :: hooks.AutogradProfiler( lambda trainer: 10 < trainer.iter < 20, self.cfg.OUTPUT_DIR ) The above example will run the profiler for iteration 10~20 and dump results to ``OUTPUT_DIR``. We did not profile the first few iterations because they are typically slower than the rest. The result files can be loaded in the ``chrome://tracing`` page in chrome browser. Note: When used together with NCCL on older version of GPUs, autograd profiler may cause deadlock because it unnecessarily allocates memory on every device it sees. The memory management calls, if interleaved with NCCL calls, lead to deadlock on GPUs that do not support ``cudaLaunchCooperativeKernelMultiDevice``. """ def __init__(self, enable_predicate, output_dir, *, use_cuda=True): """ Args: enable_predicate (callable[trainer -> bool]): a function which takes a trainer, and returns whether to enable the profiler. It will be called once every step, and can be used to select which steps to profile. output_dir (str): the output directory to dump tracing files. use_cuda (bool): same as in `torch.autograd.profiler.profile`. """ warnings.warn("AutogradProfiler has been deprecated in favor of TorchProfiler.") self._enable_predicate = enable_predicate self._use_cuda = use_cuda self._output_dir = output_dir def before_step(self): if self._enable_predicate(self.trainer): self._profiler = torch.autograd.profiler.profile(use_cuda=self._use_cuda) self._profiler.__enter__() else: self._profiler = None class EvalHook(HookBase): """ Run an evaluation function periodically, and at the end of training. It is executed every ``eval_period`` iterations and after the last iteration. """ def __init__(self, eval_period, eval_function, eval_after_train=True): """ Args: eval_period (int): the period to run `eval_function`. Set to 0 to not evaluate periodically (but still evaluate after the last iteration if `eval_after_train` is True). eval_function (callable): a function which takes no arguments, and returns a nested dict of evaluation metrics. eval_after_train (bool): whether to evaluate after the last iteration Note: This hook must be enabled in all or none workers. If you would like only certain workers to perform evaluation, give other workers a no-op function (`eval_function=lambda: None`). """ self._period = eval_period self._func = eval_function self._eval_after_train = eval_after_train def _do_eval(self): results = self._func() if results: assert isinstance( results, dict ), "Eval function must return a dict. Got {} instead.".format(results) flattened_results = flatten_results_dict(results) for k, v in flattened_results.items(): try: v = float(v) except Exception as e: raise ValueError( "[EvalHook] eval_function should return a nested dict of float. " "Got '{}: {}' instead.".format(k, v) ) from e self.trainer.storage.put_scalars(**flattened_results, smoothing_hint=False) # Evaluation may take different time among workers. # A barrier make them start the next iteration together. comm.synchronize() def after_step(self): next_iter = self.trainer.iter + 1 if self._period > 0 and next_iter % self._period == 0: # do the last eval in after_train if next_iter != self.trainer.max_iter: self._do_eval() def after_train(self): # This condition is to prevent the eval from running after a failed training if self._eval_after_train and self.trainer.iter + 1 >= self.trainer.max_iter: self._do_eval() # func is likely a closure that holds reference to the trainer # therefore we clean it to avoid circular reference in the end del self._func class PreciseBN(HookBase): """ The standard implementation of BatchNorm uses EMA in inference, which is sometimes suboptimal. This class computes the true average of statistics rather than the moving average, and put true averages to every BN layer in the given model. It is executed every ``period`` iterations and after the last iteration. """ def __init__(self, period, model, data_loader, num_iter): """ Args: period (int): the period this hook is run, or 0 to not run during training. The hook will always run in the end of training. model (nn.Module): a module whose all BN layers in training mode will be updated by precise BN. Note that user is responsible for ensuring the BN layers to be updated are in training mode when this hook is triggered. data_loader (iterable): it will produce data to be run by `model(data)`. num_iter (int): number of iterations used to compute the precise statistics. """ self._logger = logging.getLogger(__name__) if len(get_bn_modules(model)) == 0: self._logger.info( "PreciseBN is disabled because model does not contain BN layers in training mode." ) self._disabled = True return self._model = model self._data_loader = data_loader self._num_iter = num_iter self._period = period self._disabled = False self._data_iter = None def after_step(self): next_iter = self.trainer.iter + 1 is_final = next_iter == self.trainer.max_iter if is_final or (self._period > 0 and next_iter % self._period == 0): self.update_stats() def update_stats(self): """ Update the model with precise statistics. Users can manually call this method. """ if self._disabled: return if self._data_iter is None: self._data_iter = iter(self._data_loader) def data_loader(): for num_iter in itertools.count(1): if num_iter % 100 == 0: self._logger.info( "Running precise-BN ... {}/{} iterations.".format(num_iter, self._num_iter) ) # This way we can reuse the same iterator yield next(self._data_iter) with EventStorage(): # capture events in a new storage to discard them self._logger.info( "Running precise-BN for {} iterations... ".format(self._num_iter) + "Note that this could produce different statistics every time." ) update_bn_stats(self._model, data_loader(), self._num_iter) class TorchMemoryStats(HookBase): """ Writes pytorch's cuda memory statistics periodically. """ def __init__(self, period=20, max_runs=10): """ Args: period (int): Output stats each 'period' iterations max_runs (int): Stop the logging after 'max_runs' """ self._logger = logging.getLogger(__name__) self._period = period self._max_runs = max_runs self._runs = 0 def after_step(self): if self._runs > self._max_runs: return if (self.trainer.iter + 1) % self._period == 0 or ( self.trainer.iter == self.trainer.max_iter - 1 ): if torch.cuda.is_available(): max_reserved_mb = torch.cuda.max_memory_reserved() / 1024.0 / 1024.0 reserved_mb = torch.cuda.memory_reserved() / 1024.0 / 1024.0 max_allocated_mb = torch.cuda.max_memory_allocated() / 1024.0 / 1024.0 allocated_mb = torch.cuda.memory_allocated() / 1024.0 / 1024.0 self._logger.info( ( " iter: {} " " max_reserved_mem: {:.0f}MB " " reserved_mem: {:.0f}MB " " max_allocated_mem: {:.0f}MB " " allocated_mem: {:.0f}MB " ).format( self.trainer.iter, max_reserved_mb, reserved_mb, max_allocated_mb, allocated_mb, ) ) self._runs += 1 if self._runs == self._max_runs: mem_summary = torch.cuda.memory_summary() self._logger.info("\n" + mem_summary) torch.cuda.reset_peak_memory_stats() ================================================ FILE: detectron2/detectron2/engine/launch.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging from datetime import timedelta import torch import torch.distributed as dist import torch.multiprocessing as mp from detectron2.utils import comm __all__ = ["DEFAULT_TIMEOUT", "launch"] DEFAULT_TIMEOUT = timedelta(minutes=30) def _find_free_port(): import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Binding to port 0 will cause the OS to find an available port for us sock.bind(("", 0)) port = sock.getsockname()[1] sock.close() # NOTE: there is still a chance the port could be taken by other processes. return port def launch( main_func, num_gpus_per_machine, num_machines=1, machine_rank=0, dist_url=None, args=(), timeout=DEFAULT_TIMEOUT, ): """ Launch multi-gpu or distributed training. This function must be called on all machines involved in the training. It will spawn child processes (defined by ``num_gpus_per_machine``) on each machine. Args: main_func: a function that will be called by `main_func(*args)` num_gpus_per_machine (int): number of GPUs per machine num_machines (int): the total number of machines machine_rank (int): the rank of this machine dist_url (str): url to connect to for distributed jobs, including protocol e.g. "tcp://127.0.0.1:8686". Can be set to "auto" to automatically select a free port on localhost timeout (timedelta): timeout of the distributed workers args (tuple): arguments passed to main_func """ world_size = num_machines * num_gpus_per_machine if world_size > 1: # https://github.com/pytorch/pytorch/pull/14391 # TODO prctl in spawned processes if dist_url == "auto": assert num_machines == 1, "dist_url=auto not supported in multi-machine jobs." port = _find_free_port() dist_url = f"tcp://127.0.0.1:{port}" if num_machines > 1 and dist_url.startswith("file://"): logger = logging.getLogger(__name__) logger.warning( "file:// is not a reliable init_method in multi-machine jobs. Prefer tcp://" ) mp.spawn( _distributed_worker, nprocs=num_gpus_per_machine, args=( main_func, world_size, num_gpus_per_machine, machine_rank, dist_url, args, timeout, ), daemon=False, ) else: main_func(*args) def _distributed_worker( local_rank, main_func, world_size, num_gpus_per_machine, machine_rank, dist_url, args, timeout=DEFAULT_TIMEOUT, ): assert torch.cuda.is_available(), "cuda is not available. Please check your installation." global_rank = machine_rank * num_gpus_per_machine + local_rank try: dist.init_process_group( backend="NCCL", init_method=dist_url, world_size=world_size, rank=global_rank, timeout=timeout, ) except Exception as e: logger = logging.getLogger(__name__) logger.error("Process group URL: {}".format(dist_url)) raise e # Setup the local process group (which contains ranks within the same machine) assert comm._LOCAL_PROCESS_GROUP is None num_machines = world_size // num_gpus_per_machine for i in range(num_machines): ranks_on_i = list(range(i * num_gpus_per_machine, (i + 1) * num_gpus_per_machine)) pg = dist.new_group(ranks_on_i) if i == machine_rank: comm._LOCAL_PROCESS_GROUP = pg assert num_gpus_per_machine <= torch.cuda.device_count() torch.cuda.set_device(local_rank) # synchronize is needed here to prevent a possible timeout after calling init_process_group # See: https://github.com/facebookresearch/maskrcnn-benchmark/issues/172 comm.synchronize() main_func(*args) ================================================ FILE: detectron2/detectron2/engine/train_loop.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np import time import weakref from typing import List, Mapping, Optional import torch from torch.nn.parallel import DataParallel, DistributedDataParallel import detectron2.utils.comm as comm from detectron2.utils.events import EventStorage, get_event_storage from detectron2.utils.logger import _log_api_usage __all__ = ["HookBase", "TrainerBase", "SimpleTrainer", "AMPTrainer"] class HookBase: """ Base class for hooks that can be registered with :class:`TrainerBase`. Each hook can implement 4 methods. The way they are called is demonstrated in the following snippet: :: hook.before_train() for iter in range(start_iter, max_iter): hook.before_step() trainer.run_step() hook.after_step() iter += 1 hook.after_train() Notes: 1. In the hook method, users can access ``self.trainer`` to access more properties about the context (e.g., model, current iteration, or config if using :class:`DefaultTrainer`). 2. A hook that does something in :meth:`before_step` can often be implemented equivalently in :meth:`after_step`. If the hook takes non-trivial time, it is strongly recommended to implement the hook in :meth:`after_step` instead of :meth:`before_step`. The convention is that :meth:`before_step` should only take negligible time. Following this convention will allow hooks that do care about the difference between :meth:`before_step` and :meth:`after_step` (e.g., timer) to function properly. """ trainer: "TrainerBase" = None """ A weak reference to the trainer object. Set by the trainer when the hook is registered. """ def before_train(self): """ Called before the first iteration. """ pass def after_train(self): """ Called after the last iteration. """ pass def before_step(self): """ Called before each iteration. """ pass def after_step(self): """ Called after each iteration. """ pass def state_dict(self): """ Hooks are stateless by default, but can be made checkpointable by implementing `state_dict` and `load_state_dict`. """ return {} class TrainerBase: """ Base class for iterative trainer with hooks. The only assumption we made here is: the training runs in a loop. A subclass can implement what the loop is. We made no assumptions about the existence of dataloader, optimizer, model, etc. Attributes: iter(int): the current iteration. start_iter(int): The iteration to start with. By convention the minimum possible value is 0. max_iter(int): The iteration to end training. storage(EventStorage): An EventStorage that's opened during the course of training. """ def __init__(self) -> None: self._hooks: List[HookBase] = [] self.iter: int = 0 self.start_iter: int = 0 self.max_iter: int self.storage: EventStorage _log_api_usage("trainer." + self.__class__.__name__) def register_hooks(self, hooks: List[Optional[HookBase]]) -> None: """ Register hooks to the trainer. The hooks are executed in the order they are registered. Args: hooks (list[Optional[HookBase]]): list of hooks """ hooks = [h for h in hooks if h is not None] for h in hooks: assert isinstance(h, HookBase) # To avoid circular reference, hooks and trainer cannot own each other. # This normally does not matter, but will cause memory leak if the # involved objects contain __del__: # See http://engineering.hearsaysocial.com/2013/06/16/circular-references-in-python/ h.trainer = weakref.proxy(self) self._hooks.extend(hooks) def train(self, start_iter: int, max_iter: int): """ Args: start_iter, max_iter (int): See docs above """ logger = logging.getLogger(__name__) logger.info("Starting training from iteration {}".format(start_iter)) self.iter = self.start_iter = start_iter self.max_iter = max_iter with EventStorage(start_iter) as self.storage: try: self.before_train() for self.iter in range(start_iter, max_iter): self.before_step() self.run_step() self.after_step() # self.iter == max_iter can be used by `after_train` to # tell whether the training successfully finished or failed # due to exceptions. self.iter += 1 except Exception: logger.exception("Exception during training:") raise finally: self.after_train() def before_train(self): for h in self._hooks: h.before_train() def after_train(self): self.storage.iter = self.iter for h in self._hooks: h.after_train() def before_step(self): # Maintain the invariant that storage.iter == trainer.iter # for the entire execution of each step self.storage.iter = self.iter for h in self._hooks: h.before_step() def after_step(self): for h in self._hooks: h.after_step() def run_step(self): raise NotImplementedError def state_dict(self): ret = {"iteration": self.iter} hooks_state = {} for h in self._hooks: sd = h.state_dict() if sd: name = type(h).__qualname__ if name in hooks_state: # TODO handle repetitive stateful hooks continue hooks_state[name] = sd if hooks_state: ret["hooks"] = hooks_state return ret def load_state_dict(self, state_dict): logger = logging.getLogger(__name__) self.iter = state_dict["iteration"] for key, value in state_dict.get("hooks", {}).items(): for h in self._hooks: try: name = type(h).__qualname__ except AttributeError: continue if name == key: h.load_state_dict(value) break else: logger.warning(f"Cannot find the hook '{key}', its state_dict is ignored.") class SimpleTrainer(TrainerBase): """ A simple trainer for the most common type of task: single-cost single-optimizer single-data-source iterative optimization, optionally using data-parallelism. It assumes that every step, you: 1. Compute the loss with a data from the data_loader. 2. Compute the gradients with the above loss. 3. Update the model with the optimizer. All other tasks during training (checkpointing, logging, evaluation, LR schedule) are maintained by hooks, which can be registered by :meth:`TrainerBase.register_hooks`. If you want to do anything fancier than this, either subclass TrainerBase and implement your own `run_step`, or write your own training loop. """ def __init__(self, model, data_loader, optimizer): """ Args: model: a torch Module. Takes a data from data_loader and returns a dict of losses. data_loader: an iterable. Contains data to be used to call model. optimizer: a torch optimizer. """ super().__init__() """ We set the model to training mode in the trainer. However it's valid to train a model that's in eval mode. If you want your model (or a submodule of it) to behave like evaluation during training, you can overwrite its train() method. """ model.train() self.model = model self.data_loader = data_loader # to access the data loader iterator, call `self._data_loader_iter` self._data_loader_iter_obj = None self.optimizer = optimizer def run_step(self): """ Implement the standard training logic described above. """ assert self.model.training, "[SimpleTrainer] model was changed to eval mode!" start = time.perf_counter() """ If you want to do something with the data, you can wrap the dataloader. """ data = next(self._data_loader_iter) data_time = time.perf_counter() - start """ If you want to do something with the losses, you can wrap the model. """ loss_dict = self.model(data) if isinstance(loss_dict, torch.Tensor): losses = loss_dict loss_dict = {"total_loss": loss_dict} else: losses = sum(loss_dict.values()) """ If you need to accumulate gradients or do something similar, you can wrap the optimizer with your custom `zero_grad()` method. """ self.optimizer.zero_grad() losses.backward() self._write_metrics(loss_dict, data_time) """ If you need gradient clipping/scaling or other processing, you can wrap the optimizer with your custom `step()` method. But it is suboptimal as explained in https://arxiv.org/abs/2006.15704 Sec 3.2.4 """ self.optimizer.step() @property def _data_loader_iter(self): # only create the data loader iterator when it is used if self._data_loader_iter_obj is None: self._data_loader_iter_obj = iter(self.data_loader) return self._data_loader_iter_obj def reset_data_loader(self, data_loader_builder): """ Delete and replace the current data loader with a new one, which will be created by calling `data_loader_builder` (without argument). """ del self.data_loader data_loader = data_loader_builder() self.data_loader = data_loader self._data_loader_iter_obj = None def _write_metrics( self, loss_dict: Mapping[str, torch.Tensor], data_time: float, prefix: str = "", ) -> None: SimpleTrainer.write_metrics(loss_dict, data_time, prefix) @staticmethod def write_metrics( loss_dict: Mapping[str, torch.Tensor], data_time: float, prefix: str = "", ) -> None: """ Args: loss_dict (dict): dict of scalar losses data_time (float): time taken by the dataloader iteration prefix (str): prefix for logging keys """ metrics_dict = {k: v.detach().cpu().item() for k, v in loss_dict.items()} metrics_dict["data_time"] = data_time # Gather metrics among all workers for logging # This assumes we do DDP-style training, which is currently the only # supported method in detectron2. all_metrics_dict = comm.gather(metrics_dict) if comm.is_main_process(): storage = get_event_storage() # data_time among workers can have high variance. The actual latency # caused by data_time is the maximum among workers. data_time = np.max([x.pop("data_time") for x in all_metrics_dict]) storage.put_scalar("data_time", data_time) # average the rest metrics metrics_dict = { k: np.mean([x[k] for x in all_metrics_dict]) for k in all_metrics_dict[0].keys() } total_losses_reduced = sum(metrics_dict.values()) if not np.isfinite(total_losses_reduced): raise FloatingPointError( f"Loss became infinite or NaN at iteration={storage.iter}!\n" f"loss_dict = {metrics_dict}" ) storage.put_scalar("{}total_loss".format(prefix), total_losses_reduced) if len(metrics_dict) > 1: storage.put_scalars(**metrics_dict) def state_dict(self): ret = super().state_dict() ret["optimizer"] = self.optimizer.state_dict() return ret def load_state_dict(self, state_dict): super().load_state_dict(state_dict) self.optimizer.load_state_dict(state_dict["optimizer"]) class AMPTrainer(SimpleTrainer): """ Like :class:`SimpleTrainer`, but uses PyTorch's native automatic mixed precision in the training loop. """ def __init__(self, model, data_loader, optimizer, grad_scaler=None): """ Args: model, data_loader, optimizer: same as in :class:`SimpleTrainer`. grad_scaler: torch GradScaler to automatically scale gradients. """ unsupported = "AMPTrainer does not support single-process multi-device training!" if isinstance(model, DistributedDataParallel): assert not (model.device_ids and len(model.device_ids) > 1), unsupported assert not isinstance(model, DataParallel), unsupported super().__init__(model, data_loader, optimizer) if grad_scaler is None: from torch.cuda.amp import GradScaler grad_scaler = GradScaler() self.grad_scaler = grad_scaler def run_step(self): """ Implement the AMP training logic. """ assert self.model.training, "[AMPTrainer] model was changed to eval mode!" assert torch.cuda.is_available(), "[AMPTrainer] CUDA is required for AMP training!" from torch.cuda.amp import autocast start = time.perf_counter() data = next(self._data_loader_iter) data_time = time.perf_counter() - start with autocast(): loss_dict = self.model(data) if isinstance(loss_dict, torch.Tensor): losses = loss_dict loss_dict = {"total_loss": loss_dict} else: losses = sum(loss_dict.values()) self.optimizer.zero_grad() self.grad_scaler.scale(losses).backward() self._write_metrics(loss_dict, data_time) self.grad_scaler.step(self.optimizer) self.grad_scaler.update() def state_dict(self): ret = super().state_dict() ret["grad_scaler"] = self.grad_scaler.state_dict() return ret def load_state_dict(self, state_dict): super().load_state_dict(state_dict) self.grad_scaler.load_state_dict(state_dict["grad_scaler"]) ================================================ FILE: detectron2/detectron2/evaluation/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .cityscapes_evaluation import CityscapesInstanceEvaluator, CityscapesSemSegEvaluator from .coco_evaluation import COCOEvaluator from .rotated_coco_evaluation import RotatedCOCOEvaluator from .evaluator import DatasetEvaluator, DatasetEvaluators, inference_context, inference_on_dataset from .lvis_evaluation import LVISEvaluator from .panoptic_evaluation import COCOPanopticEvaluator from .pascal_voc_evaluation import PascalVOCDetectionEvaluator from .sem_seg_evaluation import SemSegEvaluator from .testing import print_csv_format, verify_results __all__ = [k for k in globals().keys() if not k.startswith("_")] ================================================ FILE: detectron2/detectron2/evaluation/cityscapes_evaluation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import glob import logging import numpy as np import os import tempfile from collections import OrderedDict import torch from PIL import Image from detectron2.data import MetadataCatalog from detectron2.utils import comm from detectron2.utils.file_io import PathManager from .evaluator import DatasetEvaluator class CityscapesEvaluator(DatasetEvaluator): """ Base class for evaluation using cityscapes API. """ def __init__(self, dataset_name): """ Args: dataset_name (str): the name of the dataset. It must have the following metadata associated with it: "thing_classes", "gt_dir". """ self._metadata = MetadataCatalog.get(dataset_name) self._cpu_device = torch.device("cpu") self._logger = logging.getLogger(__name__) def reset(self): self._working_dir = tempfile.TemporaryDirectory(prefix="cityscapes_eval_") self._temp_dir = self._working_dir.name # All workers will write to the same results directory # TODO this does not work in distributed training assert ( comm.get_local_size() == comm.get_world_size() ), "CityscapesEvaluator currently do not work with multiple machines." self._temp_dir = comm.all_gather(self._temp_dir)[0] if self._temp_dir != self._working_dir.name: self._working_dir.cleanup() self._logger.info( "Writing cityscapes results to temporary directory {} ...".format(self._temp_dir) ) class CityscapesInstanceEvaluator(CityscapesEvaluator): """ Evaluate instance segmentation results on cityscapes dataset using cityscapes API. Note: * It does not work in multi-machine distributed training. * It contains a synchronization, therefore has to be used on all ranks. * Only the main process runs evaluation. """ def process(self, inputs, outputs): from cityscapesscripts.helpers.labels import name2label for input, output in zip(inputs, outputs): file_name = input["file_name"] basename = os.path.splitext(os.path.basename(file_name))[0] pred_txt = os.path.join(self._temp_dir, basename + "_pred.txt") if "instances" in output: output = output["instances"].to(self._cpu_device) num_instances = len(output) with open(pred_txt, "w") as fout: for i in range(num_instances): pred_class = output.pred_classes[i] classes = self._metadata.thing_classes[pred_class] class_id = name2label[classes].id score = output.scores[i] mask = output.pred_masks[i].numpy().astype("uint8") png_filename = os.path.join( self._temp_dir, basename + "_{}_{}.png".format(i, classes) ) Image.fromarray(mask * 255).save(png_filename) fout.write( "{} {} {}\n".format(os.path.basename(png_filename), class_id, score) ) else: # Cityscapes requires a prediction file for every ground truth image. with open(pred_txt, "w") as fout: pass def evaluate(self): """ Returns: dict: has a key "segm", whose value is a dict of "AP" and "AP50". """ comm.synchronize() if comm.get_rank() > 0: return import cityscapesscripts.evaluation.evalInstanceLevelSemanticLabeling as cityscapes_eval self._logger.info("Evaluating results under {} ...".format(self._temp_dir)) # set some global states in cityscapes evaluation API, before evaluating cityscapes_eval.args.predictionPath = os.path.abspath(self._temp_dir) cityscapes_eval.args.predictionWalk = None cityscapes_eval.args.JSONOutput = False cityscapes_eval.args.colorized = False cityscapes_eval.args.gtInstancesFile = os.path.join(self._temp_dir, "gtInstances.json") # These lines are adopted from # https://github.com/mcordts/cityscapesScripts/blob/master/cityscapesscripts/evaluation/evalInstanceLevelSemanticLabeling.py # noqa gt_dir = PathManager.get_local_path(self._metadata.gt_dir) groundTruthImgList = glob.glob(os.path.join(gt_dir, "*", "*_gtFine_instanceIds.png")) assert len( groundTruthImgList ), "Cannot find any ground truth images to use for evaluation. Searched for: {}".format( cityscapes_eval.args.groundTruthSearch ) predictionImgList = [] for gt in groundTruthImgList: predictionImgList.append(cityscapes_eval.getPrediction(gt, cityscapes_eval.args)) results = cityscapes_eval.evaluateImgLists( predictionImgList, groundTruthImgList, cityscapes_eval.args )["averages"] ret = OrderedDict() ret["segm"] = {"AP": results["allAp"] * 100, "AP50": results["allAp50%"] * 100} self._working_dir.cleanup() return ret class CityscapesSemSegEvaluator(CityscapesEvaluator): """ Evaluate semantic segmentation results on cityscapes dataset using cityscapes API. Note: * It does not work in multi-machine distributed training. * It contains a synchronization, therefore has to be used on all ranks. * Only the main process runs evaluation. """ def process(self, inputs, outputs): from cityscapesscripts.helpers.labels import trainId2label for input, output in zip(inputs, outputs): file_name = input["file_name"] basename = os.path.splitext(os.path.basename(file_name))[0] pred_filename = os.path.join(self._temp_dir, basename + "_pred.png") output = output["sem_seg"].argmax(dim=0).to(self._cpu_device).numpy() pred = 255 * np.ones(output.shape, dtype=np.uint8) for train_id, label in trainId2label.items(): if label.ignoreInEval: continue pred[output == train_id] = label.id Image.fromarray(pred).save(pred_filename) def evaluate(self): comm.synchronize() if comm.get_rank() > 0: return # Load the Cityscapes eval script *after* setting the required env var, # since the script reads CITYSCAPES_DATASET into global variables at load time. import cityscapesscripts.evaluation.evalPixelLevelSemanticLabeling as cityscapes_eval self._logger.info("Evaluating results under {} ...".format(self._temp_dir)) # set some global states in cityscapes evaluation API, before evaluating cityscapes_eval.args.predictionPath = os.path.abspath(self._temp_dir) cityscapes_eval.args.predictionWalk = None cityscapes_eval.args.JSONOutput = False cityscapes_eval.args.colorized = False # These lines are adopted from # https://github.com/mcordts/cityscapesScripts/blob/master/cityscapesscripts/evaluation/evalPixelLevelSemanticLabeling.py # noqa gt_dir = PathManager.get_local_path(self._metadata.gt_dir) groundTruthImgList = glob.glob(os.path.join(gt_dir, "*", "*_gtFine_labelIds.png")) assert len( groundTruthImgList ), "Cannot find any ground truth images to use for evaluation. Searched for: {}".format( cityscapes_eval.args.groundTruthSearch ) predictionImgList = [] for gt in groundTruthImgList: predictionImgList.append(cityscapes_eval.getPrediction(cityscapes_eval.args, gt)) results = cityscapes_eval.evaluateImgLists( predictionImgList, groundTruthImgList, cityscapes_eval.args ) ret = OrderedDict() ret["sem_seg"] = { "IoU": 100.0 * results["averageScoreClasses"], "iIoU": 100.0 * results["averageScoreInstClasses"], "IoU_sup": 100.0 * results["averageScoreCategories"], "iIoU_sup": 100.0 * results["averageScoreInstCategories"], } self._working_dir.cleanup() return ret ================================================ FILE: detectron2/detectron2/evaluation/coco_evaluation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import contextlib import copy import io import itertools import json import logging import numpy as np import os import pickle from collections import OrderedDict import pycocotools.mask as mask_util import torch from pycocotools.coco import COCO from pycocotools.cocoeval import COCOeval from tabulate import tabulate import detectron2.utils.comm as comm from detectron2.config import CfgNode from detectron2.data import MetadataCatalog from detectron2.data.datasets.coco import convert_to_coco_json from detectron2.structures import Boxes, BoxMode, pairwise_iou from detectron2.utils.file_io import PathManager from detectron2.utils.logger import create_small_table from .evaluator import DatasetEvaluator try: from detectron2.evaluation.fast_eval_api import COCOeval_opt except ImportError: COCOeval_opt = COCOeval class COCOEvaluator(DatasetEvaluator): """ Evaluate AR for object proposals, AP for instance detection/segmentation, AP for keypoint detection outputs using COCO's metrics. See http://cocodataset.org/#detection-eval and http://cocodataset.org/#keypoints-eval to understand its metrics. The metrics range from 0 to 100 (instead of 0 to 1), where a -1 or NaN means the metric cannot be computed (e.g. due to no predictions made). In addition to COCO, this evaluator is able to support any bounding box detection, instance segmentation, or keypoint detection dataset. """ def __init__( self, dataset_name, tasks=None, distributed=True, output_dir=None, *, max_dets_per_image=None, use_fast_impl=True, kpt_oks_sigmas=(), allow_cached_coco=True, ): """ Args: dataset_name (str): name of the dataset to be evaluated. It must have either the following corresponding metadata: "json_file": the path to the COCO format annotation Or it must be in detectron2's standard dataset format so it can be converted to COCO format automatically. tasks (tuple[str]): tasks that can be evaluated under the given configuration. A task is one of "bbox", "segm", "keypoints". By default, will infer this automatically from predictions. distributed (True): if True, will collect results from all ranks and run evaluation in the main process. Otherwise, will only evaluate the results in the current process. output_dir (str): optional, an output directory to dump all results predicted on the dataset. The dump contains two files: 1. "instances_predictions.pth" a file that can be loaded with `torch.load` and contains all the results in the format they are produced by the model. 2. "coco_instances_results.json" a json file in COCO's result format. max_dets_per_image (int): limit on the maximum number of detections per image. By default in COCO, this limit is to 100, but this can be customized to be greater, as is needed in evaluation metrics AP fixed and AP pool (see https://arxiv.org/pdf/2102.01066.pdf) This doesn't affect keypoint evaluation. use_fast_impl (bool): use a fast but **unofficial** implementation to compute AP. Although the results should be very close to the official implementation in COCO API, it is still recommended to compute results with the official API for use in papers. The faster implementation also uses more RAM. kpt_oks_sigmas (list[float]): The sigmas used to calculate keypoint OKS. See http://cocodataset.org/#keypoints-eval When empty, it will use the defaults in COCO. Otherwise it should be the same length as ROI_KEYPOINT_HEAD.NUM_KEYPOINTS. allow_cached_coco (bool): Whether to use cached coco json from previous validation runs. You should set this to False if you need to use different validation data. Defaults to True. """ self._logger = logging.getLogger(__name__) self._distributed = distributed self._output_dir = output_dir if use_fast_impl and (COCOeval_opt is COCOeval): self._logger.info("Fast COCO eval is not built. Falling back to official COCO eval.") use_fast_impl = False self._use_fast_impl = use_fast_impl # COCOeval requires the limit on the number of detections per image (maxDets) to be a list # with at least 3 elements. The default maxDets in COCOeval is [1, 10, 100], in which the # 3rd element (100) is used as the limit on the number of detections per image when # evaluating AP. COCOEvaluator expects an integer for max_dets_per_image, so for COCOeval, # we reformat max_dets_per_image into [1, 10, max_dets_per_image], based on the defaults. if max_dets_per_image is None: max_dets_per_image = [1, 10, 100] else: max_dets_per_image = [1, 10, max_dets_per_image] self._max_dets_per_image = max_dets_per_image if tasks is not None and isinstance(tasks, CfgNode): kpt_oks_sigmas = ( tasks.TEST.KEYPOINT_OKS_SIGMAS if not kpt_oks_sigmas else kpt_oks_sigmas ) self._logger.warn( "COCO Evaluator instantiated using config, this is deprecated behavior." " Please pass in explicit arguments instead." ) self._tasks = None # Infering it from predictions should be better else: self._tasks = tasks self._cpu_device = torch.device("cpu") self._metadata = MetadataCatalog.get(dataset_name) if not hasattr(self._metadata, "json_file"): if output_dir is None: raise ValueError( "output_dir must be provided to COCOEvaluator " "for datasets not in COCO format." ) self._logger.info(f"Trying to convert '{dataset_name}' to COCO format ...") cache_path = os.path.join(output_dir, f"{dataset_name}_coco_format.json") self._metadata.json_file = cache_path convert_to_coco_json(dataset_name, cache_path, allow_cached=allow_cached_coco) json_file = PathManager.get_local_path(self._metadata.json_file) with contextlib.redirect_stdout(io.StringIO()): self._coco_api = COCO(json_file) # Test set json files do not contain annotations (evaluation must be # performed using the COCO evaluation server). self._do_evaluation = "annotations" in self._coco_api.dataset if self._do_evaluation: self._kpt_oks_sigmas = kpt_oks_sigmas def reset(self): self._predictions = [] def process(self, inputs, outputs): """ Args: inputs: the inputs to a COCO model (e.g., GeneralizedRCNN). It is a list of dict. Each dict corresponds to an image and contains keys like "height", "width", "file_name", "image_id". outputs: the outputs of a COCO model. It is a list of dicts with key "instances" that contains :class:`Instances`. """ for input, output in zip(inputs, outputs): prediction = {"image_id": input["image_id"]} if "instances" in output: instances = output["instances"].to(self._cpu_device) prediction["instances"] = instances_to_coco_json(instances, input["image_id"]) if "proposals" in output: prediction["proposals"] = output["proposals"].to(self._cpu_device) if len(prediction) > 1: self._predictions.append(prediction) def evaluate(self, img_ids=None): """ Args: img_ids: a list of image IDs to evaluate on. Default to None for the whole dataset """ if self._distributed: comm.synchronize() predictions = comm.gather(self._predictions, dst=0) predictions = list(itertools.chain(*predictions)) if not comm.is_main_process(): return {} else: predictions = self._predictions if len(predictions) == 0: self._logger.warning("[COCOEvaluator] Did not receive valid predictions.") return {} if self._output_dir: PathManager.mkdirs(self._output_dir) file_path = os.path.join(self._output_dir, "instances_predictions.pth") with PathManager.open(file_path, "wb") as f: torch.save(predictions, f) self._results = OrderedDict() if "proposals" in predictions[0]: self._eval_box_proposals(predictions) if "instances" in predictions[0]: self._eval_predictions(predictions, img_ids=img_ids) # Copy so the caller can do whatever with results return copy.deepcopy(self._results) def _tasks_from_predictions(self, predictions): """ Get COCO API "tasks" (i.e. iou_type) from COCO-format predictions. """ tasks = {"bbox"} for pred in predictions: if "segmentation" in pred: tasks.add("segm") if "keypoints" in pred: tasks.add("keypoints") return sorted(tasks) def _eval_predictions(self, predictions, img_ids=None): """ Evaluate predictions. Fill self._results with the metrics of the tasks. """ self._logger.info("Preparing results for COCO format ...") coco_results = list(itertools.chain(*[x["instances"] for x in predictions])) tasks = self._tasks or self._tasks_from_predictions(coco_results) # unmap the category ids for COCO if hasattr(self._metadata, "thing_dataset_id_to_contiguous_id"): dataset_id_to_contiguous_id = self._metadata.thing_dataset_id_to_contiguous_id all_contiguous_ids = list(dataset_id_to_contiguous_id.values()) num_classes = len(all_contiguous_ids) assert min(all_contiguous_ids) == 0 and max(all_contiguous_ids) == num_classes - 1 reverse_id_mapping = {v: k for k, v in dataset_id_to_contiguous_id.items()} for result in coco_results: category_id = result["category_id"] assert category_id < num_classes, ( f"A prediction has class={category_id}, " f"but the dataset only has {num_classes} classes and " f"predicted class id should be in [0, {num_classes - 1}]." ) result["category_id"] = reverse_id_mapping[category_id] if self._output_dir: file_path = os.path.join(self._output_dir, "coco_instances_results.json") self._logger.info("Saving results to {}".format(file_path)) with PathManager.open(file_path, "w") as f: f.write(json.dumps(coco_results)) f.flush() if not self._do_evaluation: self._logger.info("Annotations are not available for evaluation.") return self._logger.info( "Evaluating predictions with {} COCO API...".format( "unofficial" if self._use_fast_impl else "official" ) ) for task in sorted(tasks): assert task in {"bbox", "segm", "keypoints"}, f"Got unknown task: {task}!" coco_eval = ( _evaluate_predictions_on_coco( self._coco_api, coco_results, task, kpt_oks_sigmas=self._kpt_oks_sigmas, use_fast_impl=self._use_fast_impl, img_ids=img_ids, max_dets_per_image=self._max_dets_per_image, ) if len(coco_results) > 0 else None # cocoapi does not handle empty results very well ) res = self._derive_coco_results( coco_eval, task, class_names=self._metadata.get("thing_classes") ) self._results[task] = res def _eval_box_proposals(self, predictions): """ Evaluate the box proposals in predictions. Fill self._results with the metrics for "box_proposals" task. """ if self._output_dir: # Saving generated box proposals to file. # Predicted box_proposals are in XYXY_ABS mode. bbox_mode = BoxMode.XYXY_ABS.value ids, boxes, objectness_logits = [], [], [] for prediction in predictions: ids.append(prediction["image_id"]) boxes.append(prediction["proposals"].proposal_boxes.tensor.numpy()) objectness_logits.append(prediction["proposals"].objectness_logits.numpy()) proposal_data = { "boxes": boxes, "objectness_logits": objectness_logits, "ids": ids, "bbox_mode": bbox_mode, } with PathManager.open(os.path.join(self._output_dir, "box_proposals.pkl"), "wb") as f: pickle.dump(proposal_data, f) if not self._do_evaluation: self._logger.info("Annotations are not available for evaluation.") return self._logger.info("Evaluating bbox proposals ...") res = {} areas = {"all": "", "small": "s", "medium": "m", "large": "l"} for limit in [100, 1000]: for area, suffix in areas.items(): stats = _evaluate_box_proposals(predictions, self._coco_api, area=area, limit=limit) key = "AR{}@{:d}".format(suffix, limit) res[key] = float(stats["ar"].item() * 100) self._logger.info("Proposal metrics: \n" + create_small_table(res)) self._results["box_proposals"] = res def _derive_coco_results(self, coco_eval, iou_type, class_names=None): """ Derive the desired score numbers from summarized COCOeval. Args: coco_eval (None or COCOEval): None represents no predictions from model. iou_type (str): class_names (None or list[str]): if provided, will use it to predict per-category AP. Returns: a dict of {metric name: score} """ metrics = { "bbox": ["AP", "AP50", "AP75", "APs", "APm", "APl"], "segm": ["AP", "AP50", "AP75", "APs", "APm", "APl"], "keypoints": ["AP", "AP50", "AP75", "APm", "APl"], }[iou_type] if coco_eval is None: self._logger.warn("No predictions from the model!") return {metric: float("nan") for metric in metrics} # the standard metrics results = { metric: float(coco_eval.stats[idx] * 100 if coco_eval.stats[idx] >= 0 else "nan") for idx, metric in enumerate(metrics) } self._logger.info( "Evaluation results for {}: \n".format(iou_type) + create_small_table(results) ) if not np.isfinite(sum(results.values())): self._logger.info("Some metrics cannot be computed and is shown as NaN.") if class_names is None or len(class_names) <= 1: return results # Compute per-category AP # from https://github.com/facebookresearch/Detectron/blob/a6a835f5b8208c45d0dce217ce9bbda915f44df7/detectron/datasets/json_dataset_evaluator.py#L222-L252 # noqa precisions = coco_eval.eval["precision"] # precision has dims (iou, recall, cls, area range, max dets) assert len(class_names) == precisions.shape[2] results_per_category = [] for idx, name in enumerate(class_names): # area range index 0: all area ranges # max dets index -1: typically 100 per image precision = precisions[:, :, idx, 0, -1] precision = precision[precision > -1] ap = np.mean(precision) if precision.size else float("nan") results_per_category.append(("{}".format(name), float(ap * 100))) # tabulate it N_COLS = min(6, len(results_per_category) * 2) results_flatten = list(itertools.chain(*results_per_category)) results_2d = itertools.zip_longest(*[results_flatten[i::N_COLS] for i in range(N_COLS)]) table = tabulate( results_2d, tablefmt="pipe", floatfmt=".3f", headers=["category", "AP"] * (N_COLS // 2), numalign="left", ) self._logger.info("Per-category {} AP: \n".format(iou_type) + table) results.update({"AP-" + name: ap for name, ap in results_per_category}) return results def instances_to_coco_json(instances, img_id): """ Dump an "Instances" object to a COCO-format json that's used for evaluation. Args: instances (Instances): img_id (int): the image id Returns: list[dict]: list of json annotations in COCO format. """ num_instance = len(instances) if num_instance == 0: return [] boxes = instances.pred_boxes.tensor.numpy() boxes = BoxMode.convert(boxes, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS) boxes = boxes.tolist() scores = instances.scores.tolist() classes = instances.pred_classes.tolist() has_mask = instances.has("pred_masks") if has_mask: # use RLE to encode the masks, because they are too large and takes memory # since this evaluator stores outputs of the entire dataset rles = [ mask_util.encode(np.array(mask[:, :, None], order="F", dtype="uint8"))[0] for mask in instances.pred_masks ] for rle in rles: # "counts" is an array encoded by mask_util as a byte-stream. Python3's # json writer which always produces strings cannot serialize a bytestream # unless you decode it. Thankfully, utf-8 works out (which is also what # the pycocotools/_mask.pyx does). rle["counts"] = rle["counts"].decode("utf-8") has_keypoints = instances.has("pred_keypoints") if has_keypoints: keypoints = instances.pred_keypoints results = [] for k in range(num_instance): result = { "image_id": img_id, "category_id": classes[k], "bbox": boxes[k], "score": scores[k], } if has_mask: result["segmentation"] = rles[k] if has_keypoints: # In COCO annotations, # keypoints coordinates are pixel indices. # However our predictions are floating point coordinates. # Therefore we subtract 0.5 to be consistent with the annotation format. # This is the inverse of data loading logic in `datasets/coco.py`. keypoints[k][:, :2] -= 0.5 result["keypoints"] = keypoints[k].flatten().tolist() results.append(result) return results # inspired from Detectron: # https://github.com/facebookresearch/Detectron/blob/a6a835f5b8208c45d0dce217ce9bbda915f44df7/detectron/datasets/json_dataset_evaluator.py#L255 # noqa def _evaluate_box_proposals(dataset_predictions, coco_api, thresholds=None, area="all", limit=None): """ Evaluate detection proposal recall metrics. This function is a much faster alternative to the official COCO API recall evaluation code. However, it produces slightly different results. """ # Record max overlap value for each gt box # Return vector of overlap values areas = { "all": 0, "small": 1, "medium": 2, "large": 3, "96-128": 4, "128-256": 5, "256-512": 6, "512-inf": 7, } area_ranges = [ [0**2, 1e5**2], # all [0**2, 32**2], # small [32**2, 96**2], # medium [96**2, 1e5**2], # large [96**2, 128**2], # 96-128 [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) area_range = area_ranges[areas[area]] gt_overlaps = [] num_pos = 0 for prediction_dict in dataset_predictions: predictions = prediction_dict["proposals"] # sort predictions in descending order # TODO maybe remove this and make it explicit in the documentation inds = predictions.objectness_logits.sort(descending=True)[1] predictions = predictions[inds] ann_ids = coco_api.getAnnIds(imgIds=prediction_dict["image_id"]) anno = coco_api.loadAnns(ann_ids) gt_boxes = [ BoxMode.convert(obj["bbox"], BoxMode.XYWH_ABS, BoxMode.XYXY_ABS) for obj in anno if obj["iscrowd"] == 0 ] gt_boxes = torch.as_tensor(gt_boxes).reshape(-1, 4) # guard against no boxes gt_boxes = Boxes(gt_boxes) gt_areas = torch.as_tensor([obj["area"] for obj in anno if obj["iscrowd"] == 0]) if len(gt_boxes) == 0 or len(predictions) == 0: continue valid_gt_inds = (gt_areas >= area_range[0]) & (gt_areas <= area_range[1]) gt_boxes = gt_boxes[valid_gt_inds] num_pos += len(gt_boxes) if len(gt_boxes) == 0: continue if limit is not None and len(predictions) > limit: predictions = predictions[:limit] overlaps = pairwise_iou(predictions.proposal_boxes, gt_boxes) _gt_overlaps = torch.zeros(len(gt_boxes)) for j in range(min(len(predictions), len(gt_boxes))): # find which proposal box maximally covers each gt box # and get the iou amount of coverage for each gt box max_overlaps, argmax_overlaps = overlaps.max(dim=0) # find which gt box is 'best' covered (i.e. 'best' = most iou) gt_ovr, gt_ind = max_overlaps.max(dim=0) assert gt_ovr >= 0 # find the proposal box that covers the best covered gt box box_ind = argmax_overlaps[gt_ind] # record the iou coverage of this gt box _gt_overlaps[j] = overlaps[box_ind, gt_ind] assert _gt_overlaps[j] == gt_ovr # mark the proposal box and the gt box as used overlaps[box_ind, :] = -1 overlaps[:, gt_ind] = -1 # append recorded iou coverage level gt_overlaps.append(_gt_overlaps) gt_overlaps = ( torch.cat(gt_overlaps, dim=0) if len(gt_overlaps) else torch.zeros(0, dtype=torch.float32) ) gt_overlaps, _ = torch.sort(gt_overlaps) if thresholds is None: step = 0.05 thresholds = torch.arange(0.5, 0.95 + 1e-5, step, dtype=torch.float32) recalls = torch.zeros_like(thresholds) # compute recall for each iou threshold for i, t in enumerate(thresholds): recalls[i] = (gt_overlaps >= t).float().sum() / float(num_pos) # ar = 2 * np.trapz(recalls, thresholds) ar = recalls.mean() return { "ar": ar, "recalls": recalls, "thresholds": thresholds, "gt_overlaps": gt_overlaps, "num_pos": num_pos, } def _evaluate_predictions_on_coco( coco_gt, coco_results, iou_type, kpt_oks_sigmas=None, use_fast_impl=True, img_ids=None, max_dets_per_image=None, ): """ Evaluate the coco results using COCOEval API. """ assert len(coco_results) > 0 if iou_type == "segm": coco_results = copy.deepcopy(coco_results) # When evaluating mask AP, if the results contain bbox, cocoapi will # use the box area as the area of the instance, instead of the mask area. # This leads to a different definition of small/medium/large. # We remove the bbox field to let mask AP use mask area. for c in coco_results: c.pop("bbox", None) coco_dt = coco_gt.loadRes(coco_results) coco_eval = (COCOeval_opt if use_fast_impl else COCOeval)(coco_gt, coco_dt, iou_type) # For COCO, the default max_dets_per_image is [1, 10, 100]. if max_dets_per_image is None: max_dets_per_image = [1, 10, 100] # Default from COCOEval else: assert ( len(max_dets_per_image) >= 3 ), "COCOeval requires maxDets (and max_dets_per_image) to have length at least 3" # In the case that user supplies a custom input for max_dets_per_image, # apply COCOevalMaxDets to evaluate AP with the custom input. if max_dets_per_image[2] != 100: coco_eval = COCOevalMaxDets(coco_gt, coco_dt, iou_type) if iou_type != "keypoints": coco_eval.params.maxDets = max_dets_per_image if img_ids is not None: coco_eval.params.imgIds = img_ids if iou_type == "keypoints": # Use the COCO default keypoint OKS sigmas unless overrides are specified if kpt_oks_sigmas: assert hasattr(coco_eval.params, "kpt_oks_sigmas"), "pycocotools is too old!" coco_eval.params.kpt_oks_sigmas = np.array(kpt_oks_sigmas) # COCOAPI requires every detection and every gt to have keypoints, so # we just take the first entry from both num_keypoints_dt = len(coco_results[0]["keypoints"]) // 3 num_keypoints_gt = len(next(iter(coco_gt.anns.values()))["keypoints"]) // 3 num_keypoints_oks = len(coco_eval.params.kpt_oks_sigmas) assert num_keypoints_oks == num_keypoints_dt == num_keypoints_gt, ( f"[COCOEvaluator] Prediction contain {num_keypoints_dt} keypoints. " f"Ground truth contains {num_keypoints_gt} keypoints. " f"The length of cfg.TEST.KEYPOINT_OKS_SIGMAS is {num_keypoints_oks}. " "They have to agree with each other. For meaning of OKS, please refer to " "http://cocodataset.org/#keypoints-eval." ) coco_eval.evaluate() coco_eval.accumulate() coco_eval.summarize() return coco_eval class COCOevalMaxDets(COCOeval): """ Modified version of COCOeval for evaluating AP with a custom maxDets (by default for COCO, maxDets is 100) """ def summarize(self): """ Compute and display summary metrics for evaluation results given a custom value for max_dets_per_image """ def _summarize(ap=1, iouThr=None, areaRng="all", maxDets=100): p = self.params iStr = " {:<18} {} @[ IoU={:<9} | area={:>6s} | maxDets={:>3d} ] = {:0.3f}" titleStr = "Average Precision" if ap == 1 else "Average Recall" typeStr = "(AP)" if ap == 1 else "(AR)" iouStr = ( "{:0.2f}:{:0.2f}".format(p.iouThrs[0], p.iouThrs[-1]) if iouThr is None else "{:0.2f}".format(iouThr) ) aind = [i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng] mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets] if ap == 1: # dimension of precision: [TxRxKxAxM] s = self.eval["precision"] # IoU if iouThr is not None: t = np.where(iouThr == p.iouThrs)[0] s = s[t] s = s[:, :, :, aind, mind] else: # dimension of recall: [TxKxAxM] s = self.eval["recall"] if iouThr is not None: t = np.where(iouThr == p.iouThrs)[0] s = s[t] s = s[:, :, aind, mind] if len(s[s > -1]) == 0: mean_s = -1 else: mean_s = np.mean(s[s > -1]) print(iStr.format(titleStr, typeStr, iouStr, areaRng, maxDets, mean_s)) return mean_s def _summarizeDets(): stats = np.zeros((12,)) # Evaluate AP using the custom limit on maximum detections per image stats[0] = _summarize(1, maxDets=self.params.maxDets[2]) stats[1] = _summarize(1, iouThr=0.5, maxDets=self.params.maxDets[2]) stats[2] = _summarize(1, iouThr=0.75, maxDets=self.params.maxDets[2]) stats[3] = _summarize(1, areaRng="small", maxDets=self.params.maxDets[2]) stats[4] = _summarize(1, areaRng="medium", maxDets=self.params.maxDets[2]) stats[5] = _summarize(1, areaRng="large", maxDets=self.params.maxDets[2]) stats[6] = _summarize(0, maxDets=self.params.maxDets[0]) stats[7] = _summarize(0, maxDets=self.params.maxDets[1]) stats[8] = _summarize(0, maxDets=self.params.maxDets[2]) stats[9] = _summarize(0, areaRng="small", maxDets=self.params.maxDets[2]) stats[10] = _summarize(0, areaRng="medium", maxDets=self.params.maxDets[2]) stats[11] = _summarize(0, areaRng="large", maxDets=self.params.maxDets[2]) return stats def _summarizeKps(): stats = np.zeros((10,)) stats[0] = _summarize(1, maxDets=20) stats[1] = _summarize(1, maxDets=20, iouThr=0.5) stats[2] = _summarize(1, maxDets=20, iouThr=0.75) stats[3] = _summarize(1, maxDets=20, areaRng="medium") stats[4] = _summarize(1, maxDets=20, areaRng="large") stats[5] = _summarize(0, maxDets=20) stats[6] = _summarize(0, maxDets=20, iouThr=0.5) stats[7] = _summarize(0, maxDets=20, iouThr=0.75) stats[8] = _summarize(0, maxDets=20, areaRng="medium") stats[9] = _summarize(0, maxDets=20, areaRng="large") return stats if not self.eval: raise Exception("Please run accumulate() first") iouType = self.params.iouType if iouType == "segm" or iouType == "bbox": summarize = _summarizeDets elif iouType == "keypoints": summarize = _summarizeKps self.stats = summarize() def __str__(self): self.summarize() ================================================ FILE: detectron2/detectron2/evaluation/evaluator.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import datetime import logging import time from collections import OrderedDict, abc from contextlib import ExitStack, contextmanager from typing import List, Union import torch from torch import nn from detectron2.utils.comm import get_world_size, is_main_process from detectron2.utils.logger import log_every_n_seconds class DatasetEvaluator: """ Base class for a dataset evaluator. The function :func:`inference_on_dataset` runs the model over all samples in the dataset, and have a DatasetEvaluator to process the inputs/outputs. This class will accumulate information of the inputs/outputs (by :meth:`process`), and produce evaluation results in the end (by :meth:`evaluate`). """ def reset(self): """ Preparation for a new round of evaluation. Should be called before starting a round of evaluation. """ pass def process(self, inputs, outputs): """ Process the pair of inputs and outputs. If they contain batches, the pairs can be consumed one-by-one using `zip`: .. code-block:: python for input_, output in zip(inputs, outputs): # do evaluation on single input/output pair ... Args: inputs (list): the inputs that's used to call the model. outputs (list): the return value of `model(inputs)` """ pass def evaluate(self): """ Evaluate/summarize the performance, after processing all input/output pairs. Returns: dict: A new evaluator class can return a dict of arbitrary format as long as the user can process the results. In our train_net.py, we expect the following format: * key: the name of the task (e.g., bbox) * value: a dict of {metric name: score}, e.g.: {"AP50": 80} """ pass class DatasetEvaluators(DatasetEvaluator): """ Wrapper class to combine multiple :class:`DatasetEvaluator` instances. This class dispatches every evaluation call to all of its :class:`DatasetEvaluator`. """ def __init__(self, evaluators): """ Args: evaluators (list): the evaluators to combine. """ super().__init__() self._evaluators = evaluators def reset(self): for evaluator in self._evaluators: evaluator.reset() def process(self, inputs, outputs): for evaluator in self._evaluators: evaluator.process(inputs, outputs) def evaluate(self): results = OrderedDict() for evaluator in self._evaluators: result = evaluator.evaluate() if is_main_process() and result is not None: for k, v in result.items(): assert ( k not in results ), "Different evaluators produce results with the same key {}".format(k) results[k] = v return results def inference_on_dataset( model, data_loader, evaluator: Union[DatasetEvaluator, List[DatasetEvaluator], None] ): """ Run model on the data_loader and evaluate the metrics with evaluator. Also benchmark the inference speed of `model.__call__` accurately. The model will be used in eval mode. Args: model (callable): a callable which takes an object from `data_loader` and returns some outputs. If it's an nn.Module, it will be temporarily set to `eval` mode. If you wish to evaluate a model in `training` mode instead, you can wrap the given model and override its behavior of `.eval()` and `.train()`. data_loader: an iterable object with a length. The elements it generates will be the inputs to the model. evaluator: the evaluator(s) to run. Use `None` if you only want to benchmark, but don't want to do any evaluation. Returns: The return value of `evaluator.evaluate()` """ num_devices = get_world_size() logger = logging.getLogger(__name__) logger.info("Start inference on {} batches".format(len(data_loader))) total = len(data_loader) # inference data loader must have a fixed length if evaluator is None: # create a no-op evaluator evaluator = DatasetEvaluators([]) if isinstance(evaluator, abc.MutableSequence): evaluator = DatasetEvaluators(evaluator) evaluator.reset() num_warmup = min(5, total - 1) start_time = time.perf_counter() total_data_time = 0 total_compute_time = 0 total_eval_time = 0 with ExitStack() as stack: if isinstance(model, nn.Module): stack.enter_context(inference_context(model)) stack.enter_context(torch.no_grad()) start_data_time = time.perf_counter() for idx, inputs in enumerate(data_loader): total_data_time += time.perf_counter() - start_data_time if idx == num_warmup: start_time = time.perf_counter() total_data_time = 0 total_compute_time = 0 total_eval_time = 0 start_compute_time = time.perf_counter() outputs = model(inputs) if torch.cuda.is_available(): torch.cuda.synchronize() total_compute_time += time.perf_counter() - start_compute_time start_eval_time = time.perf_counter() evaluator.process(inputs, outputs) total_eval_time += time.perf_counter() - start_eval_time iters_after_start = idx + 1 - num_warmup * int(idx >= num_warmup) data_seconds_per_iter = total_data_time / iters_after_start compute_seconds_per_iter = total_compute_time / iters_after_start eval_seconds_per_iter = total_eval_time / iters_after_start total_seconds_per_iter = (time.perf_counter() - start_time) / iters_after_start if idx >= num_warmup * 2 or compute_seconds_per_iter > 5: eta = datetime.timedelta(seconds=int(total_seconds_per_iter * (total - idx - 1))) log_every_n_seconds( logging.INFO, ( f"Inference done {idx + 1}/{total}. " f"Dataloading: {data_seconds_per_iter:.4f} s/iter. " f"Inference: {compute_seconds_per_iter:.4f} s/iter. " f"Eval: {eval_seconds_per_iter:.4f} s/iter. " f"Total: {total_seconds_per_iter:.4f} s/iter. " f"ETA={eta}" ), n=5, ) start_data_time = time.perf_counter() # Measure the time only for this worker (before the synchronization barrier) total_time = time.perf_counter() - start_time total_time_str = str(datetime.timedelta(seconds=total_time)) # NOTE this format is parsed by grep logger.info( "Total inference time: {} ({:.6f} s / iter per device, on {} devices)".format( total_time_str, total_time / (total - num_warmup), num_devices ) ) total_compute_time_str = str(datetime.timedelta(seconds=int(total_compute_time))) logger.info( "Total inference pure compute time: {} ({:.6f} s / iter per device, on {} devices)".format( total_compute_time_str, total_compute_time / (total - num_warmup), num_devices ) ) results = evaluator.evaluate() # An evaluator may return None when not in main process. # Replace it by an empty dict instead to make it easier for downstream code to handle if results is None: results = {} return results @contextmanager def inference_context(model): """ A context where the model is temporarily changed to eval mode, and restored to previous mode afterwards. Args: model: a torch Module """ training_mode = model.training model.eval() yield model.train(training_mode) ================================================ FILE: detectron2/detectron2/evaluation/fast_eval_api.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import logging import numpy as np import time from pycocotools.cocoeval import COCOeval from detectron2 import _C logger = logging.getLogger(__name__) class COCOeval_opt(COCOeval): """ This is a slightly modified version of the original COCO API, where the functions evaluateImg() and accumulate() are implemented in C++ to speedup evaluation """ def evaluate(self): """ Run per image evaluation on given images and store results in self.evalImgs_cpp, a datastructure that isn't readable from Python but is used by a c++ implementation of accumulate(). Unlike the original COCO PythonAPI, we don't populate the datastructure self.evalImgs because this datastructure is a computational bottleneck. :return: None """ tic = time.time() p = self.params # add backward compatibility if useSegm is specified in params if p.useSegm is not None: p.iouType = "segm" if p.useSegm == 1 else "bbox" logger.info("Evaluate annotation type *{}*".format(p.iouType)) p.imgIds = list(np.unique(p.imgIds)) if p.useCats: p.catIds = list(np.unique(p.catIds)) p.maxDets = sorted(p.maxDets) self.params = p self._prepare() # bottleneck # loop through images, area range, max detection number catIds = p.catIds if p.useCats else [-1] if p.iouType == "segm" or p.iouType == "bbox": computeIoU = self.computeIoU elif p.iouType == "keypoints": computeIoU = self.computeOks self.ious = { (imgId, catId): computeIoU(imgId, catId) for imgId in p.imgIds for catId in catIds } # bottleneck maxDet = p.maxDets[-1] # <<<< Beginning of code differences with original COCO API def convert_instances_to_cpp(instances, is_det=False): # Convert annotations for a list of instances in an image to a format that's fast # to access in C++ instances_cpp = [] for instance in instances: instance_cpp = _C.InstanceAnnotation( int(instance["id"]), instance["score"] if is_det else instance.get("score", 0.0), instance["area"], bool(instance.get("iscrowd", 0)), bool(instance.get("ignore", 0)), ) instances_cpp.append(instance_cpp) return instances_cpp # Convert GT annotations, detections, and IOUs to a format that's fast to access in C++ ground_truth_instances = [ [convert_instances_to_cpp(self._gts[imgId, catId]) for catId in p.catIds] for imgId in p.imgIds ] detected_instances = [ [convert_instances_to_cpp(self._dts[imgId, catId], is_det=True) for catId in p.catIds] for imgId in p.imgIds ] ious = [[self.ious[imgId, catId] for catId in catIds] for imgId in p.imgIds] if not p.useCats: # For each image, flatten per-category lists into a single list ground_truth_instances = [[[o for c in i for o in c]] for i in ground_truth_instances] detected_instances = [[[o for c in i for o in c]] for i in detected_instances] # Call C++ implementation of self.evaluateImgs() self._evalImgs_cpp = _C.COCOevalEvaluateImages( p.areaRng, maxDet, p.iouThrs, ious, ground_truth_instances, detected_instances ) self._evalImgs = None self._paramsEval = copy.deepcopy(self.params) toc = time.time() logger.info("COCOeval_opt.evaluate() finished in {:0.2f} seconds.".format(toc - tic)) # >>>> End of code differences with original COCO API def accumulate(self): """ Accumulate per image evaluation results and store the result in self.eval. Does not support changing parameter settings from those used by self.evaluate() """ logger.info("Accumulating evaluation results...") tic = time.time() assert hasattr( self, "_evalImgs_cpp" ), "evaluate() must be called before accmulate() is called." self.eval = _C.COCOevalAccumulate(self._paramsEval, self._evalImgs_cpp) # recall is num_iou_thresholds X num_categories X num_area_ranges X num_max_detections self.eval["recall"] = np.array(self.eval["recall"]).reshape( self.eval["counts"][:1] + self.eval["counts"][2:] ) # precision and scores are num_iou_thresholds X num_recall_thresholds X num_categories X # num_area_ranges X num_max_detections self.eval["precision"] = np.array(self.eval["precision"]).reshape(self.eval["counts"]) self.eval["scores"] = np.array(self.eval["scores"]).reshape(self.eval["counts"]) toc = time.time() logger.info("COCOeval_opt.accumulate() finished in {:0.2f} seconds.".format(toc - tic)) ================================================ FILE: detectron2/detectron2/evaluation/lvis_evaluation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import itertools import json import logging import os import pickle from collections import OrderedDict import torch import detectron2.utils.comm as comm from detectron2.config import CfgNode from detectron2.data import MetadataCatalog from detectron2.structures import Boxes, BoxMode, pairwise_iou from detectron2.utils.file_io import PathManager from detectron2.utils.logger import create_small_table from .coco_evaluation import instances_to_coco_json from .evaluator import DatasetEvaluator class LVISEvaluator(DatasetEvaluator): """ Evaluate object proposal and instance detection/segmentation outputs using LVIS's metrics and evaluation API. """ def __init__( self, dataset_name, tasks=None, distributed=True, output_dir=None, *, max_dets_per_image=None, ): """ Args: dataset_name (str): name of the dataset to be evaluated. It must have the following corresponding metadata: "json_file": the path to the LVIS format annotation tasks (tuple[str]): tasks that can be evaluated under the given configuration. A task is one of "bbox", "segm". By default, will infer this automatically from predictions. distributed (True): if True, will collect results from all ranks for evaluation. Otherwise, will evaluate the results in the current process. output_dir (str): optional, an output directory to dump results. max_dets_per_image (None or int): limit on maximum detections per image in evaluating AP This limit, by default of the LVIS dataset, is 300. """ from lvis import LVIS self._logger = logging.getLogger(__name__) if tasks is not None and isinstance(tasks, CfgNode): self._logger.warn( "COCO Evaluator instantiated using config, this is deprecated behavior." " Please pass in explicit arguments instead." ) self._tasks = None # Infering it from predictions should be better else: self._tasks = tasks self._distributed = distributed self._output_dir = output_dir self._max_dets_per_image = max_dets_per_image self._cpu_device = torch.device("cpu") self._metadata = MetadataCatalog.get(dataset_name) json_file = PathManager.get_local_path(self._metadata.json_file) self._lvis_api = LVIS(json_file) # Test set json files do not contain annotations (evaluation must be # performed using the LVIS evaluation server). self._do_evaluation = len(self._lvis_api.get_ann_ids()) > 0 def reset(self): self._predictions = [] def process(self, inputs, outputs): """ Args: inputs: the inputs to a LVIS model (e.g., GeneralizedRCNN). It is a list of dict. Each dict corresponds to an image and contains keys like "height", "width", "file_name", "image_id". outputs: the outputs of a LVIS model. It is a list of dicts with key "instances" that contains :class:`Instances`. """ for input, output in zip(inputs, outputs): prediction = {"image_id": input["image_id"]} if "instances" in output: instances = output["instances"].to(self._cpu_device) prediction["instances"] = instances_to_coco_json(instances, input["image_id"]) if "proposals" in output: prediction["proposals"] = output["proposals"].to(self._cpu_device) self._predictions.append(prediction) def evaluate(self): if self._distributed: comm.synchronize() predictions = comm.gather(self._predictions, dst=0) predictions = list(itertools.chain(*predictions)) if not comm.is_main_process(): return else: predictions = self._predictions if len(predictions) == 0: self._logger.warning("[LVISEvaluator] Did not receive valid predictions.") return {} if self._output_dir: PathManager.mkdirs(self._output_dir) file_path = os.path.join(self._output_dir, "instances_predictions.pth") with PathManager.open(file_path, "wb") as f: torch.save(predictions, f) self._results = OrderedDict() if "proposals" in predictions[0]: self._eval_box_proposals(predictions) if "instances" in predictions[0]: self._eval_predictions(predictions) # Copy so the caller can do whatever with results return copy.deepcopy(self._results) def _tasks_from_predictions(self, predictions): for pred in predictions: if "segmentation" in pred: return ("bbox", "segm") return ("bbox",) def _eval_predictions(self, predictions): """ Evaluate predictions. Fill self._results with the metrics of the tasks. Args: predictions (list[dict]): list of outputs from the model """ self._logger.info("Preparing results in the LVIS format ...") lvis_results = list(itertools.chain(*[x["instances"] for x in predictions])) tasks = self._tasks or self._tasks_from_predictions(lvis_results) # LVIS evaluator can be used to evaluate results for COCO dataset categories. # In this case `_metadata` variable will have a field with COCO-specific category mapping. if hasattr(self._metadata, "thing_dataset_id_to_contiguous_id"): reverse_id_mapping = { v: k for k, v in self._metadata.thing_dataset_id_to_contiguous_id.items() } for result in lvis_results: result["category_id"] = reverse_id_mapping[result["category_id"]] else: # unmap the category ids for LVIS (from 0-indexed to 1-indexed) for result in lvis_results: result["category_id"] += 1 if self._output_dir: file_path = os.path.join(self._output_dir, "lvis_instances_results.json") self._logger.info("Saving results to {}".format(file_path)) with PathManager.open(file_path, "w") as f: f.write(json.dumps(lvis_results)) f.flush() if not self._do_evaluation: self._logger.info("Annotations are not available for evaluation.") return self._logger.info("Evaluating predictions ...") for task in sorted(tasks): res = _evaluate_predictions_on_lvis( self._lvis_api, lvis_results, task, max_dets_per_image=self._max_dets_per_image, class_names=self._metadata.get("thing_classes"), ) self._results[task] = res def _eval_box_proposals(self, predictions): """ Evaluate the box proposals in predictions. Fill self._results with the metrics for "box_proposals" task. """ if self._output_dir: # Saving generated box proposals to file. # Predicted box_proposals are in XYXY_ABS mode. bbox_mode = BoxMode.XYXY_ABS.value ids, boxes, objectness_logits = [], [], [] for prediction in predictions: ids.append(prediction["image_id"]) boxes.append(prediction["proposals"].proposal_boxes.tensor.numpy()) objectness_logits.append(prediction["proposals"].objectness_logits.numpy()) proposal_data = { "boxes": boxes, "objectness_logits": objectness_logits, "ids": ids, "bbox_mode": bbox_mode, } with PathManager.open(os.path.join(self._output_dir, "box_proposals.pkl"), "wb") as f: pickle.dump(proposal_data, f) if not self._do_evaluation: self._logger.info("Annotations are not available for evaluation.") return self._logger.info("Evaluating bbox proposals ...") res = {} areas = {"all": "", "small": "s", "medium": "m", "large": "l"} for limit in [100, 1000]: for area, suffix in areas.items(): stats = _evaluate_box_proposals(predictions, self._lvis_api, area=area, limit=limit) key = "AR{}@{:d}".format(suffix, limit) res[key] = float(stats["ar"].item() * 100) self._logger.info("Proposal metrics: \n" + create_small_table(res)) self._results["box_proposals"] = res # inspired from Detectron: # https://github.com/facebookresearch/Detectron/blob/a6a835f5b8208c45d0dce217ce9bbda915f44df7/detectron/datasets/json_dataset_evaluator.py#L255 # noqa def _evaluate_box_proposals(dataset_predictions, lvis_api, thresholds=None, area="all", limit=None): """ Evaluate detection proposal recall metrics. This function is a much faster alternative to the official LVIS API recall evaluation code. However, it produces slightly different results. """ # Record max overlap value for each gt box # Return vector of overlap values areas = { "all": 0, "small": 1, "medium": 2, "large": 3, "96-128": 4, "128-256": 5, "256-512": 6, "512-inf": 7, } area_ranges = [ [0**2, 1e5**2], # all [0**2, 32**2], # small [32**2, 96**2], # medium [96**2, 1e5**2], # large [96**2, 128**2], # 96-128 [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) area_range = area_ranges[areas[area]] gt_overlaps = [] num_pos = 0 for prediction_dict in dataset_predictions: predictions = prediction_dict["proposals"] # sort predictions in descending order # TODO maybe remove this and make it explicit in the documentation inds = predictions.objectness_logits.sort(descending=True)[1] predictions = predictions[inds] ann_ids = lvis_api.get_ann_ids(img_ids=[prediction_dict["image_id"]]) anno = lvis_api.load_anns(ann_ids) gt_boxes = [ BoxMode.convert(obj["bbox"], BoxMode.XYWH_ABS, BoxMode.XYXY_ABS) for obj in anno ] gt_boxes = torch.as_tensor(gt_boxes).reshape(-1, 4) # guard against no boxes gt_boxes = Boxes(gt_boxes) gt_areas = torch.as_tensor([obj["area"] for obj in anno]) if len(gt_boxes) == 0 or len(predictions) == 0: continue valid_gt_inds = (gt_areas >= area_range[0]) & (gt_areas <= area_range[1]) gt_boxes = gt_boxes[valid_gt_inds] num_pos += len(gt_boxes) if len(gt_boxes) == 0: continue if limit is not None and len(predictions) > limit: predictions = predictions[:limit] overlaps = pairwise_iou(predictions.proposal_boxes, gt_boxes) _gt_overlaps = torch.zeros(len(gt_boxes)) for j in range(min(len(predictions), len(gt_boxes))): # find which proposal box maximally covers each gt box # and get the iou amount of coverage for each gt box max_overlaps, argmax_overlaps = overlaps.max(dim=0) # find which gt box is 'best' covered (i.e. 'best' = most iou) gt_ovr, gt_ind = max_overlaps.max(dim=0) assert gt_ovr >= 0 # find the proposal box that covers the best covered gt box box_ind = argmax_overlaps[gt_ind] # record the iou coverage of this gt box _gt_overlaps[j] = overlaps[box_ind, gt_ind] assert _gt_overlaps[j] == gt_ovr # mark the proposal box and the gt box as used overlaps[box_ind, :] = -1 overlaps[:, gt_ind] = -1 # append recorded iou coverage level gt_overlaps.append(_gt_overlaps) gt_overlaps = ( torch.cat(gt_overlaps, dim=0) if len(gt_overlaps) else torch.zeros(0, dtype=torch.float32) ) gt_overlaps, _ = torch.sort(gt_overlaps) if thresholds is None: step = 0.05 thresholds = torch.arange(0.5, 0.95 + 1e-5, step, dtype=torch.float32) recalls = torch.zeros_like(thresholds) # compute recall for each iou threshold for i, t in enumerate(thresholds): recalls[i] = (gt_overlaps >= t).float().sum() / float(num_pos) # ar = 2 * np.trapz(recalls, thresholds) ar = recalls.mean() return { "ar": ar, "recalls": recalls, "thresholds": thresholds, "gt_overlaps": gt_overlaps, "num_pos": num_pos, } def _evaluate_predictions_on_lvis( lvis_gt, lvis_results, iou_type, max_dets_per_image=None, class_names=None ): """ Args: iou_type (str): max_dets_per_image (None or int): limit on maximum detections per image in evaluating AP This limit, by default of the LVIS dataset, is 300. class_names (None or list[str]): if provided, will use it to predict per-category AP. Returns: a dict of {metric name: score} """ metrics = { "bbox": ["AP", "AP50", "AP75", "APs", "APm", "APl", "APr", "APc", "APf"], "segm": ["AP", "AP50", "AP75", "APs", "APm", "APl", "APr", "APc", "APf"], }[iou_type] logger = logging.getLogger(__name__) if len(lvis_results) == 0: # TODO: check if needed logger.warn("No predictions from the model!") return {metric: float("nan") for metric in metrics} if iou_type == "segm": lvis_results = copy.deepcopy(lvis_results) # When evaluating mask AP, if the results contain bbox, LVIS API will # use the box area as the area of the instance, instead of the mask area. # This leads to a different definition of small/medium/large. # We remove the bbox field to let mask AP use mask area. for c in lvis_results: c.pop("bbox", None) if max_dets_per_image is None: max_dets_per_image = 300 # Default for LVIS dataset from lvis import LVISEval, LVISResults logger.info(f"Evaluating with max detections per image = {max_dets_per_image}") lvis_results = LVISResults(lvis_gt, lvis_results, max_dets=max_dets_per_image) lvis_eval = LVISEval(lvis_gt, lvis_results, iou_type) lvis_eval.run() lvis_eval.print_results() # Pull the standard metrics from the LVIS results results = lvis_eval.get_results() results = {metric: float(results[metric] * 100) for metric in metrics} logger.info("Evaluation results for {}: \n".format(iou_type) + create_small_table(results)) return results ================================================ FILE: detectron2/detectron2/evaluation/panoptic_evaluation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import contextlib import io import itertools import json import logging import numpy as np import os import tempfile from collections import OrderedDict from typing import Optional from PIL import Image from tabulate import tabulate from detectron2.data import MetadataCatalog from detectron2.utils import comm from detectron2.utils.file_io import PathManager from .evaluator import DatasetEvaluator logger = logging.getLogger(__name__) class COCOPanopticEvaluator(DatasetEvaluator): """ Evaluate Panoptic Quality metrics on COCO using PanopticAPI. It saves panoptic segmentation prediction in `output_dir` It contains a synchronize call and has to be called from all workers. """ def __init__(self, dataset_name: str, output_dir: Optional[str] = None): """ Args: dataset_name: name of the dataset output_dir: output directory to save results for evaluation. """ self._metadata = MetadataCatalog.get(dataset_name) self._thing_contiguous_id_to_dataset_id = { v: k for k, v in self._metadata.thing_dataset_id_to_contiguous_id.items() } self._stuff_contiguous_id_to_dataset_id = { v: k for k, v in self._metadata.stuff_dataset_id_to_contiguous_id.items() } self._output_dir = output_dir if self._output_dir is not None: PathManager.mkdirs(self._output_dir) def reset(self): self._predictions = [] def _convert_category_id(self, segment_info): isthing = segment_info.pop("isthing", None) if isthing is None: # the model produces panoptic category id directly. No more conversion needed return segment_info if isthing is True: segment_info["category_id"] = self._thing_contiguous_id_to_dataset_id[ segment_info["category_id"] ] else: segment_info["category_id"] = self._stuff_contiguous_id_to_dataset_id[ segment_info["category_id"] ] return segment_info def process(self, inputs, outputs): from panopticapi.utils import id2rgb for input, output in zip(inputs, outputs): panoptic_img, segments_info = output["panoptic_seg"] panoptic_img = panoptic_img.cpu().numpy() if segments_info is None: # If "segments_info" is None, we assume "panoptic_img" is a # H*W int32 image storing the panoptic_id in the format of # category_id * label_divisor + instance_id. We reserve -1 for # VOID label, and add 1 to panoptic_img since the official # evaluation script uses 0 for VOID label. label_divisor = self._metadata.label_divisor segments_info = [] for panoptic_label in np.unique(panoptic_img): if panoptic_label == -1: # VOID region. continue pred_class = panoptic_label // label_divisor isthing = ( pred_class in self._metadata.thing_dataset_id_to_contiguous_id.values() ) segments_info.append( { "id": int(panoptic_label) + 1, "category_id": int(pred_class), "isthing": bool(isthing), } ) # Official evaluation script uses 0 for VOID label. panoptic_img += 1 file_name = os.path.basename(input["file_name"]) file_name_png = os.path.splitext(file_name)[0] + ".png" with io.BytesIO() as out: Image.fromarray(id2rgb(panoptic_img)).save(out, format="PNG") segments_info = [self._convert_category_id(x) for x in segments_info] self._predictions.append( { "image_id": input["image_id"], "file_name": file_name_png, "png_string": out.getvalue(), "segments_info": segments_info, } ) def evaluate(self): comm.synchronize() self._predictions = comm.gather(self._predictions) self._predictions = list(itertools.chain(*self._predictions)) if not comm.is_main_process(): return # PanopticApi requires local files gt_json = PathManager.get_local_path(self._metadata.panoptic_json) gt_folder = PathManager.get_local_path(self._metadata.panoptic_root) with tempfile.TemporaryDirectory(prefix="panoptic_eval") as pred_dir: logger.info("Writing all panoptic predictions to {} ...".format(pred_dir)) for p in self._predictions: with open(os.path.join(pred_dir, p["file_name"]), "wb") as f: f.write(p.pop("png_string")) with open(gt_json, "r") as f: json_data = json.load(f) json_data["annotations"] = self._predictions output_dir = self._output_dir or pred_dir predictions_json = os.path.join(output_dir, "predictions.json") with PathManager.open(predictions_json, "w") as f: f.write(json.dumps(json_data)) from panopticapi.evaluation import pq_compute with contextlib.redirect_stdout(io.StringIO()): pq_res = pq_compute( gt_json, PathManager.get_local_path(predictions_json), gt_folder=gt_folder, pred_folder=pred_dir, ) res = {} res["PQ"] = 100 * pq_res["All"]["pq"] res["SQ"] = 100 * pq_res["All"]["sq"] res["RQ"] = 100 * pq_res["All"]["rq"] res["PQ_th"] = 100 * pq_res["Things"]["pq"] res["SQ_th"] = 100 * pq_res["Things"]["sq"] res["RQ_th"] = 100 * pq_res["Things"]["rq"] res["PQ_st"] = 100 * pq_res["Stuff"]["pq"] res["SQ_st"] = 100 * pq_res["Stuff"]["sq"] res["RQ_st"] = 100 * pq_res["Stuff"]["rq"] results = OrderedDict({"panoptic_seg": res}) _print_panoptic_results(pq_res) return results def _print_panoptic_results(pq_res): headers = ["", "PQ", "SQ", "RQ", "#categories"] data = [] for name in ["All", "Things", "Stuff"]: row = [name] + [pq_res[name][k] * 100 for k in ["pq", "sq", "rq"]] + [pq_res[name]["n"]] data.append(row) table = tabulate( data, headers=headers, tablefmt="pipe", floatfmt=".3f", stralign="center", numalign="center" ) logger.info("Panoptic Evaluation Results:\n" + table) if __name__ == "__main__": from detectron2.utils.logger import setup_logger logger = setup_logger() import argparse parser = argparse.ArgumentParser() parser.add_argument("--gt-json") parser.add_argument("--gt-dir") parser.add_argument("--pred-json") parser.add_argument("--pred-dir") args = parser.parse_args() from panopticapi.evaluation import pq_compute with contextlib.redirect_stdout(io.StringIO()): pq_res = pq_compute( args.gt_json, args.pred_json, gt_folder=args.gt_dir, pred_folder=args.pred_dir ) _print_panoptic_results(pq_res) ================================================ FILE: detectron2/detectron2/evaluation/pascal_voc_evaluation.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np import os import tempfile import xml.etree.ElementTree as ET from collections import OrderedDict, defaultdict from functools import lru_cache import torch from detectron2.data import MetadataCatalog from detectron2.utils import comm from detectron2.utils.file_io import PathManager from .evaluator import DatasetEvaluator class PascalVOCDetectionEvaluator(DatasetEvaluator): """ Evaluate Pascal VOC style AP for Pascal VOC dataset. It contains a synchronization, therefore has to be called from all ranks. Note that the concept of AP can be implemented in different ways and may not produce identical results. This class mimics the implementation of the official Pascal VOC Matlab API, and should produce similar but not identical results to the official API. """ def __init__(self, dataset_name): """ Args: dataset_name (str): name of the dataset, e.g., "voc_2007_test" """ self._dataset_name = dataset_name meta = MetadataCatalog.get(dataset_name) # Too many tiny files, download all to local for speed. annotation_dir_local = PathManager.get_local_path( os.path.join(meta.dirname, "Annotations/") ) self._anno_file_template = os.path.join(annotation_dir_local, "{}.xml") self._image_set_path = os.path.join(meta.dirname, "ImageSets", "Main", meta.split + ".txt") self._class_names = meta.thing_classes assert meta.year in [2007, 2012], meta.year self._is_2007 = meta.year == 2007 self._cpu_device = torch.device("cpu") self._logger = logging.getLogger(__name__) def reset(self): self._predictions = defaultdict(list) # class name -> list of prediction strings def process(self, inputs, outputs): for input, output in zip(inputs, outputs): image_id = input["image_id"] instances = output["instances"].to(self._cpu_device) boxes = instances.pred_boxes.tensor.numpy() scores = instances.scores.tolist() classes = instances.pred_classes.tolist() for box, score, cls in zip(boxes, scores, classes): xmin, ymin, xmax, ymax = box # The inverse of data loading logic in `datasets/pascal_voc.py` xmin += 1 ymin += 1 self._predictions[cls].append( f"{image_id} {score:.3f} {xmin:.1f} {ymin:.1f} {xmax:.1f} {ymax:.1f}" ) def evaluate(self): """ Returns: dict: has a key "segm", whose value is a dict of "AP", "AP50", and "AP75". """ all_predictions = comm.gather(self._predictions, dst=0) if not comm.is_main_process(): return predictions = defaultdict(list) for predictions_per_rank in all_predictions: for clsid, lines in predictions_per_rank.items(): predictions[clsid].extend(lines) del all_predictions self._logger.info( "Evaluating {} using {} metric. " "Note that results do not use the official Matlab API.".format( self._dataset_name, 2007 if self._is_2007 else 2012 ) ) with tempfile.TemporaryDirectory(prefix="pascal_voc_eval_") as dirname: res_file_template = os.path.join(dirname, "{}.txt") aps = defaultdict(list) # iou -> ap per class for cls_id, cls_name in enumerate(self._class_names): lines = predictions.get(cls_id, [""]) with open(res_file_template.format(cls_name), "w") as f: f.write("\n".join(lines)) for thresh in range(50, 100, 5): rec, prec, ap = voc_eval( res_file_template, self._anno_file_template, self._image_set_path, cls_name, ovthresh=thresh / 100.0, use_07_metric=self._is_2007, ) aps[thresh].append(ap * 100) ret = OrderedDict() mAP = {iou: np.mean(x) for iou, x in aps.items()} ret["bbox"] = {"AP": np.mean(list(mAP.values())), "AP50": mAP[50], "AP75": mAP[75]} return ret ############################################################################## # # Below code is modified from # https://github.com/rbgirshick/py-faster-rcnn/blob/master/lib/datasets/voc_eval.py # -------------------------------------------------------- # Fast/er R-CNN # Licensed under The MIT License [see LICENSE for details] # Written by Bharath Hariharan # -------------------------------------------------------- """Python implementation of the PASCAL VOC devkit's AP evaluation code.""" @lru_cache(maxsize=None) def parse_rec(filename): """Parse a PASCAL VOC xml file.""" with PathManager.open(filename) as f: tree = ET.parse(f) objects = [] for obj in tree.findall("object"): obj_struct = {} 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) bbox = obj.find("bndbox") obj_struct["bbox"] = [ int(bbox.find("xmin").text), int(bbox.find("ymin").text), int(bbox.find("xmax").text), int(bbox.find("ymax").text), ] objects.append(obj_struct) return objects def voc_ap(rec, prec, use_07_metric=False): """Compute VOC AP given precision and recall. If use_07_metric is true, uses the VOC 07 11-point method (default:False). """ if use_07_metric: # 11 point metric ap = 0.0 for t in np.arange(0.0, 1.1, 0.1): if np.sum(rec >= t) == 0: p = 0 else: p = np.max(prec[rec >= t]) ap = ap + p / 11.0 else: # correct AP calculation # first append sentinel values at the end mrec = np.concatenate(([0.0], rec, [1.0])) mpre = np.concatenate(([0.0], prec, [0.0])) # compute the precision envelope for i in range(mpre.size - 1, 0, -1): mpre[i - 1] = np.maximum(mpre[i - 1], mpre[i]) # to calculate area under PR curve, look for points # where X axis (recall) changes value i = np.where(mrec[1:] != mrec[:-1])[0] # and sum (\Delta recall) * prec ap = np.sum((mrec[i + 1] - mrec[i]) * mpre[i + 1]) return ap def voc_eval(detpath, annopath, imagesetfile, classname, ovthresh=0.5, use_07_metric=False): """rec, prec, ap = voc_eval(detpath, annopath, imagesetfile, classname, [ovthresh], [use_07_metric]) Top level function that does the PASCAL VOC evaluation. detpath: Path to detections detpath.format(classname) should produce the detection results file. annopath: Path to annotations annopath.format(imagename) should be the xml annotations file. imagesetfile: Text file containing the list of images, one image per line. classname: Category name (duh) [ovthresh]: Overlap threshold (default = 0.5) [use_07_metric]: Whether to use VOC07's 11 point AP computation (default False) """ # assumes detections are in detpath.format(classname) # assumes annotations are in annopath.format(imagename) # assumes imagesetfile is a text file with each line an image name # first load gt # read list of images with PathManager.open(imagesetfile, "r") as f: lines = f.readlines() imagenames = [x.strip() for x in lines] # load annots recs = {} for imagename in imagenames: recs[imagename] = parse_rec(annopath.format(imagename)) # extract gt objects for this class class_recs = {} npos = 0 for imagename in imagenames: R = [obj for obj in recs[imagename] if obj["name"] == classname] bbox = np.array([x["bbox"] for x in R]) difficult = np.array([x["difficult"] for x in R]).astype(np.bool) # difficult = np.array([False for x in R]).astype(np.bool) # treat all "difficult" as GT det = [False] * len(R) npos = npos + sum(~difficult) class_recs[imagename] = {"bbox": bbox, "difficult": difficult, "det": det} # read dets detfile = detpath.format(classname) with open(detfile, "r") as f: lines = f.readlines() splitlines = [x.strip().split(" ") for x in lines] image_ids = [x[0] for x in splitlines] confidence = np.array([float(x[1]) for x in splitlines]) BB = np.array([[float(z) for z in x[2:]] for x in splitlines]).reshape(-1, 4) # sort by confidence sorted_ind = np.argsort(-confidence) BB = BB[sorted_ind, :] image_ids = [image_ids[x] for x in sorted_ind] # go down dets and mark TPs and FPs nd = len(image_ids) tp = np.zeros(nd) fp = np.zeros(nd) for d in range(nd): R = class_recs[image_ids[d]] bb = BB[d, :].astype(float) ovmax = -np.inf BBGT = R["bbox"].astype(float) if BBGT.size > 0: # compute overlaps # intersection ixmin = np.maximum(BBGT[:, 0], bb[0]) iymin = np.maximum(BBGT[:, 1], bb[1]) ixmax = np.minimum(BBGT[:, 2], bb[2]) iymax = np.minimum(BBGT[:, 3], bb[3]) iw = np.maximum(ixmax - ixmin + 1.0, 0.0) ih = np.maximum(iymax - iymin + 1.0, 0.0) inters = iw * ih # union uni = ( (bb[2] - bb[0] + 1.0) * (bb[3] - bb[1] + 1.0) + (BBGT[:, 2] - BBGT[:, 0] + 1.0) * (BBGT[:, 3] - BBGT[:, 1] + 1.0) - inters ) overlaps = inters / uni ovmax = np.max(overlaps) jmax = np.argmax(overlaps) if ovmax > ovthresh: if not R["difficult"][jmax]: if not R["det"][jmax]: tp[d] = 1.0 R["det"][jmax] = 1 else: fp[d] = 1.0 else: fp[d] = 1.0 # compute precision recall fp = np.cumsum(fp) tp = np.cumsum(tp) rec = tp / float(npos) # avoid divide by zero in case the first detection matches a difficult # ground truth prec = tp / np.maximum(tp + fp, np.finfo(np.float64).eps) ap = voc_ap(rec, prec, use_07_metric) return rec, prec, ap ================================================ FILE: detectron2/detectron2/evaluation/rotated_coco_evaluation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import itertools import json import numpy as np import os import torch from pycocotools.cocoeval import COCOeval, maskUtils from detectron2.structures import BoxMode, RotatedBoxes, pairwise_iou_rotated from detectron2.utils.file_io import PathManager from .coco_evaluation import COCOEvaluator class RotatedCOCOeval(COCOeval): @staticmethod def is_rotated(box_list): if type(box_list) == np.ndarray: return box_list.shape[1] == 5 elif type(box_list) == list: if box_list == []: # cannot decide the box_dim return False return np.all( np.array( [ (len(obj) == 5) and ((type(obj) == list) or (type(obj) == np.ndarray)) for obj in box_list ] ) ) return False @staticmethod def boxlist_to_tensor(boxlist, output_box_dim): if type(boxlist) == np.ndarray: box_tensor = torch.from_numpy(boxlist) elif type(boxlist) == list: if boxlist == []: return torch.zeros((0, output_box_dim), dtype=torch.float32) else: box_tensor = torch.FloatTensor(boxlist) else: raise Exception("Unrecognized boxlist type") input_box_dim = box_tensor.shape[1] if input_box_dim != output_box_dim: if input_box_dim == 4 and output_box_dim == 5: box_tensor = BoxMode.convert(box_tensor, BoxMode.XYWH_ABS, BoxMode.XYWHA_ABS) else: raise Exception( "Unable to convert from {}-dim box to {}-dim box".format( input_box_dim, output_box_dim ) ) return box_tensor def compute_iou_dt_gt(self, dt, gt, is_crowd): if self.is_rotated(dt) or self.is_rotated(gt): # TODO: take is_crowd into consideration assert all(c == 0 for c in is_crowd) dt = RotatedBoxes(self.boxlist_to_tensor(dt, output_box_dim=5)) gt = RotatedBoxes(self.boxlist_to_tensor(gt, output_box_dim=5)) return pairwise_iou_rotated(dt, gt) else: # This is the same as the classical COCO evaluation return maskUtils.iou(dt, gt, is_crowd) def computeIoU(self, imgId, catId): p = self.params if p.useCats: gt = self._gts[imgId, catId] dt = self._dts[imgId, catId] else: gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]] dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]] if len(gt) == 0 and len(dt) == 0: return [] inds = np.argsort([-d["score"] for d in dt], kind="mergesort") dt = [dt[i] for i in inds] if len(dt) > p.maxDets[-1]: dt = dt[0 : p.maxDets[-1]] assert p.iouType == "bbox", "unsupported iouType for iou computation" g = [g["bbox"] for g in gt] d = [d["bbox"] for d in dt] # compute iou between each dt and gt region iscrowd = [int(o["iscrowd"]) for o in gt] # Note: this function is copied from cocoeval.py in cocoapi # and the major difference is here. ious = self.compute_iou_dt_gt(d, g, iscrowd) return ious class RotatedCOCOEvaluator(COCOEvaluator): """ Evaluate object proposal/instance detection outputs using COCO-like metrics and APIs, with rotated boxes support. Note: this uses IOU only and does not consider angle differences. """ def process(self, inputs, outputs): """ Args: inputs: the inputs to a COCO model (e.g., GeneralizedRCNN). It is a list of dict. Each dict corresponds to an image and contains keys like "height", "width", "file_name", "image_id". outputs: the outputs of a COCO model. It is a list of dicts with key "instances" that contains :class:`Instances`. """ for input, output in zip(inputs, outputs): prediction = {"image_id": input["image_id"]} if "instances" in output: instances = output["instances"].to(self._cpu_device) prediction["instances"] = self.instances_to_json(instances, input["image_id"]) if "proposals" in output: prediction["proposals"] = output["proposals"].to(self._cpu_device) self._predictions.append(prediction) def instances_to_json(self, instances, img_id): num_instance = len(instances) if num_instance == 0: return [] boxes = instances.pred_boxes.tensor.numpy() if boxes.shape[1] == 4: boxes = BoxMode.convert(boxes, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS) boxes = boxes.tolist() scores = instances.scores.tolist() classes = instances.pred_classes.tolist() results = [] for k in range(num_instance): result = { "image_id": img_id, "category_id": classes[k], "bbox": boxes[k], "score": scores[k], } results.append(result) return results def _eval_predictions(self, predictions, img_ids=None): # img_ids: unused """ Evaluate predictions on the given tasks. Fill self._results with the metrics of the tasks. """ self._logger.info("Preparing results for COCO format ...") coco_results = list(itertools.chain(*[x["instances"] for x in predictions])) # unmap the category ids for COCO if hasattr(self._metadata, "thing_dataset_id_to_contiguous_id"): reverse_id_mapping = { v: k for k, v in self._metadata.thing_dataset_id_to_contiguous_id.items() } for result in coco_results: result["category_id"] = reverse_id_mapping[result["category_id"]] if self._output_dir: file_path = os.path.join(self._output_dir, "coco_instances_results.json") self._logger.info("Saving results to {}".format(file_path)) with PathManager.open(file_path, "w") as f: f.write(json.dumps(coco_results)) f.flush() if not self._do_evaluation: self._logger.info("Annotations are not available for evaluation.") return self._logger.info("Evaluating predictions ...") assert self._tasks is None or set(self._tasks) == { "bbox" }, "[RotatedCOCOEvaluator] Only bbox evaluation is supported" coco_eval = ( self._evaluate_predictions_on_coco(self._coco_api, coco_results) if len(coco_results) > 0 else None # cocoapi does not handle empty results very well ) task = "bbox" res = self._derive_coco_results( coco_eval, task, class_names=self._metadata.get("thing_classes") ) self._results[task] = res def _evaluate_predictions_on_coco(self, coco_gt, coco_results): """ Evaluate the coco results using COCOEval API. """ assert len(coco_results) > 0 coco_dt = coco_gt.loadRes(coco_results) # Only bbox is supported for now coco_eval = RotatedCOCOeval(coco_gt, coco_dt, iouType="bbox") coco_eval.evaluate() coco_eval.accumulate() coco_eval.summarize() return coco_eval ================================================ FILE: detectron2/detectron2/evaluation/sem_seg_evaluation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import itertools import json import logging import numpy as np import os from collections import OrderedDict from typing import Optional, Union import pycocotools.mask as mask_util import torch from PIL import Image from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.utils.comm import all_gather, is_main_process, synchronize from detectron2.utils.file_io import PathManager from .evaluator import DatasetEvaluator _CV2_IMPORTED = True try: import cv2 # noqa except ImportError: # OpenCV is an optional dependency at the moment _CV2_IMPORTED = False def load_image_into_numpy_array( filename: str, copy: bool = False, dtype: Optional[Union[np.dtype, str]] = None, ) -> np.ndarray: with PathManager.open(filename, "rb") as f: array = np.array(Image.open(f), copy=copy, dtype=dtype) return array class SemSegEvaluator(DatasetEvaluator): """ Evaluate semantic segmentation metrics. """ def __init__( self, dataset_name, distributed=True, output_dir=None, *, sem_seg_loading_fn=load_image_into_numpy_array, num_classes=None, ignore_label=None, ): """ Args: dataset_name (str): name of the dataset to be evaluated. distributed (bool): if True, will collect results from all ranks for evaluation. Otherwise, will evaluate the results in the current process. output_dir (str): an output directory to dump results. sem_seg_loading_fn: function to read sem seg file and load into numpy array. Default provided, but projects can customize. num_classes, ignore_label: deprecated argument """ self._logger = logging.getLogger(__name__) if num_classes is not None: self._logger.warn( "SemSegEvaluator(num_classes) is deprecated! It should be obtained from metadata." ) if ignore_label is not None: self._logger.warn( "SemSegEvaluator(ignore_label) is deprecated! It should be obtained from metadata." ) self._dataset_name = dataset_name self._distributed = distributed self._output_dir = output_dir self._cpu_device = torch.device("cpu") self.input_file_to_gt_file = { dataset_record["file_name"]: dataset_record["sem_seg_file_name"] for dataset_record in DatasetCatalog.get(dataset_name) } meta = MetadataCatalog.get(dataset_name) # Dict that maps contiguous training ids to COCO category ids try: c2d = meta.stuff_dataset_id_to_contiguous_id self._contiguous_id_to_dataset_id = {v: k for k, v in c2d.items()} except AttributeError: self._contiguous_id_to_dataset_id = None self._class_names = meta.stuff_classes self.sem_seg_loading_fn = sem_seg_loading_fn self._num_classes = len(meta.stuff_classes) if num_classes is not None: assert self._num_classes == num_classes, f"{self._num_classes} != {num_classes}" self._ignore_label = ignore_label if ignore_label is not None else meta.ignore_label # This is because cv2.erode did not work for int datatype. Only works for uint8. self._compute_boundary_iou = True if not _CV2_IMPORTED: self._compute_boundary_iou = False self._logger.warn( """Boundary IoU calculation requires OpenCV. B-IoU metrics are not going to be computed because OpenCV is not available to import.""" ) if self._num_classes >= np.iinfo(np.uint8).max: self._compute_boundary_iou = False self._logger.warn( f"""SemSegEvaluator(num_classes) is more than supported value for Boundary IoU calculation! B-IoU metrics are not going to be computed. Max allowed value (exclusive) for num_classes for calculating Boundary IoU is {np.iinfo(np.uint8).max}. The number of classes of dataset {self._dataset_name} is {self._num_classes}""" ) def reset(self): self._conf_matrix = np.zeros((self._num_classes + 1, self._num_classes + 1), dtype=np.int64) self._b_conf_matrix = np.zeros( (self._num_classes + 1, self._num_classes + 1), dtype=np.int64 ) self._predictions = [] def process(self, inputs, outputs): """ Args: inputs: the inputs to a model. It is a list of dicts. Each dict corresponds to an image and contains keys like "height", "width", "file_name". outputs: the outputs of a model. It is either list of semantic segmentation predictions (Tensor [H, W]) or list of dicts with key "sem_seg" that contains semantic segmentation prediction in the same format. """ for input, output in zip(inputs, outputs): output = output["sem_seg"].argmax(dim=0).to(self._cpu_device) pred = np.array(output, dtype=np.int) gt_filename = self.input_file_to_gt_file[input["file_name"]] gt = self.sem_seg_loading_fn(gt_filename, dtype=np.int) gt[gt == self._ignore_label] = self._num_classes self._conf_matrix += np.bincount( (self._num_classes + 1) * pred.reshape(-1) + gt.reshape(-1), minlength=self._conf_matrix.size, ).reshape(self._conf_matrix.shape) if self._compute_boundary_iou: b_gt = self._mask_to_boundary(gt.astype(np.uint8)) b_pred = self._mask_to_boundary(pred.astype(np.uint8)) self._b_conf_matrix += np.bincount( (self._num_classes + 1) * b_pred.reshape(-1) + b_gt.reshape(-1), minlength=self._conf_matrix.size, ).reshape(self._conf_matrix.shape) self._predictions.extend(self.encode_json_sem_seg(pred, input["file_name"])) def evaluate(self): """ Evaluates standard semantic segmentation metrics (http://cocodataset.org/#stuff-eval): * Mean intersection-over-union averaged across classes (mIoU) * Frequency Weighted IoU (fwIoU) * Mean pixel accuracy averaged across classes (mACC) * Pixel Accuracy (pACC) """ if self._distributed: synchronize() conf_matrix_list = all_gather(self._conf_matrix) b_conf_matrix_list = all_gather(self._b_conf_matrix) self._predictions = all_gather(self._predictions) self._predictions = list(itertools.chain(*self._predictions)) if not is_main_process(): return self._conf_matrix = np.zeros_like(self._conf_matrix) for conf_matrix in conf_matrix_list: self._conf_matrix += conf_matrix self._b_conf_matrix = np.zeros_like(self._b_conf_matrix) for b_conf_matrix in b_conf_matrix_list: self._b_conf_matrix += b_conf_matrix if self._output_dir: PathManager.mkdirs(self._output_dir) file_path = os.path.join(self._output_dir, "sem_seg_predictions.json") with PathManager.open(file_path, "w") as f: f.write(json.dumps(self._predictions)) acc = np.full(self._num_classes, np.nan, dtype=np.float) iou = np.full(self._num_classes, np.nan, dtype=np.float) tp = self._conf_matrix.diagonal()[:-1].astype(np.float) pos_gt = np.sum(self._conf_matrix[:-1, :-1], axis=0).astype(np.float) class_weights = pos_gt / np.sum(pos_gt) pos_pred = np.sum(self._conf_matrix[:-1, :-1], axis=1).astype(np.float) acc_valid = pos_gt > 0 acc[acc_valid] = tp[acc_valid] / pos_gt[acc_valid] union = pos_gt + pos_pred - tp iou_valid = np.logical_and(acc_valid, union > 0) iou[iou_valid] = tp[iou_valid] / union[iou_valid] macc = np.sum(acc[acc_valid]) / np.sum(acc_valid) miou = np.sum(iou[iou_valid]) / np.sum(iou_valid) fiou = np.sum(iou[iou_valid] * class_weights[iou_valid]) pacc = np.sum(tp) / np.sum(pos_gt) if self._compute_boundary_iou: b_iou = np.full(self._num_classes, np.nan, dtype=np.float) b_tp = self._b_conf_matrix.diagonal()[:-1].astype(np.float) b_pos_gt = np.sum(self._b_conf_matrix[:-1, :-1], axis=0).astype(np.float) b_pos_pred = np.sum(self._b_conf_matrix[:-1, :-1], axis=1).astype(np.float) b_union = b_pos_gt + b_pos_pred - b_tp b_iou_valid = b_union > 0 b_iou[b_iou_valid] = b_tp[b_iou_valid] / b_union[b_iou_valid] res = {} res["mIoU"] = 100 * miou res["fwIoU"] = 100 * fiou for i, name in enumerate(self._class_names): res[f"IoU-{name}"] = 100 * iou[i] if self._compute_boundary_iou: res[f"BoundaryIoU-{name}"] = 100 * b_iou[i] res[f"min(IoU, B-Iou)-{name}"] = 100 * min(iou[i], b_iou[i]) res["mACC"] = 100 * macc res["pACC"] = 100 * pacc for i, name in enumerate(self._class_names): res[f"ACC-{name}"] = 100 * acc[i] if self._output_dir: file_path = os.path.join(self._output_dir, "sem_seg_evaluation.pth") with PathManager.open(file_path, "wb") as f: torch.save(res, f) results = OrderedDict({"sem_seg": res}) self._logger.info(results) return results def encode_json_sem_seg(self, sem_seg, input_file_name): """ Convert semantic segmentation to COCO stuff format with segments encoded as RLEs. See http://cocodataset.org/#format-results """ json_list = [] for label in np.unique(sem_seg): if self._contiguous_id_to_dataset_id is not None: assert ( label in self._contiguous_id_to_dataset_id ), "Label {} is not in the metadata info for {}".format(label, self._dataset_name) dataset_id = self._contiguous_id_to_dataset_id[label] else: dataset_id = int(label) mask = (sem_seg == label).astype(np.uint8) mask_rle = mask_util.encode(np.array(mask[:, :, None], order="F"))[0] mask_rle["counts"] = mask_rle["counts"].decode("utf-8") json_list.append( {"file_name": input_file_name, "category_id": dataset_id, "segmentation": mask_rle} ) return json_list def _mask_to_boundary(self, mask: np.ndarray, dilation_ratio=0.02): assert mask.ndim == 2, "mask_to_boundary expects a 2-dimensional image" h, w = mask.shape diag_len = np.sqrt(h**2 + w**2) dilation = max(1, int(round(dilation_ratio * diag_len))) kernel = np.ones((3, 3), dtype=np.uint8) padded_mask = cv2.copyMakeBorder(mask, 1, 1, 1, 1, cv2.BORDER_CONSTANT, value=0) eroded_mask_with_padding = cv2.erode(padded_mask, kernel, iterations=dilation) eroded_mask = eroded_mask_with_padding[1:-1, 1:-1] boundary = mask - eroded_mask return boundary ================================================ FILE: detectron2/detectron2/evaluation/testing.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np import pprint import sys from collections.abc import Mapping def print_csv_format(results): """ Print main metrics in a format similar to Detectron, so that they are easy to copypaste into a spreadsheet. Args: results (OrderedDict[dict]): task_name -> {metric -> score} unordered dict can also be printed, but in arbitrary order """ assert isinstance(results, Mapping) or not len(results), results logger = logging.getLogger(__name__) for task, res in results.items(): if isinstance(res, Mapping): # Don't print "AP-category" metrics since they are usually not tracked. important_res = [(k, v) for k, v in res.items() if "-" not in k] logger.info("copypaste: Task: {}".format(task)) logger.info("copypaste: " + ",".join([k[0] for k in important_res])) logger.info("copypaste: " + ",".join(["{0:.4f}".format(k[1]) for k in important_res])) else: logger.info(f"copypaste: {task}={res}") def verify_results(cfg, results): """ Args: results (OrderedDict[dict]): task_name -> {metric -> score} Returns: bool: whether the verification succeeds or not """ expected_results = cfg.TEST.EXPECTED_RESULTS if not len(expected_results): return True ok = True for task, metric, expected, tolerance in expected_results: actual = results[task].get(metric, None) if actual is None: ok = False continue if not np.isfinite(actual): ok = False continue diff = abs(actual - expected) if diff > tolerance: ok = False logger = logging.getLogger(__name__) if not ok: logger.error("Result verification failed!") logger.error("Expected Results: " + str(expected_results)) logger.error("Actual Results: " + pprint.pformat(results)) sys.exit(1) else: logger.info("Results verification passed.") return ok def flatten_results_dict(results): """ Expand a hierarchical dict of scalars into a flat dict of scalars. If results[k1][k2][k3] = v, the returned dict will have the entry {"k1/k2/k3": v}. Args: results (dict): """ r = {} for k, v in results.items(): if isinstance(v, Mapping): v = flatten_results_dict(v) for kk, vv in v.items(): r[k + "/" + kk] = vv else: r[k] = v return r ================================================ FILE: detectron2/detectron2/export/README.md ================================================ This directory contains code to prepare a detectron2 model for deployment. Currently it supports exporting a detectron2 model to TorchScript, ONNX, or (deprecated) Caffe2 format. Please see [documentation](https://detectron2.readthedocs.io/tutorials/deployment.html) for its usage. ### Acknowledgements Thanks to Mobile Vision team at Facebook for developing the Caffe2 conversion tools. Thanks to Computing Platform Department - PAI team at Alibaba Group (@bddpqq, @chenbohua3) who help export Detectron2 models to TorchScript. Thanks to ONNX Converter team at Microsoft who help export Detectron2 models to ONNX. ================================================ FILE: detectron2/detectron2/export/__init__.py ================================================ # -*- coding: utf-8 -*- import warnings from .flatten import TracingAdapter from .torchscript import dump_torchscript_IR, scripting_with_instances try: from caffe2.proto import caffe2_pb2 as _tmp # caffe2 is optional except ImportError: pass else: from .api import * # TODO: Update ONNX Opset version and run tests when a newer PyTorch is supported STABLE_ONNX_OPSET_VERSION = 11 def add_export_config(cfg): warnings.warn( "add_export_config has been deprecated and behaves as no-op function.", DeprecationWarning ) return cfg __all__ = [k for k in globals().keys() if not k.startswith("_")] ================================================ FILE: detectron2/detectron2/export/api.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import logging import os import torch from caffe2.proto import caffe2_pb2 from torch import nn from detectron2.config import CfgNode from detectron2.utils.file_io import PathManager from .caffe2_inference import ProtobufDetectionModel from .caffe2_modeling import META_ARCH_CAFFE2_EXPORT_TYPE_MAP, convert_batched_inputs_to_c2_format from .shared import get_pb_arg_vali, get_pb_arg_vals, save_graph __all__ = [ "Caffe2Model", "Caffe2Tracer", ] class Caffe2Tracer: """ Make a detectron2 model traceable with Caffe2 operators. This class creates a traceable version of a detectron2 model which: 1. Rewrite parts of the model using ops in Caffe2. Note that some ops do not have GPU implementation in Caffe2. 2. Remove post-processing and only produce raw layer outputs After making a traceable model, the class provide methods to export such a model to different deployment formats. Exported graph produced by this class take two input tensors: 1. (1, C, H, W) float "data" which is an image (usually in [0, 255]). (H, W) often has to be padded to multiple of 32 (depend on the model architecture). 2. 1x3 float "im_info", each row of which is (height, width, 1.0). Height and width are true image shapes before padding. The class currently only supports models using builtin meta architectures. Batch inference is not supported, and contributions are welcome. """ def __init__(self, cfg: CfgNode, model: nn.Module, inputs): """ Args: cfg (CfgNode): a detectron2 config used to construct caffe2-compatible model. model (nn.Module): An original pytorch model. Must be among a few official models in detectron2 that can be converted to become caffe2-compatible automatically. Weights have to be already loaded to this model. inputs: sample inputs that the given model takes for inference. Will be used to trace the model. For most models, random inputs with no detected objects will not work as they lead to wrong traces. """ assert isinstance(cfg, CfgNode), cfg assert isinstance(model, torch.nn.Module), type(model) # TODO make it support custom models, by passing in c2 model directly C2MetaArch = META_ARCH_CAFFE2_EXPORT_TYPE_MAP[cfg.MODEL.META_ARCHITECTURE] self.traceable_model = C2MetaArch(cfg, copy.deepcopy(model)) self.inputs = inputs self.traceable_inputs = self.traceable_model.get_caffe2_inputs(inputs) def export_caffe2(self): """ Export the model to Caffe2's protobuf format. The returned object can be saved with its :meth:`.save_protobuf()` method. The result can be loaded and executed using Caffe2 runtime. Returns: :class:`Caffe2Model` """ from .caffe2_export import export_caffe2_detection_model predict_net, init_net = export_caffe2_detection_model( self.traceable_model, self.traceable_inputs ) return Caffe2Model(predict_net, init_net) def export_onnx(self): """ Export the model to ONNX format. Note that the exported model contains custom ops only available in caffe2, therefore it cannot be directly executed by other runtime (such as onnxruntime or TensorRT). Post-processing or transformation passes may be applied on the model to accommodate different runtimes, but we currently do not provide support for them. Returns: onnx.ModelProto: an onnx model. """ from .caffe2_export import export_onnx_model as export_onnx_model_impl return export_onnx_model_impl(self.traceable_model, (self.traceable_inputs,)) def export_torchscript(self): """ Export the model to a ``torch.jit.TracedModule`` by tracing. The returned object can be saved to a file by ``.save()``. Returns: torch.jit.TracedModule: a torch TracedModule """ logger = logging.getLogger(__name__) logger.info("Tracing the model with torch.jit.trace ...") with torch.no_grad(): return torch.jit.trace(self.traceable_model, (self.traceable_inputs,)) class Caffe2Model(nn.Module): """ A wrapper around the traced model in Caffe2's protobuf format. The exported graph has different inputs/outputs from the original Pytorch model, as explained in :class:`Caffe2Tracer`. This class wraps around the exported graph to simulate the same interface as the original Pytorch model. It also provides functions to save/load models in Caffe2's format.' Examples: :: c2_model = Caffe2Tracer(cfg, torch_model, inputs).export_caffe2() inputs = [{"image": img_tensor_CHW}] outputs = c2_model(inputs) orig_outputs = torch_model(inputs) """ def __init__(self, predict_net, init_net): super().__init__() self.eval() # always in eval mode self._predict_net = predict_net self._init_net = init_net self._predictor = None __init__.__HIDE_SPHINX_DOC__ = True @property def predict_net(self): """ caffe2.core.Net: the underlying caffe2 predict net """ return self._predict_net @property def init_net(self): """ caffe2.core.Net: the underlying caffe2 init net """ return self._init_net def save_protobuf(self, output_dir): """ Save the model as caffe2's protobuf format. It saves the following files: * "model.pb": definition of the graph. Can be visualized with tools like `netron `_. * "model_init.pb": model parameters * "model.pbtxt": human-readable definition of the graph. Not needed for deployment. Args: output_dir (str): the output directory to save protobuf files. """ logger = logging.getLogger(__name__) logger.info("Saving model to {} ...".format(output_dir)) if not PathManager.exists(output_dir): PathManager.mkdirs(output_dir) with PathManager.open(os.path.join(output_dir, "model.pb"), "wb") as f: f.write(self._predict_net.SerializeToString()) with PathManager.open(os.path.join(output_dir, "model.pbtxt"), "w") as f: f.write(str(self._predict_net)) with PathManager.open(os.path.join(output_dir, "model_init.pb"), "wb") as f: f.write(self._init_net.SerializeToString()) def save_graph(self, output_file, inputs=None): """ Save the graph as SVG format. Args: output_file (str): a SVG file inputs: optional inputs given to the model. If given, the inputs will be used to run the graph to record shape of every tensor. The shape information will be saved together with the graph. """ from .caffe2_export import run_and_save_graph if inputs is None: save_graph(self._predict_net, output_file, op_only=False) else: size_divisibility = get_pb_arg_vali(self._predict_net, "size_divisibility", 0) device = get_pb_arg_vals(self._predict_net, "device", b"cpu").decode("ascii") inputs = convert_batched_inputs_to_c2_format(inputs, size_divisibility, device) inputs = [x.cpu().numpy() for x in inputs] run_and_save_graph(self._predict_net, self._init_net, inputs, output_file) @staticmethod def load_protobuf(dir): """ Args: dir (str): a directory used to save Caffe2Model with :meth:`save_protobuf`. The files "model.pb" and "model_init.pb" are needed. Returns: Caffe2Model: the caffe2 model loaded from this directory. """ predict_net = caffe2_pb2.NetDef() with PathManager.open(os.path.join(dir, "model.pb"), "rb") as f: predict_net.ParseFromString(f.read()) init_net = caffe2_pb2.NetDef() with PathManager.open(os.path.join(dir, "model_init.pb"), "rb") as f: init_net.ParseFromString(f.read()) return Caffe2Model(predict_net, init_net) def __call__(self, inputs): """ An interface that wraps around a Caffe2 model and mimics detectron2's models' input/output format. See details about the format at :doc:`/tutorials/models`. This is used to compare the outputs of caffe2 model with its original torch model. Due to the extra conversion between Pytorch/Caffe2, this method is not meant for benchmark. Because of the conversion, this method also has dependency on detectron2 in order to convert to detectron2's output format. """ if self._predictor is None: self._predictor = ProtobufDetectionModel(self._predict_net, self._init_net) return self._predictor(inputs) ================================================ FILE: detectron2/detectron2/export/c10.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import math from typing import Dict import torch import torch.nn.functional as F from detectron2.layers import ShapeSpec, cat from detectron2.layers.roi_align_rotated import ROIAlignRotated from detectron2.modeling import poolers from detectron2.modeling.proposal_generator import rpn from detectron2.modeling.roi_heads.mask_head import mask_rcnn_inference from detectron2.structures import Boxes, ImageList, Instances, Keypoints from .shared import alias, to_device """ This file contains caffe2-compatible implementation of several detectron2 components. """ class Caffe2Boxes(Boxes): """ Representing a list of detectron2.structures.Boxes from minibatch, each box is represented by a 5d vector (batch index + 4 coordinates), or a 6d vector (batch index + 5 coordinates) for RotatedBoxes. """ def __init__(self, tensor): assert isinstance(tensor, torch.Tensor) assert tensor.dim() == 2 and tensor.size(-1) in [4, 5, 6], tensor.size() # TODO: make tensor immutable when dim is Nx5 for Boxes, # and Nx6 for RotatedBoxes? self.tensor = tensor # TODO clean up this class, maybe just extend Instances class InstancesList(object): """ Tensor representation of a list of Instances object for a batch of images. When dealing with a batch of images with Caffe2 ops, a list of bboxes (instances) are usually represented by single Tensor with size (sigma(Ni), 5) or (sigma(Ni), 4) plus a batch split Tensor. This class is for providing common functions to convert between these two representations. """ def __init__(self, im_info, indices, extra_fields=None): # [N, 3] -> (H, W, Scale) self.im_info = im_info # [N,] -> indice of batch to which the instance belongs self.indices = indices # [N, ...] self.batch_extra_fields = extra_fields or {} self.image_size = self.im_info def get_fields(self): """like `get_fields` in the Instances object, but return each field in tensor representations""" ret = {} for k, v in self.batch_extra_fields.items(): # if isinstance(v, torch.Tensor): # tensor_rep = v # elif isinstance(v, (Boxes, Keypoints)): # tensor_rep = v.tensor # else: # raise ValueError("Can't find tensor representation for: {}".format()) ret[k] = v return ret def has(self, name): return name in self.batch_extra_fields def set(self, name, value): # len(tensor) is a bad practice that generates ONNX constants during tracing. # Although not a problem for the `assert` statement below, torch ONNX exporter # still raises a misleading warning as it does not this call comes from `assert` if isinstance(value, Boxes): data_len = value.tensor.shape[0] elif isinstance(value, torch.Tensor): data_len = value.shape[0] else: data_len = len(value) if len(self.batch_extra_fields): assert ( len(self) == data_len ), "Adding a field of length {} to a Instances of length {}".format(data_len, len(self)) self.batch_extra_fields[name] = value def __setattr__(self, name, val): if name in ["im_info", "indices", "batch_extra_fields", "image_size"]: super().__setattr__(name, val) else: self.set(name, val) def __getattr__(self, name): if name not in self.batch_extra_fields: raise AttributeError("Cannot find field '{}' in the given Instances!".format(name)) return self.batch_extra_fields[name] def __len__(self): return len(self.indices) def flatten(self): ret = [] for _, v in self.batch_extra_fields.items(): if isinstance(v, (Boxes, Keypoints)): ret.append(v.tensor) else: ret.append(v) return ret @staticmethod def to_d2_instances_list(instances_list): """ Convert InstancesList to List[Instances]. The input `instances_list` can also be a List[Instances], in this case this method is a non-op. """ if not isinstance(instances_list, InstancesList): assert all(isinstance(x, Instances) for x in instances_list) return instances_list ret = [] for i, info in enumerate(instances_list.im_info): instances = Instances(torch.Size([int(info[0].item()), int(info[1].item())])) ids = instances_list.indices == i for k, v in instances_list.batch_extra_fields.items(): if isinstance(v, torch.Tensor): instances.set(k, v[ids]) continue elif isinstance(v, Boxes): instances.set(k, v[ids, -4:]) continue target_type, tensor_source = v assert isinstance(tensor_source, torch.Tensor) assert tensor_source.shape[0] == instances_list.indices.shape[0] tensor_source = tensor_source[ids] if issubclass(target_type, Boxes): instances.set(k, Boxes(tensor_source[:, -4:])) elif issubclass(target_type, Keypoints): instances.set(k, Keypoints(tensor_source)) elif issubclass(target_type, torch.Tensor): instances.set(k, tensor_source) else: raise ValueError("Can't handle targe type: {}".format(target_type)) ret.append(instances) return ret class Caffe2Compatible(object): """ A model can inherit this class to indicate that it can be traced and deployed with caffe2. """ def _get_tensor_mode(self): return self._tensor_mode def _set_tensor_mode(self, v): self._tensor_mode = v tensor_mode = property(_get_tensor_mode, _set_tensor_mode) """ If true, the model expects C2-style tensor only inputs/outputs format. """ class Caffe2RPN(Caffe2Compatible, rpn.RPN): @classmethod def from_config(cls, cfg, input_shape: Dict[str, ShapeSpec]): ret = super(Caffe2Compatible, cls).from_config(cfg, input_shape) assert tuple(cfg.MODEL.RPN.BBOX_REG_WEIGHTS) == (1.0, 1.0, 1.0, 1.0) or tuple( cfg.MODEL.RPN.BBOX_REG_WEIGHTS ) == (1.0, 1.0, 1.0, 1.0, 1.0) return ret def _generate_proposals( self, images, objectness_logits_pred, anchor_deltas_pred, gt_instances=None ): assert isinstance(images, ImageList) if self.tensor_mode: im_info = images.image_sizes else: im_info = torch.tensor([[im_sz[0], im_sz[1], 1.0] for im_sz in images.image_sizes]).to( images.tensor.device ) assert isinstance(im_info, torch.Tensor) rpn_rois_list = [] rpn_roi_probs_list = [] for scores, bbox_deltas, cell_anchors_tensor, feat_stride in zip( objectness_logits_pred, anchor_deltas_pred, iter(self.anchor_generator.cell_anchors), self.anchor_generator.strides, ): scores = scores.detach() bbox_deltas = bbox_deltas.detach() rpn_rois, rpn_roi_probs = torch.ops._caffe2.GenerateProposals( scores, bbox_deltas, im_info, cell_anchors_tensor, spatial_scale=1.0 / feat_stride, pre_nms_topN=self.pre_nms_topk[self.training], post_nms_topN=self.post_nms_topk[self.training], nms_thresh=self.nms_thresh, min_size=self.min_box_size, # correct_transform_coords=True, # deprecated argument angle_bound_on=True, # Default angle_bound_lo=-180, angle_bound_hi=180, clip_angle_thresh=1.0, # Default legacy_plus_one=False, ) rpn_rois_list.append(rpn_rois) rpn_roi_probs_list.append(rpn_roi_probs) # For FPN in D2, in RPN all proposals from different levels are concated # together, ranked and picked by top post_nms_topk. Then in ROIPooler # it calculates level_assignments and calls the RoIAlign from # the corresponding level. if len(objectness_logits_pred) == 1: rpn_rois = rpn_rois_list[0] rpn_roi_probs = rpn_roi_probs_list[0] else: assert len(rpn_rois_list) == len(rpn_roi_probs_list) rpn_post_nms_topN = self.post_nms_topk[self.training] device = rpn_rois_list[0].device input_list = [to_device(x, "cpu") for x in (rpn_rois_list + rpn_roi_probs_list)] # TODO remove this after confirming rpn_max_level/rpn_min_level # is not needed in CollectRpnProposals. feature_strides = list(self.anchor_generator.strides) rpn_min_level = int(math.log2(feature_strides[0])) rpn_max_level = int(math.log2(feature_strides[-1])) assert (rpn_max_level - rpn_min_level + 1) == len( rpn_rois_list ), "CollectRpnProposals requires continuous levels" rpn_rois = torch.ops._caffe2.CollectRpnProposals( input_list, # NOTE: in current implementation, rpn_max_level and rpn_min_level # are not needed, only the subtraction of two matters and it # can be infer from the number of inputs. Keep them now for # consistency. rpn_max_level=2 + len(rpn_rois_list) - 1, rpn_min_level=2, rpn_post_nms_topN=rpn_post_nms_topN, ) rpn_rois = to_device(rpn_rois, device) rpn_roi_probs = [] proposals = self.c2_postprocess(im_info, rpn_rois, rpn_roi_probs, self.tensor_mode) return proposals, {} def forward(self, images, features, gt_instances=None): assert not self.training features = [features[f] for f in self.in_features] objectness_logits_pred, anchor_deltas_pred = self.rpn_head(features) return self._generate_proposals( images, objectness_logits_pred, anchor_deltas_pred, gt_instances, ) @staticmethod def c2_postprocess(im_info, rpn_rois, rpn_roi_probs, tensor_mode): proposals = InstancesList( im_info=im_info, indices=rpn_rois[:, 0], extra_fields={ "proposal_boxes": Caffe2Boxes(rpn_rois), "objectness_logits": (torch.Tensor, rpn_roi_probs), }, ) if not tensor_mode: proposals = InstancesList.to_d2_instances_list(proposals) else: proposals = [proposals] return proposals class Caffe2ROIPooler(Caffe2Compatible, poolers.ROIPooler): @staticmethod def c2_preprocess(box_lists): assert all(isinstance(x, Boxes) for x in box_lists) if all(isinstance(x, Caffe2Boxes) for x in box_lists): # input is pure-tensor based assert len(box_lists) == 1 pooler_fmt_boxes = box_lists[0].tensor else: pooler_fmt_boxes = poolers.convert_boxes_to_pooler_format(box_lists) return pooler_fmt_boxes def forward(self, x, box_lists): assert not self.training pooler_fmt_boxes = self.c2_preprocess(box_lists) num_level_assignments = len(self.level_poolers) if num_level_assignments == 1: if isinstance(self.level_poolers[0], ROIAlignRotated): c2_roi_align = torch.ops._caffe2.RoIAlignRotated aligned = True else: c2_roi_align = torch.ops._caffe2.RoIAlign aligned = self.level_poolers[0].aligned x0 = x[0] if x0.is_quantized: x0 = x0.dequantize() out = c2_roi_align( x0, pooler_fmt_boxes, order="NCHW", spatial_scale=float(self.level_poolers[0].spatial_scale), pooled_h=int(self.output_size[0]), pooled_w=int(self.output_size[1]), sampling_ratio=int(self.level_poolers[0].sampling_ratio), aligned=aligned, ) return out device = pooler_fmt_boxes.device assert ( self.max_level - self.min_level + 1 == 4 ), "Currently DistributeFpnProposals only support 4 levels" fpn_outputs = torch.ops._caffe2.DistributeFpnProposals( to_device(pooler_fmt_boxes, "cpu"), roi_canonical_scale=self.canonical_box_size, roi_canonical_level=self.canonical_level, roi_max_level=self.max_level, roi_min_level=self.min_level, legacy_plus_one=False, ) fpn_outputs = [to_device(x, device) for x in fpn_outputs] rois_fpn_list = fpn_outputs[:-1] rois_idx_restore_int32 = fpn_outputs[-1] roi_feat_fpn_list = [] for roi_fpn, x_level, pooler in zip(rois_fpn_list, x, self.level_poolers): if isinstance(pooler, ROIAlignRotated): c2_roi_align = torch.ops._caffe2.RoIAlignRotated aligned = True else: c2_roi_align = torch.ops._caffe2.RoIAlign aligned = bool(pooler.aligned) if x_level.is_quantized: x_level = x_level.dequantize() roi_feat_fpn = c2_roi_align( x_level, roi_fpn, order="NCHW", spatial_scale=float(pooler.spatial_scale), pooled_h=int(self.output_size[0]), pooled_w=int(self.output_size[1]), sampling_ratio=int(pooler.sampling_ratio), aligned=aligned, ) roi_feat_fpn_list.append(roi_feat_fpn) roi_feat_shuffled = cat(roi_feat_fpn_list, dim=0) assert roi_feat_shuffled.numel() > 0 and rois_idx_restore_int32.numel() > 0, ( "Caffe2 export requires tracing with a model checkpoint + input that can produce valid" " detections. But no detections were obtained with the given checkpoint and input!" ) roi_feat = torch.ops._caffe2.BatchPermutation(roi_feat_shuffled, rois_idx_restore_int32) return roi_feat class Caffe2FastRCNNOutputsInference: def __init__(self, tensor_mode): self.tensor_mode = tensor_mode # whether the output is caffe2 tensor mode def __call__(self, box_predictor, predictions, proposals): """equivalent to FastRCNNOutputLayers.inference""" num_classes = box_predictor.num_classes score_thresh = box_predictor.test_score_thresh nms_thresh = box_predictor.test_nms_thresh topk_per_image = box_predictor.test_topk_per_image is_rotated = len(box_predictor.box2box_transform.weights) == 5 if is_rotated: box_dim = 5 assert box_predictor.box2box_transform.weights[4] == 1, ( "The weights for Rotated BBoxTransform in C2 have only 4 dimensions," + " thus enforcing the angle weight to be 1 for now" ) box2box_transform_weights = box_predictor.box2box_transform.weights[:4] else: box_dim = 4 box2box_transform_weights = box_predictor.box2box_transform.weights class_logits, box_regression = predictions if num_classes + 1 == class_logits.shape[1]: class_prob = F.softmax(class_logits, -1) else: assert num_classes == class_logits.shape[1] class_prob = F.sigmoid(class_logits) # BoxWithNMSLimit will infer num_classes from the shape of the class_prob # So append a zero column as placeholder for the background class class_prob = torch.cat((class_prob, torch.zeros(class_prob.shape[0], 1)), dim=1) assert box_regression.shape[1] % box_dim == 0 cls_agnostic_bbox_reg = box_regression.shape[1] // box_dim == 1 input_tensor_mode = proposals[0].proposal_boxes.tensor.shape[1] == box_dim + 1 rois = type(proposals[0].proposal_boxes).cat([p.proposal_boxes for p in proposals]) device, dtype = rois.tensor.device, rois.tensor.dtype if input_tensor_mode: im_info = proposals[0].image_size rois = rois.tensor else: im_info = torch.tensor( [[sz[0], sz[1], 1.0] for sz in [x.image_size for x in proposals]] ) batch_ids = cat( [ torch.full((b, 1), i, dtype=dtype, device=device) for i, b in enumerate(len(p) for p in proposals) ], dim=0, ) rois = torch.cat([batch_ids, rois.tensor], dim=1) roi_pred_bbox, roi_batch_splits = torch.ops._caffe2.BBoxTransform( to_device(rois, "cpu"), to_device(box_regression, "cpu"), to_device(im_info, "cpu"), weights=box2box_transform_weights, apply_scale=True, rotated=is_rotated, angle_bound_on=True, angle_bound_lo=-180, angle_bound_hi=180, clip_angle_thresh=1.0, legacy_plus_one=False, ) roi_pred_bbox = to_device(roi_pred_bbox, device) roi_batch_splits = to_device(roi_batch_splits, device) nms_outputs = torch.ops._caffe2.BoxWithNMSLimit( to_device(class_prob, "cpu"), to_device(roi_pred_bbox, "cpu"), to_device(roi_batch_splits, "cpu"), score_thresh=float(score_thresh), nms=float(nms_thresh), detections_per_im=int(topk_per_image), soft_nms_enabled=False, soft_nms_method="linear", soft_nms_sigma=0.5, soft_nms_min_score_thres=0.001, rotated=is_rotated, cls_agnostic_bbox_reg=cls_agnostic_bbox_reg, input_boxes_include_bg_cls=False, output_classes_include_bg_cls=False, legacy_plus_one=False, ) roi_score_nms = to_device(nms_outputs[0], device) roi_bbox_nms = to_device(nms_outputs[1], device) roi_class_nms = to_device(nms_outputs[2], device) roi_batch_splits_nms = to_device(nms_outputs[3], device) roi_keeps_nms = to_device(nms_outputs[4], device) roi_keeps_size_nms = to_device(nms_outputs[5], device) if not self.tensor_mode: roi_class_nms = roi_class_nms.to(torch.int64) roi_batch_ids = cat( [ torch.full((b, 1), i, dtype=dtype, device=device) for i, b in enumerate(int(x.item()) for x in roi_batch_splits_nms) ], dim=0, ) roi_class_nms = alias(roi_class_nms, "class_nms") roi_score_nms = alias(roi_score_nms, "score_nms") roi_bbox_nms = alias(roi_bbox_nms, "bbox_nms") roi_batch_splits_nms = alias(roi_batch_splits_nms, "batch_splits_nms") roi_keeps_nms = alias(roi_keeps_nms, "keeps_nms") roi_keeps_size_nms = alias(roi_keeps_size_nms, "keeps_size_nms") results = InstancesList( im_info=im_info, indices=roi_batch_ids[:, 0], extra_fields={ "pred_boxes": Caffe2Boxes(roi_bbox_nms), "scores": roi_score_nms, "pred_classes": roi_class_nms, }, ) if not self.tensor_mode: results = InstancesList.to_d2_instances_list(results) batch_splits = roi_batch_splits_nms.int().tolist() kept_indices = list(roi_keeps_nms.to(torch.int64).split(batch_splits)) else: results = [results] kept_indices = [roi_keeps_nms] return results, kept_indices class Caffe2MaskRCNNInference: def __call__(self, pred_mask_logits, pred_instances): """equivalent to mask_head.mask_rcnn_inference""" if all(isinstance(x, InstancesList) for x in pred_instances): assert len(pred_instances) == 1 mask_probs_pred = pred_mask_logits.sigmoid() mask_probs_pred = alias(mask_probs_pred, "mask_fcn_probs") pred_instances[0].pred_masks = mask_probs_pred else: mask_rcnn_inference(pred_mask_logits, pred_instances) class Caffe2KeypointRCNNInference: def __init__(self, use_heatmap_max_keypoint): self.use_heatmap_max_keypoint = use_heatmap_max_keypoint def __call__(self, pred_keypoint_logits, pred_instances): # just return the keypoint heatmap for now, # there will be option to call HeatmapMaxKeypointOp output = alias(pred_keypoint_logits, "kps_score") if all(isinstance(x, InstancesList) for x in pred_instances): assert len(pred_instances) == 1 if self.use_heatmap_max_keypoint: device = output.device output = torch.ops._caffe2.HeatmapMaxKeypoint( to_device(output, "cpu"), pred_instances[0].pred_boxes.tensor, should_output_softmax=True, # worth make it configerable? ) output = to_device(output, device) output = alias(output, "keypoints_out") pred_instances[0].pred_keypoints = output return pred_keypoint_logits ================================================ FILE: detectron2/detectron2/export/caffe2_export.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import io import logging import numpy as np from typing import List import onnx import onnx.optimizer import torch from caffe2.proto import caffe2_pb2 from caffe2.python import core from caffe2.python.onnx.backend import Caffe2Backend from tabulate import tabulate from termcolor import colored from torch.onnx import OperatorExportTypes from .shared import ( ScopedWS, construct_init_net_from_params, fuse_alias_placeholder, fuse_copy_between_cpu_and_gpu, get_params_from_init_net, group_norm_replace_aten_with_caffe2, infer_device_type, remove_dead_end_ops, remove_reshape_for_fc, save_graph, ) logger = logging.getLogger(__name__) def export_onnx_model(model, inputs): """ Trace and export a model to onnx format. Args: model (nn.Module): inputs (tuple[args]): the model will be called by `model(*inputs)` Returns: an onnx model """ assert isinstance(model, torch.nn.Module) # make sure all modules are in eval mode, onnx may change the training state # of the module if the states are not consistent def _check_eval(module): assert not module.training model.apply(_check_eval) # Export the model to ONNX with torch.no_grad(): with io.BytesIO() as f: torch.onnx.export( model, inputs, f, operator_export_type=OperatorExportTypes.ONNX_ATEN_FALLBACK, # verbose=True, # NOTE: uncomment this for debugging # export_params=True, ) onnx_model = onnx.load_from_string(f.getvalue()) return onnx_model def _op_stats(net_def): type_count = {} for t in [op.type for op in net_def.op]: type_count[t] = type_count.get(t, 0) + 1 type_count_list = sorted(type_count.items(), key=lambda kv: kv[0]) # alphabet type_count_list = sorted(type_count_list, key=lambda kv: -kv[1]) # count return "\n".join("{:>4}x {}".format(count, name) for name, count in type_count_list) def _assign_device_option( predict_net: caffe2_pb2.NetDef, init_net: caffe2_pb2.NetDef, tensor_inputs: List[torch.Tensor] ): """ ONNX exported network doesn't have concept of device, assign necessary device option for each op in order to make it runable on GPU runtime. """ def _get_device_type(torch_tensor): assert torch_tensor.device.type in ["cpu", "cuda"] assert torch_tensor.device.index == 0 return torch_tensor.device.type def _assign_op_device_option(net_proto, net_ssa, blob_device_types): for op, ssa_i in zip(net_proto.op, net_ssa): if op.type in ["CopyCPUToGPU", "CopyGPUToCPU"]: op.device_option.CopyFrom(core.DeviceOption(caffe2_pb2.CUDA, 0)) else: devices = [blob_device_types[b] for b in ssa_i[0] + ssa_i[1]] assert all(d == devices[0] for d in devices) if devices[0] == "cuda": op.device_option.CopyFrom(core.DeviceOption(caffe2_pb2.CUDA, 0)) # update ops in predict_net predict_net_input_device_types = { (name, 0): _get_device_type(tensor) for name, tensor in zip(predict_net.external_input, tensor_inputs) } predict_net_device_types = infer_device_type( predict_net, known_status=predict_net_input_device_types, device_name_style="pytorch" ) predict_net_ssa, _ = core.get_ssa(predict_net) _assign_op_device_option(predict_net, predict_net_ssa, predict_net_device_types) # update ops in init_net init_net_ssa, versions = core.get_ssa(init_net) init_net_output_device_types = { (name, versions[name]): predict_net_device_types[(name, 0)] for name in init_net.external_output } init_net_device_types = infer_device_type( init_net, known_status=init_net_output_device_types, device_name_style="pytorch" ) _assign_op_device_option(init_net, init_net_ssa, init_net_device_types) def export_caffe2_detection_model(model: torch.nn.Module, tensor_inputs: List[torch.Tensor]): """ Export a caffe2-compatible Detectron2 model to caffe2 format via ONNX. Arg: model: a caffe2-compatible version of detectron2 model, defined in caffe2_modeling.py tensor_inputs: a list of tensors that caffe2 model takes as input. """ model = copy.deepcopy(model) assert isinstance(model, torch.nn.Module) assert hasattr(model, "encode_additional_info") # Export via ONNX logger.info( "Exporting a {} model via ONNX ...".format(type(model).__name__) + " Some warnings from ONNX are expected and are usually not to worry about." ) onnx_model = export_onnx_model(model, (tensor_inputs,)) # Convert ONNX model to Caffe2 protobuf init_net, predict_net = Caffe2Backend.onnx_graph_to_caffe2_net(onnx_model) ops_table = [[op.type, op.input, op.output] for op in predict_net.op] table = tabulate(ops_table, headers=["type", "input", "output"], tablefmt="pipe") logger.info( "ONNX export Done. Exported predict_net (before optimizations):\n" + colored(table, "cyan") ) # Apply protobuf optimization fuse_alias_placeholder(predict_net, init_net) if any(t.device.type != "cpu" for t in tensor_inputs): fuse_copy_between_cpu_and_gpu(predict_net) remove_dead_end_ops(init_net) _assign_device_option(predict_net, init_net, tensor_inputs) params, device_options = get_params_from_init_net(init_net) predict_net, params = remove_reshape_for_fc(predict_net, params) init_net = construct_init_net_from_params(params, device_options) group_norm_replace_aten_with_caffe2(predict_net) # Record necessary information for running the pb model in Detectron2 system. model.encode_additional_info(predict_net, init_net) logger.info("Operators used in predict_net: \n{}".format(_op_stats(predict_net))) logger.info("Operators used in init_net: \n{}".format(_op_stats(init_net))) return predict_net, init_net def run_and_save_graph(predict_net, init_net, tensor_inputs, graph_save_path): """ Run the caffe2 model on given inputs, recording the shape and draw the graph. predict_net/init_net: caffe2 model. tensor_inputs: a list of tensors that caffe2 model takes as input. graph_save_path: path for saving graph of exported model. """ logger.info("Saving graph of ONNX exported model to {} ...".format(graph_save_path)) save_graph(predict_net, graph_save_path, op_only=False) # Run the exported Caffe2 net logger.info("Running ONNX exported model ...") with ScopedWS("__ws_tmp__", True) as ws: ws.RunNetOnce(init_net) initialized_blobs = set(ws.Blobs()) uninitialized = [inp for inp in predict_net.external_input if inp not in initialized_blobs] for name, blob in zip(uninitialized, tensor_inputs): ws.FeedBlob(name, blob) try: ws.RunNetOnce(predict_net) except RuntimeError as e: logger.warning("Encountered RuntimeError: \n{}".format(str(e))) ws_blobs = {b: ws.FetchBlob(b) for b in ws.Blobs()} blob_sizes = {b: ws_blobs[b].shape for b in ws_blobs if isinstance(ws_blobs[b], np.ndarray)} logger.info("Saving graph with blob shapes to {} ...".format(graph_save_path)) save_graph(predict_net, graph_save_path, op_only=False, blob_sizes=blob_sizes) return ws_blobs ================================================ FILE: detectron2/detectron2/export/caffe2_inference.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np from itertools import count import torch from caffe2.proto import caffe2_pb2 from caffe2.python import core from .caffe2_modeling import META_ARCH_CAFFE2_EXPORT_TYPE_MAP, convert_batched_inputs_to_c2_format from .shared import ScopedWS, get_pb_arg_vali, get_pb_arg_vals, infer_device_type logger = logging.getLogger(__name__) # ===== ref: mobile-vision predictor's 'Caffe2Wrapper' class ====== class ProtobufModel(torch.nn.Module): """ Wrapper of a caffe2's protobuf model. It works just like nn.Module, but running caffe2 under the hood. Input/Output are tuple[tensor] that match the caffe2 net's external_input/output. """ _ids = count(0) def __init__(self, predict_net, init_net): logger.info(f"Initializing ProtobufModel for: {predict_net.name} ...") super().__init__() assert isinstance(predict_net, caffe2_pb2.NetDef) assert isinstance(init_net, caffe2_pb2.NetDef) # create unique temporary workspace for each instance self.ws_name = "__tmp_ProtobufModel_{}__".format(next(self._ids)) self.net = core.Net(predict_net) logger.info("Running init_net once to fill the parameters ...") with ScopedWS(self.ws_name, is_reset=True, is_cleanup=False) as ws: ws.RunNetOnce(init_net) uninitialized_external_input = [] for blob in self.net.Proto().external_input: if blob not in ws.Blobs(): uninitialized_external_input.append(blob) ws.CreateBlob(blob) ws.CreateNet(self.net) self._error_msgs = set() self._input_blobs = uninitialized_external_input def _infer_output_devices(self, inputs): """ Returns: list[str]: list of device for each external output """ def _get_device_type(torch_tensor): assert torch_tensor.device.type in ["cpu", "cuda"] assert torch_tensor.device.index == 0 return torch_tensor.device.type predict_net = self.net.Proto() input_device_types = { (name, 0): _get_device_type(tensor) for name, tensor in zip(self._input_blobs, inputs) } device_type_map = infer_device_type( predict_net, known_status=input_device_types, device_name_style="pytorch" ) ssa, versions = core.get_ssa(predict_net) versioned_outputs = [(name, versions[name]) for name in predict_net.external_output] output_devices = [device_type_map[outp] for outp in versioned_outputs] return output_devices def forward(self, inputs): """ Args: inputs (tuple[torch.Tensor]) Returns: tuple[torch.Tensor] """ assert len(inputs) == len(self._input_blobs), ( f"Length of inputs ({len(inputs)}) " f"doesn't match the required input blobs: {self._input_blobs}" ) with ScopedWS(self.ws_name, is_reset=False, is_cleanup=False) as ws: for b, tensor in zip(self._input_blobs, inputs): ws.FeedBlob(b, tensor) try: ws.RunNet(self.net.Proto().name) except RuntimeError as e: if not str(e) in self._error_msgs: self._error_msgs.add(str(e)) logger.warning("Encountered new RuntimeError: \n{}".format(str(e))) logger.warning("Catch the error and use partial results.") c2_outputs = [ws.FetchBlob(b) for b in self.net.Proto().external_output] # Remove outputs of current run, this is necessary in order to # prevent fetching the result from previous run if the model fails # in the middle. for b in self.net.Proto().external_output: # Needs to create uninitialized blob to make the net runable. # This is "equivalent" to: ws.RemoveBlob(b) then ws.CreateBlob(b), # but there'no such API. ws.FeedBlob(b, f"{b}, a C++ native class of type nullptr (uninitialized).") # Cast output to torch.Tensor on the desired device output_devices = ( self._infer_output_devices(inputs) if any(t.device.type != "cpu" for t in inputs) else ["cpu" for _ in self.net.Proto().external_output] ) outputs = [] for name, c2_output, device in zip( self.net.Proto().external_output, c2_outputs, output_devices ): if not isinstance(c2_output, np.ndarray): raise RuntimeError( "Invalid output for blob {}, received: {}".format(name, c2_output) ) outputs.append(torch.tensor(c2_output).to(device=device)) return tuple(outputs) class ProtobufDetectionModel(torch.nn.Module): """ A class works just like a pytorch meta arch in terms of inference, but running caffe2 model under the hood. """ def __init__(self, predict_net, init_net, *, convert_outputs=None): """ Args: predict_net, init_net (core.Net): caffe2 nets convert_outptus (callable): a function that converts caffe2 outputs to the same format of the original pytorch model. By default, use the one defined in the caffe2 meta_arch. """ super().__init__() self.protobuf_model = ProtobufModel(predict_net, init_net) self.size_divisibility = get_pb_arg_vali(predict_net, "size_divisibility", 0) self.device = get_pb_arg_vals(predict_net, "device", b"cpu").decode("ascii") if convert_outputs is None: meta_arch = get_pb_arg_vals(predict_net, "meta_architecture", b"GeneralizedRCNN") meta_arch = META_ARCH_CAFFE2_EXPORT_TYPE_MAP[meta_arch.decode("ascii")] self._convert_outputs = meta_arch.get_outputs_converter(predict_net, init_net) else: self._convert_outputs = convert_outputs def _convert_inputs(self, batched_inputs): # currently all models convert inputs in the same way return convert_batched_inputs_to_c2_format( batched_inputs, self.size_divisibility, self.device ) def forward(self, batched_inputs): c2_inputs = self._convert_inputs(batched_inputs) c2_results = self.protobuf_model(c2_inputs) c2_results = dict(zip(self.protobuf_model.net.Proto().external_output, c2_results)) return self._convert_outputs(batched_inputs, c2_inputs, c2_results) ================================================ FILE: detectron2/detectron2/export/caffe2_modeling.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import functools import io import struct import types import torch from detectron2.modeling import meta_arch from detectron2.modeling.box_regression import Box2BoxTransform from detectron2.modeling.roi_heads import keypoint_head from detectron2.structures import Boxes, ImageList, Instances, RotatedBoxes from .c10 import Caffe2Compatible from .caffe2_patch import ROIHeadsPatcher, patch_generalized_rcnn from .shared import ( alias, check_set_pb_arg, get_pb_arg_floats, get_pb_arg_valf, get_pb_arg_vali, get_pb_arg_vals, mock_torch_nn_functional_interpolate, ) def assemble_rcnn_outputs_by_name(image_sizes, tensor_outputs, force_mask_on=False): """ A function to assemble caffe2 model's outputs (i.e. Dict[str, Tensor]) to detectron2's format (i.e. list of Instances instance). This only works when the model follows the Caffe2 detectron's naming convention. Args: image_sizes (List[List[int, int]]): [H, W] of every image. tensor_outputs (Dict[str, Tensor]): external_output to its tensor. force_mask_on (Bool): if true, the it make sure there'll be pred_masks even if the mask is not found from tensor_outputs (usually due to model crash) """ results = [Instances(image_size) for image_size in image_sizes] batch_splits = tensor_outputs.get("batch_splits", None) if batch_splits: raise NotImplementedError() assert len(image_sizes) == 1 result = results[0] bbox_nms = tensor_outputs["bbox_nms"] score_nms = tensor_outputs["score_nms"] class_nms = tensor_outputs["class_nms"] # Detection will always success because Conv support 0-batch assert bbox_nms is not None assert score_nms is not None assert class_nms is not None if bbox_nms.shape[1] == 5: result.pred_boxes = RotatedBoxes(bbox_nms) else: result.pred_boxes = Boxes(bbox_nms) result.scores = score_nms result.pred_classes = class_nms.to(torch.int64) mask_fcn_probs = tensor_outputs.get("mask_fcn_probs", None) if mask_fcn_probs is not None: # finish the mask pred mask_probs_pred = mask_fcn_probs num_masks = mask_probs_pred.shape[0] class_pred = result.pred_classes indices = torch.arange(num_masks, device=class_pred.device) mask_probs_pred = mask_probs_pred[indices, class_pred][:, None] result.pred_masks = mask_probs_pred elif force_mask_on: # NOTE: there's no way to know the height/width of mask here, it won't be # used anyway when batch size is 0, so just set them to 0. result.pred_masks = torch.zeros([0, 1, 0, 0], dtype=torch.uint8) keypoints_out = tensor_outputs.get("keypoints_out", None) kps_score = tensor_outputs.get("kps_score", None) if keypoints_out is not None: # keypoints_out: [N, 4, #kypoints], where 4 is in order of (x, y, score, prob) keypoints_tensor = keypoints_out # NOTE: it's possible that prob is not calculated if "should_output_softmax" # is set to False in HeatmapMaxKeypoint, so just using raw score, seems # it doesn't affect mAP. TODO: check more carefully. keypoint_xyp = keypoints_tensor.transpose(1, 2)[:, :, [0, 1, 2]] result.pred_keypoints = keypoint_xyp elif kps_score is not None: # keypoint heatmap to sparse data structure pred_keypoint_logits = kps_score keypoint_head.keypoint_rcnn_inference(pred_keypoint_logits, [result]) return results def _cast_to_f32(f64): return struct.unpack("f", struct.pack("f", f64))[0] def set_caffe2_compatible_tensor_mode(model, enable=True): def _fn(m): if isinstance(m, Caffe2Compatible): m.tensor_mode = enable model.apply(_fn) def convert_batched_inputs_to_c2_format(batched_inputs, size_divisibility, device): """ See get_caffe2_inputs() below. """ assert all(isinstance(x, dict) for x in batched_inputs) assert all(x["image"].dim() == 3 for x in batched_inputs) images = [x["image"] for x in batched_inputs] images = ImageList.from_tensors(images, size_divisibility) im_info = [] for input_per_image, image_size in zip(batched_inputs, images.image_sizes): target_height = input_per_image.get("height", image_size[0]) target_width = input_per_image.get("width", image_size[1]) # noqa # NOTE: The scale inside im_info is kept as convention and for providing # post-processing information if further processing is needed. For # current Caffe2 model definitions that don't include post-processing inside # the model, this number is not used. # NOTE: There can be a slight difference between width and height # scales, using a single number can results in numerical difference # compared with D2's post-processing. scale = target_height / image_size[0] im_info.append([image_size[0], image_size[1], scale]) im_info = torch.Tensor(im_info) return images.tensor.to(device), im_info.to(device) class Caffe2MetaArch(Caffe2Compatible, torch.nn.Module): """ Base class for caffe2-compatible implementation of a meta architecture. The forward is traceable and its traced graph can be converted to caffe2 graph through ONNX. """ def __init__(self, cfg, torch_model): """ Args: cfg (CfgNode): torch_model (nn.Module): the detectron2 model (meta_arch) to be converted. """ super().__init__() self._wrapped_model = torch_model self.eval() set_caffe2_compatible_tensor_mode(self, True) def get_caffe2_inputs(self, batched_inputs): """ Convert pytorch-style structured inputs to caffe2-style inputs that are tuples of tensors. Args: batched_inputs (list[dict]): inputs to a detectron2 model in its standard format. Each dict has "image" (CHW tensor), and optionally "height" and "width". Returns: tuple[Tensor]: tuple of tensors that will be the inputs to the :meth:`forward` method. For existing models, the first is an NCHW tensor (padded and batched); the second is a im_info Nx3 tensor, where the rows are (height, width, unused legacy parameter) """ return convert_batched_inputs_to_c2_format( batched_inputs, self._wrapped_model.backbone.size_divisibility, self._wrapped_model.device, ) def encode_additional_info(self, predict_net, init_net): """ Save extra metadata that will be used by inference in the output protobuf. """ pass def forward(self, inputs): """ Run the forward in caffe2-style. It has to use caffe2-compatible ops and the method will be used for tracing. Args: inputs (tuple[Tensor]): inputs defined by :meth:`get_caffe2_input`. They will be the inputs of the converted caffe2 graph. Returns: tuple[Tensor]: output tensors. They will be the outputs of the converted caffe2 graph. """ raise NotImplementedError def _caffe2_preprocess_image(self, inputs): """ Caffe2 implementation of preprocess_image, which is called inside each MetaArch's forward. It normalizes the input images, and the final caffe2 graph assumes the inputs have been batched already. """ data, im_info = inputs data = alias(data, "data") im_info = alias(im_info, "im_info") mean, std = self._wrapped_model.pixel_mean, self._wrapped_model.pixel_std normalized_data = (data - mean) / std normalized_data = alias(normalized_data, "normalized_data") # Pack (data, im_info) into ImageList which is recognized by self.inference. images = ImageList(tensor=normalized_data, image_sizes=im_info) return images @staticmethod def get_outputs_converter(predict_net, init_net): """ Creates a function that converts outputs of the caffe2 model to detectron2's standard format. The function uses information in `predict_net` and `init_net` that are available at inferene time. Therefore the function logic can be used in inference. The returned function has the following signature: def convert(batched_inputs, c2_inputs, c2_results) -> detectron2_outputs Where * batched_inputs (list[dict]): the original input format of the meta arch * c2_inputs (tuple[Tensor]): the caffe2 inputs. * c2_results (dict[str, Tensor]): the caffe2 output format, corresponding to the outputs of the :meth:`forward` function. * detectron2_outputs: the original output format of the meta arch. This function can be used to compare the outputs of the original meta arch and the converted caffe2 graph. Returns: callable: a callable of the above signature. """ raise NotImplementedError class Caffe2GeneralizedRCNN(Caffe2MetaArch): def __init__(self, cfg, torch_model): assert isinstance(torch_model, meta_arch.GeneralizedRCNN) torch_model = patch_generalized_rcnn(torch_model) super().__init__(cfg, torch_model) try: use_heatmap_max_keypoint = cfg.EXPORT_CAFFE2.USE_HEATMAP_MAX_KEYPOINT except AttributeError: use_heatmap_max_keypoint = False self.roi_heads_patcher = ROIHeadsPatcher( self._wrapped_model.roi_heads, use_heatmap_max_keypoint ) def encode_additional_info(self, predict_net, init_net): size_divisibility = self._wrapped_model.backbone.size_divisibility check_set_pb_arg(predict_net, "size_divisibility", "i", size_divisibility) check_set_pb_arg( predict_net, "device", "s", str.encode(str(self._wrapped_model.device), "ascii") ) check_set_pb_arg(predict_net, "meta_architecture", "s", b"GeneralizedRCNN") @mock_torch_nn_functional_interpolate() def forward(self, inputs): if not self.tensor_mode: return self._wrapped_model.inference(inputs) images = self._caffe2_preprocess_image(inputs) features = self._wrapped_model.backbone(images.tensor) proposals, _ = self._wrapped_model.proposal_generator(images, features) with self.roi_heads_patcher.mock_roi_heads(): detector_results, _ = self._wrapped_model.roi_heads(images, features, proposals) return tuple(detector_results[0].flatten()) @staticmethod def get_outputs_converter(predict_net, init_net): def f(batched_inputs, c2_inputs, c2_results): _, im_info = c2_inputs image_sizes = [[int(im[0]), int(im[1])] for im in im_info] results = assemble_rcnn_outputs_by_name(image_sizes, c2_results) return meta_arch.GeneralizedRCNN._postprocess(results, batched_inputs, image_sizes) return f class Caffe2RetinaNet(Caffe2MetaArch): def __init__(self, cfg, torch_model): assert isinstance(torch_model, meta_arch.RetinaNet) super().__init__(cfg, torch_model) @mock_torch_nn_functional_interpolate() def forward(self, inputs): assert self.tensor_mode images = self._caffe2_preprocess_image(inputs) # explicitly return the images sizes to avoid removing "im_info" by ONNX # since it's not used in the forward path return_tensors = [images.image_sizes] features = self._wrapped_model.backbone(images.tensor) features = [features[f] for f in self._wrapped_model.head_in_features] for i, feature_i in enumerate(features): features[i] = alias(feature_i, "feature_{}".format(i), is_backward=True) return_tensors.append(features[i]) pred_logits, pred_anchor_deltas = self._wrapped_model.head(features) for i, (box_cls_i, box_delta_i) in enumerate(zip(pred_logits, pred_anchor_deltas)): return_tensors.append(alias(box_cls_i, "box_cls_{}".format(i))) return_tensors.append(alias(box_delta_i, "box_delta_{}".format(i))) return tuple(return_tensors) def encode_additional_info(self, predict_net, init_net): size_divisibility = self._wrapped_model.backbone.size_divisibility check_set_pb_arg(predict_net, "size_divisibility", "i", size_divisibility) check_set_pb_arg( predict_net, "device", "s", str.encode(str(self._wrapped_model.device), "ascii") ) check_set_pb_arg(predict_net, "meta_architecture", "s", b"RetinaNet") # Inference parameters: check_set_pb_arg( predict_net, "score_threshold", "f", _cast_to_f32(self._wrapped_model.test_score_thresh) ) check_set_pb_arg( predict_net, "topk_candidates", "i", self._wrapped_model.test_topk_candidates ) check_set_pb_arg( predict_net, "nms_threshold", "f", _cast_to_f32(self._wrapped_model.test_nms_thresh) ) check_set_pb_arg( predict_net, "max_detections_per_image", "i", self._wrapped_model.max_detections_per_image, ) check_set_pb_arg( predict_net, "bbox_reg_weights", "floats", [_cast_to_f32(w) for w in self._wrapped_model.box2box_transform.weights], ) self._encode_anchor_generator_cfg(predict_net) def _encode_anchor_generator_cfg(self, predict_net): # serialize anchor_generator for future use serialized_anchor_generator = io.BytesIO() torch.save(self._wrapped_model.anchor_generator, serialized_anchor_generator) # Ideally we can put anchor generating inside the model, then we don't # need to store this information. bytes = serialized_anchor_generator.getvalue() check_set_pb_arg(predict_net, "serialized_anchor_generator", "s", bytes) @staticmethod def get_outputs_converter(predict_net, init_net): self = types.SimpleNamespace() serialized_anchor_generator = io.BytesIO( get_pb_arg_vals(predict_net, "serialized_anchor_generator", None) ) self.anchor_generator = torch.load(serialized_anchor_generator) bbox_reg_weights = get_pb_arg_floats(predict_net, "bbox_reg_weights", None) self.box2box_transform = Box2BoxTransform(weights=tuple(bbox_reg_weights)) self.test_score_thresh = get_pb_arg_valf(predict_net, "score_threshold", None) self.test_topk_candidates = get_pb_arg_vali(predict_net, "topk_candidates", None) self.test_nms_thresh = get_pb_arg_valf(predict_net, "nms_threshold", None) self.max_detections_per_image = get_pb_arg_vali( predict_net, "max_detections_per_image", None ) # hack to reuse inference code from RetinaNet for meth in [ "forward_inference", "inference_single_image", "_transpose_dense_predictions", "_decode_multi_level_predictions", "_decode_per_level_predictions", ]: setattr(self, meth, functools.partial(getattr(meta_arch.RetinaNet, meth), self)) def f(batched_inputs, c2_inputs, c2_results): _, im_info = c2_inputs image_sizes = [[int(im[0]), int(im[1])] for im in im_info] dummy_images = ImageList( torch.randn( ( len(im_info), 3, ) + tuple(image_sizes[0]) ), image_sizes, ) num_features = len([x for x in c2_results.keys() if x.startswith("box_cls_")]) pred_logits = [c2_results["box_cls_{}".format(i)] for i in range(num_features)] pred_anchor_deltas = [c2_results["box_delta_{}".format(i)] for i in range(num_features)] # For each feature level, feature should have the same batch size and # spatial dimension as the box_cls and box_delta. dummy_features = [x.clone()[:, 0:0, :, :] for x in pred_logits] # self.num_classess can be inferred self.num_classes = pred_logits[0].shape[1] // (pred_anchor_deltas[0].shape[1] // 4) results = self.forward_inference( dummy_images, dummy_features, [pred_logits, pred_anchor_deltas] ) return meta_arch.GeneralizedRCNN._postprocess(results, batched_inputs, image_sizes) return f META_ARCH_CAFFE2_EXPORT_TYPE_MAP = { "GeneralizedRCNN": Caffe2GeneralizedRCNN, "RetinaNet": Caffe2RetinaNet, } ================================================ FILE: detectron2/detectron2/export/caffe2_patch.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import contextlib from unittest import mock import torch from detectron2.modeling import poolers from detectron2.modeling.proposal_generator import rpn from detectron2.modeling.roi_heads import keypoint_head, mask_head from detectron2.modeling.roi_heads.fast_rcnn import FastRCNNOutputLayers from .c10 import ( Caffe2Compatible, Caffe2FastRCNNOutputsInference, Caffe2KeypointRCNNInference, Caffe2MaskRCNNInference, Caffe2ROIPooler, Caffe2RPN, ) class GenericMixin(object): pass class Caffe2CompatibleConverter(object): """ A GenericUpdater which implements the `create_from` interface, by modifying module object and assign it with another class replaceCls. """ def __init__(self, replaceCls): self.replaceCls = replaceCls def create_from(self, module): # update module's class to the new class assert isinstance(module, torch.nn.Module) if issubclass(self.replaceCls, GenericMixin): # replaceCls should act as mixin, create a new class on-the-fly new_class = type( "{}MixedWith{}".format(self.replaceCls.__name__, module.__class__.__name__), (self.replaceCls, module.__class__), {}, # {"new_method": lambda self: ...}, ) module.__class__ = new_class else: # replaceCls is complete class, this allow arbitrary class swap module.__class__ = self.replaceCls # initialize Caffe2Compatible if isinstance(module, Caffe2Compatible): module.tensor_mode = False return module def patch(model, target, updater, *args, **kwargs): """ recursively (post-order) update all modules with the target type and its subclasses, make a initialization/composition/inheritance/... via the updater.create_from. """ for name, module in model.named_children(): model._modules[name] = patch(module, target, updater, *args, **kwargs) if isinstance(model, target): return updater.create_from(model, *args, **kwargs) return model def patch_generalized_rcnn(model): ccc = Caffe2CompatibleConverter model = patch(model, rpn.RPN, ccc(Caffe2RPN)) model = patch(model, poolers.ROIPooler, ccc(Caffe2ROIPooler)) return model @contextlib.contextmanager def mock_fastrcnn_outputs_inference( tensor_mode, check=True, box_predictor_type=FastRCNNOutputLayers ): with mock.patch.object( box_predictor_type, "inference", autospec=True, side_effect=Caffe2FastRCNNOutputsInference(tensor_mode), ) as mocked_func: yield if check: assert mocked_func.call_count > 0 @contextlib.contextmanager def mock_mask_rcnn_inference(tensor_mode, patched_module, check=True): with mock.patch( "{}.mask_rcnn_inference".format(patched_module), side_effect=Caffe2MaskRCNNInference() ) as mocked_func: yield if check: assert mocked_func.call_count > 0 @contextlib.contextmanager def mock_keypoint_rcnn_inference(tensor_mode, patched_module, use_heatmap_max_keypoint, check=True): with mock.patch( "{}.keypoint_rcnn_inference".format(patched_module), side_effect=Caffe2KeypointRCNNInference(use_heatmap_max_keypoint), ) as mocked_func: yield if check: assert mocked_func.call_count > 0 class ROIHeadsPatcher: def __init__(self, heads, use_heatmap_max_keypoint): self.heads = heads self.use_heatmap_max_keypoint = use_heatmap_max_keypoint @contextlib.contextmanager def mock_roi_heads(self, tensor_mode=True): """ Patching several inference functions inside ROIHeads and its subclasses Args: tensor_mode (bool): whether the inputs/outputs are caffe2's tensor format or not. Default to True. """ # NOTE: this requries the `keypoint_rcnn_inference` and `mask_rcnn_inference` # are called inside the same file as BaseXxxHead due to using mock.patch. kpt_heads_mod = keypoint_head.BaseKeypointRCNNHead.__module__ mask_head_mod = mask_head.BaseMaskRCNNHead.__module__ mock_ctx_managers = [ mock_fastrcnn_outputs_inference( tensor_mode=tensor_mode, check=True, box_predictor_type=type(self.heads.box_predictor), ) ] if getattr(self.heads, "keypoint_on", False): mock_ctx_managers += [ mock_keypoint_rcnn_inference( tensor_mode, kpt_heads_mod, self.use_heatmap_max_keypoint ) ] if getattr(self.heads, "mask_on", False): mock_ctx_managers += [mock_mask_rcnn_inference(tensor_mode, mask_head_mod)] with contextlib.ExitStack() as stack: # python 3.3+ for mgr in mock_ctx_managers: stack.enter_context(mgr) yield ================================================ FILE: detectron2/detectron2/export/flatten.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import collections from dataclasses import dataclass from typing import Callable, List, Optional, Tuple import torch from torch import nn from detectron2.structures import Boxes, Instances, ROIMasks from detectron2.utils.registry import _convert_target_to_string, locate from .torchscript_patch import patch_builtin_len @dataclass class Schema: """ A Schema defines how to flatten a possibly hierarchical object into tuple of primitive objects, so it can be used as inputs/outputs of PyTorch's tracing. PyTorch does not support tracing a function that produces rich output structures (e.g. dict, Instances, Boxes). To trace such a function, we flatten the rich object into tuple of tensors, and return this tuple of tensors instead. Meanwhile, we also need to know how to "rebuild" the original object from the flattened results, so we can evaluate the flattened results. A Schema defines how to flatten an object, and while flattening it, it records necessary schemas so that the object can be rebuilt using the flattened outputs. The flattened object and the schema object is returned by ``.flatten`` classmethod. Then the original object can be rebuilt with the ``__call__`` method of schema. A Schema is a dataclass that can be serialized easily. """ # inspired by FetchMapper in tensorflow/python/client/session.py @classmethod def flatten(cls, obj): raise NotImplementedError def __call__(self, values): raise NotImplementedError @staticmethod def _concat(values): ret = () sizes = [] for v in values: assert isinstance(v, tuple), "Flattened results must be a tuple" ret = ret + v sizes.append(len(v)) return ret, sizes @staticmethod def _split(values, sizes): if len(sizes): expected_len = sum(sizes) assert ( len(values) == expected_len ), f"Values has length {len(values)} but expect length {expected_len}." ret = [] for k in range(len(sizes)): begin, end = sum(sizes[:k]), sum(sizes[: k + 1]) ret.append(values[begin:end]) return ret @dataclass class ListSchema(Schema): schemas: List[Schema] # the schemas that define how to flatten each element in the list sizes: List[int] # the flattened length of each element def __call__(self, values): values = self._split(values, self.sizes) if len(values) != len(self.schemas): raise ValueError( f"Values has length {len(values)} but schemas " f"has length {len(self.schemas)}!" ) values = [m(v) for m, v in zip(self.schemas, values)] return list(values) @classmethod def flatten(cls, obj): res = [flatten_to_tuple(k) for k in obj] values, sizes = cls._concat([k[0] for k in res]) return values, cls([k[1] for k in res], sizes) @dataclass class TupleSchema(ListSchema): def __call__(self, values): return tuple(super().__call__(values)) @dataclass class IdentitySchema(Schema): def __call__(self, values): return values[0] @classmethod def flatten(cls, obj): return (obj,), cls() @dataclass class DictSchema(ListSchema): keys: List[str] def __call__(self, values): values = super().__call__(values) return dict(zip(self.keys, values)) @classmethod def flatten(cls, obj): for k in obj.keys(): if not isinstance(k, str): raise KeyError("Only support flattening dictionaries if keys are str.") keys = sorted(obj.keys()) values = [obj[k] for k in keys] ret, schema = ListSchema.flatten(values) return ret, cls(schema.schemas, schema.sizes, keys) @dataclass class InstancesSchema(DictSchema): def __call__(self, values): image_size, fields = values[-1], values[:-1] fields = super().__call__(fields) return Instances(image_size, **fields) @classmethod def flatten(cls, obj): ret, schema = super().flatten(obj.get_fields()) size = obj.image_size if not isinstance(size, torch.Tensor): size = torch.tensor(size) return ret + (size,), schema @dataclass class TensorWrapSchema(Schema): """ For classes that are simple wrapper of tensors, e.g. Boxes, RotatedBoxes, BitMasks """ class_name: str def __call__(self, values): return locate(self.class_name)(values[0]) @classmethod def flatten(cls, obj): return (obj.tensor,), cls(_convert_target_to_string(type(obj))) # if more custom structures needed in the future, can allow # passing in extra schemas for custom types def flatten_to_tuple(obj): """ Flatten an object so it can be used for PyTorch tracing. Also returns how to rebuild the original object from the flattened outputs. Returns: res (tuple): the flattened results that can be used as tracing outputs schema: an object with a ``__call__`` method such that ``schema(res) == obj``. It is a pure dataclass that can be serialized. """ schemas = [ ((str, bytes), IdentitySchema), (list, ListSchema), (tuple, TupleSchema), (collections.abc.Mapping, DictSchema), (Instances, InstancesSchema), ((Boxes, ROIMasks), TensorWrapSchema), ] for klass, schema in schemas: if isinstance(obj, klass): F = schema break else: F = IdentitySchema return F.flatten(obj) class TracingAdapter(nn.Module): """ A model may take rich input/output format (e.g. dict or custom classes), but `torch.jit.trace` requires tuple of tensors as input/output. This adapter flattens input/output format of a model so it becomes traceable. It also records the necessary schema to rebuild model's inputs/outputs from flattened inputs/outputs. Example: :: outputs = model(inputs) # inputs/outputs may be rich structure adapter = TracingAdapter(model, inputs) # can now trace the model, with adapter.flattened_inputs, or another # tuple of tensors with the same length and meaning traced = torch.jit.trace(adapter, adapter.flattened_inputs) # traced model can only produce flattened outputs (tuple of tensors) flattened_outputs = traced(*adapter.flattened_inputs) # adapter knows the schema to convert it back (new_outputs == outputs) new_outputs = adapter.outputs_schema(flattened_outputs) """ flattened_inputs: Tuple[torch.Tensor] = None """ Flattened version of inputs given to this class's constructor. """ inputs_schema: Schema = None """ Schema of the inputs given to this class's constructor. """ outputs_schema: Schema = None """ Schema of the output produced by calling the given model with inputs. """ def __init__( self, model: nn.Module, inputs, inference_func: Optional[Callable] = None, allow_non_tensor: bool = False, ): """ Args: model: an nn.Module inputs: An input argument or a tuple of input arguments used to call model. After flattening, it has to only consist of tensors. inference_func: a callable that takes (model, *inputs), calls the model with inputs, and return outputs. By default it is ``lambda model, *inputs: model(*inputs)``. Can be override if you need to call the model differently. allow_non_tensor: allow inputs/outputs to contain non-tensor objects. This option will filter out non-tensor objects to make the model traceable, but ``inputs_schema``/``outputs_schema`` cannot be used anymore because inputs/outputs cannot be rebuilt from pure tensors. This is useful when you're only interested in the single trace of execution (e.g. for flop count), but not interested in generalizing the traced graph to new inputs. """ super().__init__() if isinstance(model, (nn.parallel.distributed.DistributedDataParallel, nn.DataParallel)): model = model.module self.model = model if not isinstance(inputs, tuple): inputs = (inputs,) self.inputs = inputs self.allow_non_tensor = allow_non_tensor if inference_func is None: inference_func = lambda model, *inputs: model(*inputs) # noqa self.inference_func = inference_func self.flattened_inputs, self.inputs_schema = flatten_to_tuple(inputs) if all(isinstance(x, torch.Tensor) for x in self.flattened_inputs): return if self.allow_non_tensor: self.flattened_inputs = tuple( [x for x in self.flattened_inputs if isinstance(x, torch.Tensor)] ) self.inputs_schema = None else: for input in self.flattened_inputs: if not isinstance(input, torch.Tensor): raise ValueError( "Inputs for tracing must only contain tensors. " f"Got a {type(input)} instead." ) def forward(self, *args: torch.Tensor): with torch.no_grad(), patch_builtin_len(): if self.inputs_schema is not None: inputs_orig_format = self.inputs_schema(args) else: if len(args) != len(self.flattened_inputs) or any( x is not y for x, y in zip(args, self.flattened_inputs) ): raise ValueError( "TracingAdapter does not contain valid inputs_schema." " So it cannot generalize to other inputs and must be" " traced with `.flattened_inputs`." ) inputs_orig_format = self.inputs outputs = self.inference_func(self.model, *inputs_orig_format) flattened_outputs, schema = flatten_to_tuple(outputs) flattened_output_tensors = tuple( [x for x in flattened_outputs if isinstance(x, torch.Tensor)] ) if len(flattened_output_tensors) < len(flattened_outputs): if self.allow_non_tensor: flattened_outputs = flattened_output_tensors self.outputs_schema = None else: raise ValueError( "Model cannot be traced because some model outputs " "cannot flatten to tensors." ) else: # schema is valid if self.outputs_schema is None: self.outputs_schema = schema else: assert self.outputs_schema == schema, ( "Model should always return outputs with the same " "structure so it can be traced!" ) return flattened_outputs def _create_wrapper(self, traced_model): """ Return a function that has an input/output interface the same as the original model, but it calls the given traced model under the hood. """ def forward(*args): flattened_inputs, _ = flatten_to_tuple(args) flattened_outputs = traced_model(*flattened_inputs) return self.outputs_schema(flattened_outputs) return forward ================================================ FILE: detectron2/detectron2/export/shared.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import collections import contextlib import copy import functools import logging import numpy as np import os from typing import Any, Callable, Dict, List, Optional, Tuple, Union from unittest import mock import caffe2.python.utils as putils import torch import torch.nn.functional as F from caffe2.proto import caffe2_pb2 from caffe2.python import core, net_drawer, workspace from torch.nn.functional import interpolate as interp logger = logging.getLogger(__name__) # ==== torch/utils_toffee/cast.py ======================================= def to_device(t, device_str): """ This function is a replacement of .to(another_device) such that it allows the casting to be traced properly by explicitly calling the underlying copy ops. It also avoids introducing unncessary op when casting to the same device. """ src = t.device dst = torch.device(device_str) if src == dst: return t elif src.type == "cuda" and dst.type == "cpu": return torch.ops._caffe2.CopyGPUToCPU(t) elif src.type == "cpu" and dst.type == "cuda": return torch.ops._caffe2.CopyCPUToGPU(t) else: raise RuntimeError("Can't cast tensor from device {} to device {}".format(src, dst)) # ==== torch/utils_toffee/interpolate.py ======================================= # Note: borrowed from vision/detection/fair/detectron/detectron/modeling/detector.py def BilinearInterpolation(tensor_in, up_scale): assert up_scale % 2 == 0, "Scale should be even" def upsample_filt(size): factor = (size + 1) // 2 if size % 2 == 1: center = factor - 1 else: center = factor - 0.5 og = np.ogrid[:size, :size] return (1 - abs(og[0] - center) / factor) * (1 - abs(og[1] - center) / factor) kernel_size = int(up_scale) * 2 bil_filt = upsample_filt(kernel_size) dim = int(tensor_in.shape[1]) kernel = np.zeros((dim, dim, kernel_size, kernel_size), dtype=np.float32) kernel[range(dim), range(dim), :, :] = bil_filt tensor_out = F.conv_transpose2d( tensor_in, weight=to_device(torch.Tensor(kernel), tensor_in.device), bias=None, stride=int(up_scale), padding=int(up_scale / 2), ) return tensor_out # NOTE: ONNX is incompatible with traced torch.nn.functional.interpolate if # using dynamic `scale_factor` rather than static `size`. (T43166860) # NOTE: Caffe2 Int8 conversion might not be able to quantize `size` properly. def onnx_compatibale_interpolate( input, size=None, scale_factor=None, mode="nearest", align_corners=None ): # NOTE: The input dimensions are interpreted in the form: # `mini-batch x channels x [optional depth] x [optional height] x width`. if size is None and scale_factor is not None: if input.dim() == 4: if isinstance(scale_factor, (int, float)): height_scale, width_scale = (scale_factor, scale_factor) else: assert isinstance(scale_factor, (tuple, list)) assert len(scale_factor) == 2 height_scale, width_scale = scale_factor assert not align_corners, "No matching C2 op for align_corners == True" if mode == "nearest": return torch.ops._caffe2.ResizeNearest( input, order="NCHW", width_scale=width_scale, height_scale=height_scale ) elif mode == "bilinear": logger.warning( "Use F.conv_transpose2d for bilinear interpolate" " because there's no such C2 op, this may cause significant" " slowdown and the boundary pixels won't be as same as" " using F.interpolate due to padding." ) assert height_scale == width_scale return BilinearInterpolation(input, up_scale=height_scale) logger.warning("Output size is not static, it might cause ONNX conversion issue") return interp(input, size, scale_factor, mode, align_corners) @contextlib.contextmanager def mock_torch_nn_functional_interpolate(): if torch.onnx.is_in_onnx_export(): with mock.patch( "torch.nn.functional.interpolate", side_effect=onnx_compatibale_interpolate ): yield else: yield # ==== torch/utils_caffe2/ws_utils.py ========================================== class ScopedWS(object): def __init__(self, ws_name, is_reset, is_cleanup=False): self.ws_name = ws_name self.is_reset = is_reset self.is_cleanup = is_cleanup self.org_ws = "" def __enter__(self): self.org_ws = workspace.CurrentWorkspace() if self.ws_name is not None: workspace.SwitchWorkspace(self.ws_name, True) if self.is_reset: workspace.ResetWorkspace() return workspace def __exit__(self, *args): if self.is_cleanup: workspace.ResetWorkspace() if self.ws_name is not None: workspace.SwitchWorkspace(self.org_ws) def fetch_any_blob(name): bb = None try: bb = workspace.FetchBlob(name) except TypeError: bb = workspace.FetchInt8Blob(name) except Exception as e: logger.error("Get blob {} error: {}".format(name, e)) return bb # ==== torch/utils_caffe2/protobuf.py ========================================== def get_pb_arg(pb, arg_name): for x in pb.arg: if x.name == arg_name: return x return None def get_pb_arg_valf(pb, arg_name, default_val): arg = get_pb_arg(pb, arg_name) return arg.f if arg is not None else default_val def get_pb_arg_floats(pb, arg_name, default_val): arg = get_pb_arg(pb, arg_name) return list(map(float, arg.floats)) if arg is not None else default_val def get_pb_arg_ints(pb, arg_name, default_val): arg = get_pb_arg(pb, arg_name) return list(map(int, arg.ints)) if arg is not None else default_val def get_pb_arg_vali(pb, arg_name, default_val): arg = get_pb_arg(pb, arg_name) return arg.i if arg is not None else default_val def get_pb_arg_vals(pb, arg_name, default_val): arg = get_pb_arg(pb, arg_name) return arg.s if arg is not None else default_val def get_pb_arg_valstrings(pb, arg_name, default_val): arg = get_pb_arg(pb, arg_name) return list(arg.strings) if arg is not None else default_val def check_set_pb_arg(pb, arg_name, arg_attr, arg_value, allow_override=False): arg = get_pb_arg(pb, arg_name) if arg is None: arg = putils.MakeArgument(arg_name, arg_value) assert hasattr(arg, arg_attr) pb.arg.extend([arg]) if allow_override and getattr(arg, arg_attr) != arg_value: logger.warning( "Override argument {}: {} -> {}".format(arg_name, getattr(arg, arg_attr), arg_value) ) setattr(arg, arg_attr, arg_value) else: assert arg is not None assert getattr(arg, arg_attr) == arg_value, "Existing value {}, new value {}".format( getattr(arg, arg_attr), arg_value ) def _create_const_fill_op_from_numpy(name, tensor, device_option=None): assert type(tensor) == np.ndarray kTypeNameMapper = { np.dtype("float32"): "GivenTensorFill", np.dtype("int32"): "GivenTensorIntFill", np.dtype("int64"): "GivenTensorInt64Fill", np.dtype("uint8"): "GivenTensorStringFill", } args_dict = {} if tensor.dtype == np.dtype("uint8"): args_dict.update({"values": [str(tensor.data)], "shape": [1]}) else: args_dict.update({"values": tensor, "shape": tensor.shape}) if device_option is not None: args_dict["device_option"] = device_option return core.CreateOperator(kTypeNameMapper[tensor.dtype], [], [name], **args_dict) def _create_const_fill_op_from_c2_int8_tensor(name, int8_tensor): assert type(int8_tensor) == workspace.Int8Tensor kTypeNameMapper = { np.dtype("int32"): "Int8GivenIntTensorFill", np.dtype("uint8"): "Int8GivenTensorFill", } tensor = int8_tensor.data assert tensor.dtype in [np.dtype("uint8"), np.dtype("int32")] values = tensor.tobytes() if tensor.dtype == np.dtype("uint8") else tensor return core.CreateOperator( kTypeNameMapper[tensor.dtype], [], [name], values=values, shape=tensor.shape, Y_scale=int8_tensor.scale, Y_zero_point=int8_tensor.zero_point, ) def create_const_fill_op( name: str, blob: Union[np.ndarray, workspace.Int8Tensor], device_option: Optional[caffe2_pb2.DeviceOption] = None, ) -> caffe2_pb2.OperatorDef: """ Given a blob object, return the Caffe2 operator that creates this blob as constant. Currently support NumPy tensor and Caffe2 Int8Tensor. """ tensor_type = type(blob) assert tensor_type in [ np.ndarray, workspace.Int8Tensor, ], 'Error when creating const fill op for "{}", unsupported blob type: {}'.format( name, type(blob) ) if tensor_type == np.ndarray: return _create_const_fill_op_from_numpy(name, blob, device_option) elif tensor_type == workspace.Int8Tensor: assert device_option is None return _create_const_fill_op_from_c2_int8_tensor(name, blob) def construct_init_net_from_params( params: Dict[str, Any], device_options: Optional[Dict[str, caffe2_pb2.DeviceOption]] = None ) -> caffe2_pb2.NetDef: """ Construct the init_net from params dictionary """ init_net = caffe2_pb2.NetDef() device_options = device_options or {} for name, blob in params.items(): if isinstance(blob, str): logger.warning( ( "Blob {} with type {} is not supported in generating init net," " skipped.".format(name, type(blob)) ) ) continue init_net.op.extend( [create_const_fill_op(name, blob, device_option=device_options.get(name, None))] ) init_net.external_output.append(name) return init_net def get_producer_map(ssa): """ Return dict from versioned blob to (i, j), where i is index of producer op, j is the index of output of that op. """ producer_map = {} for i in range(len(ssa)): outputs = ssa[i][1] for j, outp in enumerate(outputs): producer_map[outp] = (i, j) return producer_map def get_consumer_map(ssa): """ Return dict from versioned blob to list of (i, j), where i is index of consumer op, j is the index of input of that op. """ consumer_map = collections.defaultdict(list) for i in range(len(ssa)): inputs = ssa[i][0] for j, inp in enumerate(inputs): consumer_map[inp].append((i, j)) return consumer_map def get_params_from_init_net( init_net: caffe2_pb2.NetDef, ) -> [Dict[str, Any], Dict[str, caffe2_pb2.DeviceOption]]: """ Take the output blobs from init_net by running it. Outputs: params: dict from blob name to numpy array device_options: dict from blob name to the device option of its creating op """ # NOTE: this assumes that the params is determined by producer op with the # only exception be CopyGPUToCPU which is CUDA op but returns CPU tensor. def _get_device_option(producer_op): if producer_op.type == "CopyGPUToCPU": return caffe2_pb2.DeviceOption() else: return producer_op.device_option with ScopedWS("__get_params_from_init_net__", is_reset=True, is_cleanup=True) as ws: ws.RunNetOnce(init_net) params = {b: fetch_any_blob(b) for b in init_net.external_output} ssa, versions = core.get_ssa(init_net) producer_map = get_producer_map(ssa) device_options = { b: _get_device_option(init_net.op[producer_map[(b, versions[b])][0]]) for b in init_net.external_output } return params, device_options def _updater_raise(op, input_types, output_types): raise RuntimeError( "Failed to apply updater for op {} given input_types {} and" " output_types {}".format(op, input_types, output_types) ) def _generic_status_identifier( predict_net: caffe2_pb2.NetDef, status_updater: Callable, known_status: Dict[Tuple[str, int], Any], ) -> Dict[Tuple[str, int], Any]: """ Statically infer the status of each blob, the status can be such as device type (CPU/GPU), layout (NCHW/NHWC), data type (float32/int8), etc. "Blob" here is versioned blob (Tuple[str, int]) in the format compatible with ssa. Inputs: predict_net: the caffe2 network status_updater: a callable, given an op and the status of its input/output, it returns the updated status of input/output. `None` is used for representing unknown status. known_status: a dict containing known status, used as initialization. Outputs: A dict mapping from versioned blob to its status """ ssa, versions = core.get_ssa(predict_net) versioned_ext_input = [(b, 0) for b in predict_net.external_input] versioned_ext_output = [(b, versions[b]) for b in predict_net.external_output] all_versioned_blobs = set().union(*[set(x[0] + x[1]) for x in ssa]) allowed_vbs = all_versioned_blobs.union(versioned_ext_input).union(versioned_ext_output) assert all(k in allowed_vbs for k in known_status) assert all(v is not None for v in known_status.values()) _known_status = copy.deepcopy(known_status) def _check_and_update(key, value): assert value is not None if key in _known_status: if not _known_status[key] == value: raise RuntimeError( "Confilict status for {}, existing status {}, new status {}".format( key, _known_status[key], value ) ) _known_status[key] = value def _update_i(op, ssa_i): versioned_inputs = ssa_i[0] versioned_outputs = ssa_i[1] inputs_status = [_known_status.get(b, None) for b in versioned_inputs] outputs_status = [_known_status.get(b, None) for b in versioned_outputs] new_inputs_status, new_outputs_status = status_updater(op, inputs_status, outputs_status) for versioned_blob, status in zip( versioned_inputs + versioned_outputs, new_inputs_status + new_outputs_status ): if status is not None: _check_and_update(versioned_blob, status) for op, ssa_i in zip(predict_net.op, ssa): _update_i(op, ssa_i) for op, ssa_i in zip(reversed(predict_net.op), reversed(ssa)): _update_i(op, ssa_i) # NOTE: This strictly checks all the blob from predict_net must be assgined # a known status. However sometimes it's impossible (eg. having deadend op), # we may relax this constraint if for k in all_versioned_blobs: if k not in _known_status: raise NotImplementedError( "Can not infer the status for {}. Currently only support the case where" " a single forward and backward pass can identify status for all blobs.".format(k) ) return _known_status def infer_device_type( predict_net: caffe2_pb2.NetDef, known_status: Dict[Tuple[str, int], Any], device_name_style: str = "caffe2", ) -> Dict[Tuple[str, int], str]: """Return the device type ("cpu" or "gpu"/"cuda") of each (versioned) blob""" assert device_name_style in ["caffe2", "pytorch"] _CPU_STR = "cpu" _GPU_STR = "gpu" if device_name_style == "caffe2" else "cuda" def _copy_cpu_to_gpu_updater(op, input_types, output_types): if input_types[0] == _GPU_STR or output_types[0] == _CPU_STR: _updater_raise(op, input_types, output_types) return ([_CPU_STR], [_GPU_STR]) def _copy_gpu_to_cpu_updater(op, input_types, output_types): if input_types[0] == _CPU_STR or output_types[0] == _GPU_STR: _updater_raise(op, input_types, output_types) return ([_GPU_STR], [_CPU_STR]) def _other_ops_updater(op, input_types, output_types): non_none_types = [x for x in input_types + output_types if x is not None] if len(non_none_types) > 0: the_type = non_none_types[0] if not all(x == the_type for x in non_none_types): _updater_raise(op, input_types, output_types) else: the_type = None return ([the_type for _ in op.input], [the_type for _ in op.output]) def _device_updater(op, *args, **kwargs): return { "CopyCPUToGPU": _copy_cpu_to_gpu_updater, "CopyGPUToCPU": _copy_gpu_to_cpu_updater, }.get(op.type, _other_ops_updater)(op, *args, **kwargs) return _generic_status_identifier(predict_net, _device_updater, known_status) # ==== torch/utils_caffe2/vis.py =============================================== def _modify_blob_names(ops, blob_rename_f): ret = [] def _replace_list(blob_list, replaced_list): del blob_list[:] blob_list.extend(replaced_list) for x in ops: cur = copy.deepcopy(x) _replace_list(cur.input, list(map(blob_rename_f, cur.input))) _replace_list(cur.output, list(map(blob_rename_f, cur.output))) ret.append(cur) return ret def _rename_blob(name, blob_sizes, blob_ranges): def _list_to_str(bsize): ret = ", ".join([str(x) for x in bsize]) ret = "[" + ret + "]" return ret ret = name if blob_sizes is not None and name in blob_sizes: ret += "\n" + _list_to_str(blob_sizes[name]) if blob_ranges is not None and name in blob_ranges: ret += "\n" + _list_to_str(blob_ranges[name]) return ret # graph_name could not contain word 'graph' def save_graph(net, file_name, graph_name="net", op_only=True, blob_sizes=None, blob_ranges=None): blob_rename_f = functools.partial(_rename_blob, blob_sizes=blob_sizes, blob_ranges=blob_ranges) return save_graph_base(net, file_name, graph_name, op_only, blob_rename_f) def save_graph_base(net, file_name, graph_name="net", op_only=True, blob_rename_func=None): graph = None ops = net.op if blob_rename_func is not None: ops = _modify_blob_names(ops, blob_rename_func) 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 ) try: par_dir = os.path.dirname(file_name) if not os.path.exists(par_dir): os.makedirs(par_dir) format = os.path.splitext(os.path.basename(file_name))[-1] if format == ".png": graph.write_png(file_name) elif format == ".pdf": graph.write_pdf(file_name) elif format == ".svg": graph.write_svg(file_name) else: print("Incorrect format {}".format(format)) except Exception as e: print("Error when writing graph to image {}".format(e)) return graph # ==== torch/utils_toffee/aten_to_caffe2.py ==================================== def group_norm_replace_aten_with_caffe2(predict_net: caffe2_pb2.NetDef): """ For ONNX exported model, GroupNorm will be represented as ATen op, this can be a drop in replacement from ATen to GroupNorm """ count = 0 for op in predict_net.op: if op.type == "ATen": op_name = get_pb_arg_vals(op, "operator", None) # return byte in py3 if op_name and op_name.decode() == "group_norm": op.arg.remove(get_pb_arg(op, "operator")) if get_pb_arg_vali(op, "cudnn_enabled", None): op.arg.remove(get_pb_arg(op, "cudnn_enabled")) num_groups = get_pb_arg_vali(op, "num_groups", None) if num_groups is not None: op.arg.remove(get_pb_arg(op, "num_groups")) check_set_pb_arg(op, "group", "i", num_groups) op.type = "GroupNorm" count += 1 if count > 1: logger.info("Replaced {} ATen operator to GroupNormOp".format(count)) # ==== torch/utils_toffee/alias.py ============================================= def alias(x, name, is_backward=False): if not torch.onnx.is_in_onnx_export(): return x assert isinstance(x, torch.Tensor) return torch.ops._caffe2.AliasWithName(x, name, is_backward=is_backward) def fuse_alias_placeholder(predict_net, init_net): """Remove AliasWithName placeholder and rename the input/output of it""" # First we finish all the re-naming for i, op in enumerate(predict_net.op): if op.type == "AliasWithName": assert len(op.input) == 1 assert len(op.output) == 1 name = get_pb_arg_vals(op, "name", None).decode() is_backward = bool(get_pb_arg_vali(op, "is_backward", 0)) rename_op_input(predict_net, init_net, i, 0, name, from_producer=is_backward) rename_op_output(predict_net, i, 0, name) # Remove AliasWithName, should be very safe since it's a non-op new_ops = [] for op in predict_net.op: if op.type != "AliasWithName": new_ops.append(op) else: # safety check assert op.input == op.output assert op.input[0] == op.arg[0].s.decode() del predict_net.op[:] predict_net.op.extend(new_ops) # ==== torch/utils_caffe2/graph_transform.py =================================== class IllegalGraphTransformError(ValueError): """When a graph transform function call can't be executed.""" def _rename_versioned_blob_in_proto( proto: caffe2_pb2.NetDef, old_name: str, new_name: str, version: int, ssa: List[Tuple[List[Tuple[str, int]], List[Tuple[str, int]]]], start_versions: Dict[str, int], end_versions: Dict[str, int], ): """In given proto, rename all blobs with matched version""" # Operater list for op, i_th_ssa in zip(proto.op, ssa): versioned_inputs, versioned_outputs = i_th_ssa for i in range(len(op.input)): if versioned_inputs[i] == (old_name, version): op.input[i] = new_name for i in range(len(op.output)): if versioned_outputs[i] == (old_name, version): op.output[i] = new_name # external_input if start_versions.get(old_name, 0) == version: for i in range(len(proto.external_input)): if proto.external_input[i] == old_name: proto.external_input[i] = new_name # external_output if end_versions.get(old_name, 0) == version: for i in range(len(proto.external_output)): if proto.external_output[i] == old_name: proto.external_output[i] = new_name def rename_op_input( predict_net: caffe2_pb2.NetDef, init_net: caffe2_pb2.NetDef, op_id: int, input_id: int, new_name: str, from_producer: bool = False, ): """ Rename the op_id-th operator in predict_net, change it's input_id-th input's name to the new_name. It also does automatic re-route and change external_input and init_net if necessary. - It requires the input is only consumed by this op. - This function modifies predict_net and init_net in-place. - When from_producer is enable, this also updates other operators that consumes the same input. Be cautious because may trigger unintended behavior. """ assert isinstance(predict_net, caffe2_pb2.NetDef) assert isinstance(init_net, caffe2_pb2.NetDef) init_net_ssa, init_net_versions = core.get_ssa(init_net) predict_net_ssa, predict_net_versions = core.get_ssa( predict_net, copy.deepcopy(init_net_versions) ) versioned_inputs, versioned_outputs = predict_net_ssa[op_id] old_name, version = versioned_inputs[input_id] if from_producer: producer_map = get_producer_map(predict_net_ssa) if not (old_name, version) in producer_map: raise NotImplementedError( "Can't find producer, the input {} is probably from" " init_net, this is not supported yet.".format(old_name) ) producer = producer_map[(old_name, version)] rename_op_output(predict_net, producer[0], producer[1], new_name) return def contain_targets(op_ssa): return (old_name, version) in op_ssa[0] is_consumer = [contain_targets(op_ssa) for op_ssa in predict_net_ssa] if sum(is_consumer) > 1: raise IllegalGraphTransformError( ( "Input '{}' of operator(#{}) are consumed by other ops, please use" + " rename_op_output on the producer instead. Offending op: \n{}" ).format(old_name, op_id, predict_net.op[op_id]) ) # update init_net _rename_versioned_blob_in_proto( init_net, old_name, new_name, version, init_net_ssa, {}, init_net_versions ) # update predict_net _rename_versioned_blob_in_proto( predict_net, old_name, new_name, version, predict_net_ssa, init_net_versions, predict_net_versions, ) def rename_op_output(predict_net: caffe2_pb2.NetDef, op_id: int, output_id: int, new_name: str): """ Rename the op_id-th operator in predict_net, change it's output_id-th input's name to the new_name. It also does automatic re-route and change external_output and if necessary. - It allows multiple consumers of its output. - This function modifies predict_net in-place, doesn't need init_net. """ assert isinstance(predict_net, caffe2_pb2.NetDef) ssa, blob_versions = core.get_ssa(predict_net) versioned_inputs, versioned_outputs = ssa[op_id] old_name, version = versioned_outputs[output_id] # update predict_net _rename_versioned_blob_in_proto( predict_net, old_name, new_name, version, ssa, {}, blob_versions ) def get_sub_graph_external_input_output( predict_net: caffe2_pb2.NetDef, sub_graph_op_indices: List[int] ) -> Tuple[List[Tuple[str, int]], List[Tuple[str, int]]]: """ Return the list of external input/output of sub-graph, each element is tuple of the name and corresponding version in predict_net. external input/output is defined the same way as caffe2 NetDef. """ ssa, versions = core.get_ssa(predict_net) all_inputs = [] all_outputs = [] for op_id in sub_graph_op_indices: all_inputs += [inp for inp in ssa[op_id][0] if inp not in all_inputs] all_outputs += list(ssa[op_id][1]) # ssa output won't repeat # for versioned blobs, external inputs are just those blob in all_inputs # but not in all_outputs ext_inputs = [inp for inp in all_inputs if inp not in all_outputs] # external outputs are essentially outputs of this subgraph that are used # outside of this sub-graph (including predict_net.external_output) all_other_inputs = sum( (ssa[i][0] for i in range(len(ssa)) if i not in sub_graph_op_indices), [(outp, versions[outp]) for outp in predict_net.external_output], ) ext_outputs = [outp for outp in all_outputs if outp in set(all_other_inputs)] return ext_inputs, ext_outputs class DiGraph: """A DAG representation of caffe2 graph, each vertice is a versioned blob.""" def __init__(self): self.vertices = set() self.graph = collections.defaultdict(list) def add_edge(self, u, v): self.graph[u].append(v) self.vertices.add(u) self.vertices.add(v) # grab from https://www.geeksforgeeks.org/find-paths-given-source-destination/ def get_all_paths(self, s, d): visited = {k: False for k in self.vertices} path = [] all_paths = [] def _get_all_paths_util(graph, u, d, visited, path): visited[u] = True path.append(u) if u == d: all_paths.append(copy.deepcopy(path)) else: for i in graph[u]: if not visited[i]: _get_all_paths_util(graph, i, d, visited, path) path.pop() visited[u] = False _get_all_paths_util(self.graph, s, d, visited, path) return all_paths @staticmethod def from_ssa(ssa): graph = DiGraph() for op_id in range(len(ssa)): for inp in ssa[op_id][0]: for outp in ssa[op_id][1]: graph.add_edge(inp, outp) return graph def _get_dependency_chain(ssa, versioned_target, versioned_source): """ Return the index list of relevant operator to produce target blob from source blob, if there's no dependency, return empty list. """ # finding all paths between nodes can be O(N!), thus we can only search # in the subgraph using the op starting from the first consumer of source blob # to the producer of the target blob. consumer_map = get_consumer_map(ssa) producer_map = get_producer_map(ssa) start_op = min(x[0] for x in consumer_map[versioned_source]) - 15 end_op = ( producer_map[versioned_target][0] + 15 if versioned_target in producer_map else start_op ) sub_graph_ssa = ssa[start_op : end_op + 1] if len(sub_graph_ssa) > 30: logger.warning( "Subgraph bebetween {} and {} is large (from op#{} to op#{}), it" " might take non-trival time to find all paths between them.".format( versioned_source, versioned_target, start_op, end_op ) ) dag = DiGraph.from_ssa(sub_graph_ssa) paths = dag.get_all_paths(versioned_source, versioned_target) # include two ends ops_in_paths = [[producer_map[blob][0] for blob in path[1:]] for path in paths] return sorted(set().union(*[set(ops) for ops in ops_in_paths])) def identify_reshape_sub_graph(predict_net: caffe2_pb2.NetDef) -> List[List[int]]: """ Idenfity the reshape sub-graph in a protobuf. The reshape sub-graph is defined as matching the following pattern: (input_blob) -> Op_1 -> ... -> Op_N -> (new_shape) -─┐ └-------------------------------------------> Reshape -> (output_blob) Return: List of sub-graphs, each sub-graph is represented as a list of indices of the relavent ops, [Op_1, Op_2, ..., Op_N, Reshape] """ ssa, _ = core.get_ssa(predict_net) ret = [] for i, op in enumerate(predict_net.op): if op.type == "Reshape": assert len(op.input) == 2 input_ssa = ssa[i][0] data_source = input_ssa[0] shape_source = input_ssa[1] op_indices = _get_dependency_chain(ssa, shape_source, data_source) ret.append(op_indices + [i]) return ret def remove_reshape_for_fc(predict_net, params): """ In PyTorch nn.Linear has to take 2D tensor, this often leads to reshape a 4D tensor to 2D by calling .view(). However this (dynamic) reshaping doesn't work well with ONNX and Int8 tools, and cause using extra ops (eg. ExpandDims) that might not be available on mobile. Luckily Caffe2 supports 4D tensor for FC, so we can remove those reshape after exporting ONNX model. """ from caffe2.python import core # find all reshape sub-graph that can be removed, which is now all Reshape # sub-graph whose output is only consumed by FC. # TODO: to make it safer, we may need the actually value to better determine # if a Reshape before FC is removable. reshape_sub_graphs = identify_reshape_sub_graph(predict_net) sub_graphs_to_remove = [] for reshape_sub_graph in reshape_sub_graphs: reshape_op_id = reshape_sub_graph[-1] assert predict_net.op[reshape_op_id].type == "Reshape" ssa, _ = core.get_ssa(predict_net) reshape_output = ssa[reshape_op_id][1][0] consumers = [i for i in range(len(ssa)) if reshape_output in ssa[i][0]] if all(predict_net.op[consumer].type == "FC" for consumer in consumers): # safety check if the sub-graph is isolated, for this reshape sub-graph, # it means it has one non-param external input and one external output. ext_inputs, ext_outputs = get_sub_graph_external_input_output( predict_net, reshape_sub_graph ) non_params_ext_inputs = [inp for inp in ext_inputs if inp[1] != 0] if len(non_params_ext_inputs) == 1 and len(ext_outputs) == 1: sub_graphs_to_remove.append(reshape_sub_graph) # perform removing subgraph by: # 1: rename the Reshape's output to its input, then the graph can be # seen as in-place itentify, meaning whose external input/output are the same. # 2: simply remove those ops. remove_op_ids = [] params_to_remove = [] for sub_graph in sub_graphs_to_remove: logger.info( "Remove Reshape sub-graph:\n{}".format( "".join(["(#{:>4})\n{}".format(i, predict_net.op[i]) for i in sub_graph]) ) ) reshape_op_id = sub_graph[-1] new_reshap_output = predict_net.op[reshape_op_id].input[0] rename_op_output(predict_net, reshape_op_id, 0, new_reshap_output) ext_inputs, ext_outputs = get_sub_graph_external_input_output(predict_net, sub_graph) non_params_ext_inputs = [inp for inp in ext_inputs if inp[1] != 0] params_ext_inputs = [inp for inp in ext_inputs if inp[1] == 0] assert len(non_params_ext_inputs) == 1 and len(ext_outputs) == 1 assert ext_outputs[0][0] == non_params_ext_inputs[0][0] assert ext_outputs[0][1] == non_params_ext_inputs[0][1] + 1 remove_op_ids.extend(sub_graph) params_to_remove.extend(params_ext_inputs) predict_net = copy.deepcopy(predict_net) new_ops = [op for i, op in enumerate(predict_net.op) if i not in remove_op_ids] del predict_net.op[:] predict_net.op.extend(new_ops) for versioned_params in params_to_remove: name = versioned_params[0] logger.info("Remove params: {} from init_net and predict_net.external_input".format(name)) del params[name] predict_net.external_input.remove(name) return predict_net, params def fuse_copy_between_cpu_and_gpu(predict_net: caffe2_pb2.NetDef): """ In-place fuse extra copy ops between cpu/gpu for the following case: a -CopyAToB-> b -CopyBToA> c1 -NextOp1-> d1 -CopyBToA> c2 -NextOp2-> d2 The fused network will look like: a -NextOp1-> d1 -NextOp2-> d2 """ _COPY_OPS = ["CopyCPUToGPU", "CopyGPUToCPU"] def _fuse_once(predict_net): ssa, blob_versions = core.get_ssa(predict_net) consumer_map = get_consumer_map(ssa) versioned_external_output = [ (name, blob_versions[name]) for name in predict_net.external_output ] for op_id, op in enumerate(predict_net.op): if op.type in _COPY_OPS: fw_copy_versioned_output = ssa[op_id][1][0] consumer_ids = [x[0] for x in consumer_map[fw_copy_versioned_output]] reverse_op_type = _COPY_OPS[1 - _COPY_OPS.index(op.type)] is_fusable = ( len(consumer_ids) > 0 and fw_copy_versioned_output not in versioned_external_output and all( predict_net.op[_op_id].type == reverse_op_type and ssa[_op_id][1][0] not in versioned_external_output for _op_id in consumer_ids ) ) if is_fusable: for rv_copy_op_id in consumer_ids: # making each NextOp uses "a" directly and removing Copy ops rs_copy_versioned_output = ssa[rv_copy_op_id][1][0] next_op_id, inp_id = consumer_map[rs_copy_versioned_output][0] predict_net.op[next_op_id].input[inp_id] = op.input[0] # remove CopyOps new_ops = [ op for i, op in enumerate(predict_net.op) if i != op_id and i not in consumer_ids ] del predict_net.op[:] predict_net.op.extend(new_ops) return True return False # _fuse_once returns False is nothing can be fused while _fuse_once(predict_net): pass def remove_dead_end_ops(net_def: caffe2_pb2.NetDef): """remove ops if its output is not used or not in external_output""" ssa, versions = core.get_ssa(net_def) versioned_external_output = [(name, versions[name]) for name in net_def.external_output] consumer_map = get_consumer_map(ssa) removed_op_ids = set() def _is_dead_end(versioned_blob): return not ( versioned_blob in versioned_external_output or ( len(consumer_map[versioned_blob]) > 0 and all(x[0] not in removed_op_ids for x in consumer_map[versioned_blob]) ) ) for i, ssa_i in reversed(list(enumerate(ssa))): versioned_outputs = ssa_i[1] if all(_is_dead_end(outp) for outp in versioned_outputs): removed_op_ids.add(i) # simply removing those deadend ops should have no effect to external_output new_ops = [op for i, op in enumerate(net_def.op) if i not in removed_op_ids] del net_def.op[:] net_def.op.extend(new_ops) ================================================ FILE: detectron2/detectron2/export/torchscript.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import os import torch from detectron2.utils.file_io import PathManager from .torchscript_patch import freeze_training_mode, patch_instances __all__ = ["scripting_with_instances", "dump_torchscript_IR"] def scripting_with_instances(model, fields): """ Run :func:`torch.jit.script` on a model that uses the :class:`Instances` class. Since attributes of :class:`Instances` are "dynamically" added in eager mode,it is difficult for scripting to support it out of the box. This function is made to support scripting a model that uses :class:`Instances`. It does the following: 1. Create a scriptable ``new_Instances`` class which behaves similarly to ``Instances``, but with all attributes been "static". The attributes need to be statically declared in the ``fields`` argument. 2. Register ``new_Instances``, and force scripting compiler to use it when trying to compile ``Instances``. After this function, the process will be reverted. User should be able to script another model using different fields. Example: Assume that ``Instances`` in the model consist of two attributes named ``proposal_boxes`` and ``objectness_logits`` with type :class:`Boxes` and :class:`Tensor` respectively during inference. You can call this function like: :: fields = {"proposal_boxes": Boxes, "objectness_logits": torch.Tensor} torchscipt_model = scripting_with_instances(model, fields) Note: It only support models in evaluation mode. Args: model (nn.Module): The input model to be exported by scripting. fields (Dict[str, type]): Attribute names and corresponding type that ``Instances`` will use in the model. Note that all attributes used in ``Instances`` need to be added, regardless of whether they are inputs/outputs of the model. Data type not defined in detectron2 is not supported for now. Returns: torch.jit.ScriptModule: the model in torchscript format """ assert ( not model.training ), "Currently we only support exporting models in evaluation mode to torchscript" with freeze_training_mode(model), patch_instances(fields): scripted_model = torch.jit.script(model) return scripted_model # alias for old name export_torchscript_with_instances = scripting_with_instances def dump_torchscript_IR(model, dir): """ Dump IR of a TracedModule/ScriptModule/Function in various format (code, graph, inlined graph). Useful for debugging. Args: model (TracedModule/ScriptModule/ScriptFUnction): traced or scripted module dir (str): output directory to dump files. """ dir = os.path.expanduser(dir) PathManager.mkdirs(dir) def _get_script_mod(mod): if isinstance(mod, torch.jit.TracedModule): return mod._actual_script_module return mod # Dump pretty-printed code: https://pytorch.org/docs/stable/jit.html#inspecting-code with PathManager.open(os.path.join(dir, "model_ts_code.txt"), "w") as f: def get_code(mod): # Try a few ways to get code using private attributes. try: # This contains more information than just `mod.code` return _get_script_mod(mod)._c.code except AttributeError: pass try: return mod.code except AttributeError: return None def dump_code(prefix, mod): code = get_code(mod) name = prefix or "root model" if code is None: f.write(f"Could not found code for {name} (type={mod.original_name})\n") f.write("\n") else: f.write(f"\nCode for {name}, type={mod.original_name}:\n") f.write(code) f.write("\n") f.write("-" * 80) for name, m in mod.named_children(): dump_code(prefix + "." + name, m) if isinstance(model, torch.jit.ScriptFunction): f.write(get_code(model)) else: dump_code("", model) def _get_graph(model): try: # Recursively dump IR of all modules return _get_script_mod(model)._c.dump_to_str(True, False, False) except AttributeError: return model.graph.str() with PathManager.open(os.path.join(dir, "model_ts_IR.txt"), "w") as f: f.write(_get_graph(model)) # Dump IR of the entire graph (all submodules inlined) with PathManager.open(os.path.join(dir, "model_ts_IR_inlined.txt"), "w") as f: f.write(str(model.inlined_graph)) if not isinstance(model, torch.jit.ScriptFunction): # Dump the model structure in pytorch style with PathManager.open(os.path.join(dir, "model.txt"), "w") as f: f.write(str(model)) ================================================ FILE: detectron2/detectron2/export/torchscript_patch.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import os import sys import tempfile from contextlib import ExitStack, contextmanager from copy import deepcopy from unittest import mock import torch from torch import nn # need some explicit imports due to https://github.com/pytorch/pytorch/issues/38964 import detectron2 # noqa F401 from detectron2.structures import Boxes, Instances from detectron2.utils.env import _import_file _counter = 0 def _clear_jit_cache(): from torch.jit._recursive import concrete_type_store from torch.jit._state import _jit_caching_layer concrete_type_store.type_store.clear() # for modules _jit_caching_layer.clear() # for free functions def _add_instances_conversion_methods(newInstances): """ Add from_instances methods to the scripted Instances class. """ cls_name = newInstances.__name__ @torch.jit.unused def from_instances(instances: Instances): """ Create scripted Instances from original Instances """ fields = instances.get_fields() image_size = instances.image_size ret = newInstances(image_size) for name, val in fields.items(): assert hasattr(ret, f"_{name}"), f"No attribute named {name} in {cls_name}" setattr(ret, name, deepcopy(val)) return ret newInstances.from_instances = from_instances @contextmanager def patch_instances(fields): """ A contextmanager, under which the Instances class in detectron2 is replaced by a statically-typed scriptable class, defined by `fields`. See more in `scripting_with_instances`. """ with tempfile.TemporaryDirectory(prefix="detectron2") as dir, tempfile.NamedTemporaryFile( mode="w", encoding="utf-8", suffix=".py", dir=dir, delete=False ) as f: try: # Objects that use Instances should not reuse previously-compiled # results in cache, because `Instances` could be a new class each time. _clear_jit_cache() cls_name, s = _gen_instance_module(fields) f.write(s) f.flush() f.close() module = _import(f.name) new_instances = getattr(module, cls_name) _ = torch.jit.script(new_instances) # let torchscript think Instances was scripted already Instances.__torch_script_class__ = True # let torchscript find new_instances when looking for the jit type of Instances Instances._jit_override_qualname = torch._jit_internal._qualified_name(new_instances) _add_instances_conversion_methods(new_instances) yield new_instances finally: try: del Instances.__torch_script_class__ del Instances._jit_override_qualname except AttributeError: pass sys.modules.pop(module.__name__) def _gen_instance_class(fields): """ Args: fields (dict[name: type]) """ class _FieldType: def __init__(self, name, type_): assert isinstance(name, str), f"Field name must be str, got {name}" self.name = name self.type_ = type_ self.annotation = f"{type_.__module__}.{type_.__name__}" fields = [_FieldType(k, v) for k, v in fields.items()] def indent(level, s): return " " * 4 * level + s lines = [] global _counter _counter += 1 cls_name = "ScriptedInstances{}".format(_counter) field_names = tuple(x.name for x in fields) extra_args = ", ".join([f"{f.name}: Optional[{f.annotation}] = None" for f in fields]) lines.append( f""" class {cls_name}: def __init__(self, image_size: Tuple[int, int], {extra_args}): self.image_size = image_size self._field_names = {field_names} """ ) for f in fields: lines.append( indent(2, f"self._{f.name} = torch.jit.annotate(Optional[{f.annotation}], {f.name})") ) for f in fields: lines.append( f""" @property def {f.name}(self) -> {f.annotation}: # has to use a local for type refinement # https://pytorch.org/docs/stable/jit_language_reference.html#optional-type-refinement t = self._{f.name} assert t is not None, "{f.name} is None and cannot be accessed!" return t @{f.name}.setter def {f.name}(self, value: {f.annotation}) -> None: self._{f.name} = value """ ) # support method `__len__` lines.append( """ def __len__(self) -> int: """ ) for f in fields: lines.append( f""" t = self._{f.name} if t is not None: return len(t) """ ) lines.append( """ raise NotImplementedError("Empty Instances does not support __len__!") """ ) # support method `has` lines.append( """ def has(self, name: str) -> bool: """ ) for f in fields: lines.append( f""" if name == "{f.name}": return self._{f.name} is not None """ ) lines.append( """ return False """ ) # support method `to` none_args = ", None" * len(fields) lines.append( f""" def to(self, device: torch.device) -> "{cls_name}": ret = {cls_name}(self.image_size{none_args}) """ ) for f in fields: if hasattr(f.type_, "to"): lines.append( f""" t = self._{f.name} if t is not None: ret._{f.name} = t.to(device) """ ) else: # For now, ignore fields that cannot be moved to devices. # Maybe can support other tensor-like classes (e.g. __torch_function__) pass lines.append( """ return ret """ ) # support method `getitem` none_args = ", None" * len(fields) lines.append( f""" def __getitem__(self, item) -> "{cls_name}": ret = {cls_name}(self.image_size{none_args}) """ ) for f in fields: lines.append( f""" t = self._{f.name} if t is not None: ret._{f.name} = t[item] """ ) lines.append( """ return ret """ ) # support method `cat` # this version does not contain checks that all instances have same size and fields none_args = ", None" * len(fields) lines.append( f""" def cat(self, instances: List["{cls_name}"]) -> "{cls_name}": ret = {cls_name}(self.image_size{none_args}) """ ) for f in fields: lines.append( f""" t = self._{f.name} if t is not None: values: List[{f.annotation}] = [x.{f.name} for x in instances] if torch.jit.isinstance(t, torch.Tensor): ret._{f.name} = torch.cat(values, dim=0) else: ret._{f.name} = t.cat(values) """ ) lines.append( """ return ret""" ) # support method `get_fields()` lines.append( """ def get_fields(self) -> Dict[str, Tensor]: ret = {} """ ) for f in fields: if f.type_ == Boxes: stmt = "t.tensor" elif f.type_ == torch.Tensor: stmt = "t" else: stmt = f'assert False, "unsupported type {str(f.type_)}"' lines.append( f""" t = self._{f.name} if t is not None: ret["{f.name}"] = {stmt} """ ) lines.append( """ return ret""" ) return cls_name, os.linesep.join(lines) def _gen_instance_module(fields): # TODO: find a more automatic way to enable import of other classes s = """ from copy import deepcopy import torch from torch import Tensor import typing from typing import * import detectron2 from detectron2.structures import Boxes, Instances """ cls_name, cls_def = _gen_instance_class(fields) s += cls_def return cls_name, s def _import(path): return _import_file( "{}{}".format(sys.modules[__name__].__name__, _counter), path, make_importable=True ) @contextmanager def patch_builtin_len(modules=()): """ Patch the builtin len() function of a few detectron2 modules to use __len__ instead, because __len__ does not convert values to integers and therefore is friendly to tracing. Args: modules (list[stsr]): names of extra modules to patch len(), in addition to those in detectron2. """ def _new_len(obj): return obj.__len__() with ExitStack() as stack: MODULES = [ "detectron2.modeling.roi_heads.fast_rcnn", "detectron2.modeling.roi_heads.mask_head", "detectron2.modeling.roi_heads.keypoint_head", ] + list(modules) ctxs = [stack.enter_context(mock.patch(mod + ".len")) for mod in MODULES] for m in ctxs: m.side_effect = _new_len yield def patch_nonscriptable_classes(): """ Apply patches on a few nonscriptable detectron2 classes. Should not have side-effects on eager usage. """ # __prepare_scriptable__ can also be added to models for easier maintenance. # But it complicates the clean model code. from detectron2.modeling.backbone import ResNet, FPN # Due to https://github.com/pytorch/pytorch/issues/36061, # we change backbone to use ModuleList for scripting. # (note: this changes param names in state_dict) def prepare_resnet(self): ret = deepcopy(self) ret.stages = nn.ModuleList(ret.stages) for k in self.stage_names: delattr(ret, k) return ret ResNet.__prepare_scriptable__ = prepare_resnet def prepare_fpn(self): ret = deepcopy(self) ret.lateral_convs = nn.ModuleList(ret.lateral_convs) ret.output_convs = nn.ModuleList(ret.output_convs) for name, _ in self.named_children(): if name.startswith("fpn_"): delattr(ret, name) return ret FPN.__prepare_scriptable__ = prepare_fpn # Annotate some attributes to be constants for the purpose of scripting, # even though they are not constants in eager mode. from detectron2.modeling.roi_heads import StandardROIHeads if hasattr(StandardROIHeads, "__annotations__"): # copy first to avoid editing annotations of base class StandardROIHeads.__annotations__ = deepcopy(StandardROIHeads.__annotations__) StandardROIHeads.__annotations__["mask_on"] = torch.jit.Final[bool] StandardROIHeads.__annotations__["keypoint_on"] = torch.jit.Final[bool] # These patches are not supposed to have side-effects. patch_nonscriptable_classes() @contextmanager def freeze_training_mode(model): """ A context manager that annotates the "training" attribute of every submodule to constant, so that the training codepath in these modules can be meta-compiled away. Upon exiting, the annotations are reverted. """ classes = {type(x) for x in model.modules()} # __constants__ is the old way to annotate constants and not compatible # with __annotations__ . classes = {x for x in classes if not hasattr(x, "__constants__")} for cls in classes: cls.__annotations__["training"] = torch.jit.Final[bool] yield for cls in classes: cls.__annotations__["training"] = bool ================================================ FILE: detectron2/detectron2/layers/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .batch_norm import FrozenBatchNorm2d, get_norm, NaiveSyncBatchNorm, CycleBatchNormList from .deform_conv import DeformConv, ModulatedDeformConv from .mask_ops import paste_masks_in_image from .nms import batched_nms, batched_nms_rotated, nms, nms_rotated from .roi_align import ROIAlign, roi_align from .roi_align_rotated import ROIAlignRotated, roi_align_rotated from .shape_spec import ShapeSpec from .wrappers import ( BatchNorm2d, Conv2d, ConvTranspose2d, cat, interpolate, Linear, nonzero_tuple, cross_entropy, empty_input_loss_func_wrapper, shapes_to_tensor, move_device_like, ) from .blocks import CNNBlockBase, DepthwiseSeparableConv2d from .aspp import ASPP from .losses import ciou_loss, diou_loss __all__ = [k for k in globals().keys() if not k.startswith("_")] ================================================ FILE: detectron2/detectron2/layers/aspp.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from copy import deepcopy import fvcore.nn.weight_init as weight_init import torch from torch import nn from torch.nn import functional as F from .batch_norm import get_norm from .blocks import DepthwiseSeparableConv2d from .wrappers import Conv2d class ASPP(nn.Module): """ Atrous Spatial Pyramid Pooling (ASPP). """ def __init__( self, in_channels, out_channels, dilations, *, norm, activation, pool_kernel_size=None, dropout: float = 0.0, use_depthwise_separable_conv=False, ): """ Args: in_channels (int): number of input channels for ASPP. out_channels (int): number of output channels. dilations (list): a list of 3 dilations in ASPP. norm (str or callable): normalization for all conv layers. See :func:`layers.get_norm` for supported format. norm is applied to all conv layers except the conv following global average pooling. activation (callable): activation function. pool_kernel_size (tuple, list): the average pooling size (kh, kw) for image pooling layer in ASPP. If set to None, it always performs global average pooling. If not None, it must be divisible by the shape of inputs in forward(). It is recommended to use a fixed input feature size in training, and set this option to match this size, so that it performs global average pooling in training, and the size of the pooling window stays consistent in inference. dropout (float): apply dropout on the output of ASPP. It is used in the official DeepLab implementation with a rate of 0.1: https://github.com/tensorflow/models/blob/21b73d22f3ed05b650e85ac50849408dd36de32e/research/deeplab/model.py#L532 # noqa use_depthwise_separable_conv (bool): use DepthwiseSeparableConv2d for 3x3 convs in ASPP, proposed in :paper:`DeepLabV3+`. """ super(ASPP, self).__init__() assert len(dilations) == 3, "ASPP expects 3 dilations, got {}".format(len(dilations)) self.pool_kernel_size = pool_kernel_size self.dropout = dropout use_bias = norm == "" self.convs = nn.ModuleList() # conv 1x1 self.convs.append( Conv2d( in_channels, out_channels, kernel_size=1, bias=use_bias, norm=get_norm(norm, out_channels), activation=deepcopy(activation), ) ) weight_init.c2_xavier_fill(self.convs[-1]) # atrous convs for dilation in dilations: if use_depthwise_separable_conv: self.convs.append( DepthwiseSeparableConv2d( in_channels, out_channels, kernel_size=3, padding=dilation, dilation=dilation, norm1=norm, activation1=deepcopy(activation), norm2=norm, activation2=deepcopy(activation), ) ) else: self.convs.append( Conv2d( in_channels, out_channels, kernel_size=3, padding=dilation, dilation=dilation, bias=use_bias, norm=get_norm(norm, out_channels), activation=deepcopy(activation), ) ) weight_init.c2_xavier_fill(self.convs[-1]) # image pooling # We do not add BatchNorm because the spatial resolution is 1x1, # the original TF implementation has BatchNorm. if pool_kernel_size is None: image_pooling = nn.Sequential( nn.AdaptiveAvgPool2d(1), Conv2d(in_channels, out_channels, 1, bias=True, activation=deepcopy(activation)), ) else: image_pooling = nn.Sequential( nn.AvgPool2d(kernel_size=pool_kernel_size, stride=1), Conv2d(in_channels, out_channels, 1, bias=True, activation=deepcopy(activation)), ) weight_init.c2_xavier_fill(image_pooling[1]) self.convs.append(image_pooling) self.project = Conv2d( 5 * out_channels, out_channels, kernel_size=1, bias=use_bias, norm=get_norm(norm, out_channels), activation=deepcopy(activation), ) weight_init.c2_xavier_fill(self.project) def forward(self, x): size = x.shape[-2:] if self.pool_kernel_size is not None: if size[0] % self.pool_kernel_size[0] or size[1] % self.pool_kernel_size[1]: raise ValueError( "`pool_kernel_size` must be divisible by the shape of inputs. " "Input size: {} `pool_kernel_size`: {}".format(size, self.pool_kernel_size) ) res = [] for conv in self.convs: res.append(conv(x)) res[-1] = F.interpolate(res[-1], size=size, mode="bilinear", align_corners=False) res = torch.cat(res, dim=1) res = self.project(res) res = F.dropout(res, self.dropout, training=self.training) if self.dropout > 0 else res return res ================================================ FILE: detectron2/detectron2/layers/batch_norm.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch import torch.distributed as dist from fvcore.nn.distributed import differentiable_all_reduce from torch import nn from torch.nn import functional as F from detectron2.utils import comm, env from .wrappers import BatchNorm2d class FrozenBatchNorm2d(nn.Module): """ BatchNorm2d where the batch statistics and the affine parameters are fixed. It contains non-trainable buffers called "weight" and "bias", "running_mean", "running_var", initialized to perform identity transformation. The pre-trained backbone models from Caffe2 only contain "weight" and "bias", which are computed from the original four parameters of BN. The affine transform `x * weight + bias` will perform the equivalent computation of `(x - running_mean) / sqrt(running_var) * weight + bias`. When loading a backbone model from Caffe2, "running_mean" and "running_var" will be left unchanged as identity transformation. Other pre-trained backbone models may contain all 4 parameters. The forward is implemented by `F.batch_norm(..., training=False)`. """ _version = 3 def __init__(self, num_features, eps=1e-5): super().__init__() self.num_features = num_features self.eps = eps self.register_buffer("weight", torch.ones(num_features)) self.register_buffer("bias", torch.zeros(num_features)) self.register_buffer("running_mean", torch.zeros(num_features)) self.register_buffer("running_var", torch.ones(num_features) - eps) def forward(self, x): if x.requires_grad: # When gradients are needed, F.batch_norm will use extra memory # because its backward op computes gradients for weight/bias as well. scale = self.weight * (self.running_var + self.eps).rsqrt() bias = self.bias - self.running_mean * scale scale = scale.reshape(1, -1, 1, 1) bias = bias.reshape(1, -1, 1, 1) out_dtype = x.dtype # may be half return x * scale.to(out_dtype) + bias.to(out_dtype) else: # When gradients are not needed, F.batch_norm is a single fused op # and provide more optimization opportunities. return F.batch_norm( x, self.running_mean, self.running_var, self.weight, self.bias, training=False, eps=self.eps, ) def _load_from_state_dict( self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs ): version = local_metadata.get("version", None) if version is None or version < 2: # No running_mean/var in early versions # This will silent the warnings if prefix + "running_mean" not in state_dict: state_dict[prefix + "running_mean"] = torch.zeros_like(self.running_mean) if prefix + "running_var" not in state_dict: state_dict[prefix + "running_var"] = torch.ones_like(self.running_var) super()._load_from_state_dict( state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs ) def __repr__(self): return "FrozenBatchNorm2d(num_features={}, eps={})".format(self.num_features, self.eps) @classmethod def convert_frozen_batchnorm(cls, module): """ Convert all BatchNorm/SyncBatchNorm in module into FrozenBatchNorm. Args: module (torch.nn.Module): Returns: If module is BatchNorm/SyncBatchNorm, returns a new module. Otherwise, in-place convert module and return it. Similar to convert_sync_batchnorm in https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/batchnorm.py """ bn_module = nn.modules.batchnorm bn_module = (bn_module.BatchNorm2d, bn_module.SyncBatchNorm) res = module if isinstance(module, bn_module): res = cls(module.num_features) if module.affine: res.weight.data = module.weight.data.clone().detach() res.bias.data = module.bias.data.clone().detach() res.running_mean.data = module.running_mean.data res.running_var.data = module.running_var.data res.eps = module.eps else: for name, child in module.named_children(): new_child = cls.convert_frozen_batchnorm(child) if new_child is not child: res.add_module(name, new_child) return res def get_norm(norm, out_channels): """ Args: norm (str or callable): either one of BN, SyncBN, FrozenBN, GN; or a callable that takes a channel number and returns the normalization layer as a nn.Module. Returns: nn.Module or None: the normalization layer """ if norm is None: return None if isinstance(norm, str): if len(norm) == 0: return None norm = { "BN": BatchNorm2d, # Fixed in https://github.com/pytorch/pytorch/pull/36382 "SyncBN": NaiveSyncBatchNorm if env.TORCH_VERSION <= (1, 5) else nn.SyncBatchNorm, "FrozenBN": FrozenBatchNorm2d, "GN": lambda channels: nn.GroupNorm(32, channels), # for debugging: "nnSyncBN": nn.SyncBatchNorm, "naiveSyncBN": NaiveSyncBatchNorm, # expose stats_mode N as an option to caller, required for zero-len inputs "naiveSyncBN_N": lambda channels: NaiveSyncBatchNorm(channels, stats_mode="N"), "LN": lambda channels: LayerNorm(channels), }[norm] return norm(out_channels) class NaiveSyncBatchNorm(BatchNorm2d): """ In PyTorch<=1.5, ``nn.SyncBatchNorm`` has incorrect gradient when the batch size on each worker is different. (e.g., when scale augmentation is used, or when it is applied to mask head). This is a slower but correct alternative to `nn.SyncBatchNorm`. Note: There isn't a single definition of Sync BatchNorm. When ``stats_mode==""``, this module computes overall statistics by using statistics of each worker with equal weight. The result is true statistics of all samples (as if they are all on one worker) only when all workers have the same (N, H, W). This mode does not support inputs with zero batch size. When ``stats_mode=="N"``, this module computes overall statistics by weighting the statistics of each worker by their ``N``. The result is true statistics of all samples (as if they are all on one worker) only when all workers have the same (H, W). It is slower than ``stats_mode==""``. Even though the result of this module may not be the true statistics of all samples, it may still be reasonable because it might be preferrable to assign equal weights to all workers, regardless of their (H, W) dimension, instead of putting larger weight on larger images. From preliminary experiments, little difference is found between such a simplified implementation and an accurate computation of overall mean & variance. """ def __init__(self, *args, stats_mode="", **kwargs): super().__init__(*args, **kwargs) assert stats_mode in ["", "N"] self._stats_mode = stats_mode def forward(self, input): if comm.get_world_size() == 1 or not self.training: return super().forward(input) B, C = input.shape[0], input.shape[1] half_input = input.dtype == torch.float16 if half_input: # fp16 does not have good enough numerics for the reduction here input = input.float() mean = torch.mean(input, dim=[0, 2, 3]) meansqr = torch.mean(input * input, dim=[0, 2, 3]) if self._stats_mode == "": assert B > 0, 'SyncBatchNorm(stats_mode="") does not support zero batch size.' vec = torch.cat([mean, meansqr], dim=0) vec = differentiable_all_reduce(vec) * (1.0 / dist.get_world_size()) mean, meansqr = torch.split(vec, C) momentum = self.momentum else: if B == 0: vec = torch.zeros([2 * C + 1], device=mean.device, dtype=mean.dtype) vec = vec + input.sum() # make sure there is gradient w.r.t input else: vec = torch.cat( [mean, meansqr, torch.ones([1], device=mean.device, dtype=mean.dtype)], dim=0 ) vec = differentiable_all_reduce(vec * B) total_batch = vec[-1].detach() momentum = total_batch.clamp(max=1) * self.momentum # no update if total_batch is 0 mean, meansqr, _ = torch.split(vec / total_batch.clamp(min=1), C) # avoid div-by-zero var = meansqr - mean * mean invstd = torch.rsqrt(var + self.eps) scale = self.weight * invstd bias = self.bias - mean * scale scale = scale.reshape(1, -1, 1, 1) bias = bias.reshape(1, -1, 1, 1) self.running_mean += momentum * (mean.detach() - self.running_mean) self.running_var += momentum * (var.detach() - self.running_var) ret = input * scale + bias if half_input: ret = ret.half() return ret class CycleBatchNormList(nn.ModuleList): """ Implement domain-specific BatchNorm by cycling. When a BatchNorm layer is used for multiple input domains or input features, it might need to maintain a separate test-time statistics for each domain. See Sec 5.2 in :paper:`rethinking-batchnorm`. This module implements it by using N separate BN layers and it cycles through them every time a forward() is called. NOTE: The caller of this module MUST guarantee to always call this module by multiple of N times. Otherwise its test-time statistics will be incorrect. """ def __init__(self, length: int, bn_class=nn.BatchNorm2d, **kwargs): """ Args: length: number of BatchNorm layers to cycle. bn_class: the BatchNorm class to use kwargs: arguments of the BatchNorm class, such as num_features. """ self._affine = kwargs.pop("affine", True) super().__init__([bn_class(**kwargs, affine=False) for k in range(length)]) if self._affine: # shared affine, domain-specific BN channels = self[0].num_features self.weight = nn.Parameter(torch.ones(channels)) self.bias = nn.Parameter(torch.zeros(channels)) self._pos = 0 def forward(self, x): ret = self[self._pos](x) self._pos = (self._pos + 1) % len(self) if self._affine: w = self.weight.reshape(1, -1, 1, 1) b = self.bias.reshape(1, -1, 1, 1) return ret * w + b else: return ret def extra_repr(self): return f"affine={self._affine}" class LayerNorm(nn.Module): """ A LayerNorm variant, popularized by Transformers, that performs point-wise mean and variance normalization over the channel dimension for inputs that have shape (batch_size, channels, height, width). https://github.com/facebookresearch/ConvNeXt/blob/d1fa8f6fef0a165b27399986cc2bdacc92777e40/models/convnext.py#L119 # noqa B950 """ def __init__(self, normalized_shape, eps=1e-6): super().__init__() self.weight = nn.Parameter(torch.ones(normalized_shape)) self.bias = nn.Parameter(torch.zeros(normalized_shape)) self.eps = eps self.normalized_shape = (normalized_shape,) def forward(self, x): u = x.mean(1, keepdim=True) s = (x - u).pow(2).mean(1, keepdim=True) x = (x - u) / torch.sqrt(s + self.eps) x = self.weight[:, None, None] * x + self.bias[:, None, None] return x ================================================ FILE: detectron2/detectron2/layers/blocks.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import fvcore.nn.weight_init as weight_init from torch import nn from .batch_norm import FrozenBatchNorm2d, get_norm from .wrappers import Conv2d """ CNN building blocks. """ class CNNBlockBase(nn.Module): """ A CNN block is assumed to have input channels, output channels and a stride. The input and output of `forward()` method must be NCHW tensors. The method can perform arbitrary computation but must match the given channels and stride specification. Attribute: in_channels (int): out_channels (int): stride (int): """ def __init__(self, in_channels, out_channels, stride): """ The `__init__` method of any subclass should also contain these arguments. Args: in_channels (int): out_channels (int): stride (int): """ super().__init__() self.in_channels = in_channels self.out_channels = out_channels self.stride = stride def freeze(self): """ Make this block not trainable. This method sets all parameters to `requires_grad=False`, and convert all BatchNorm layers to FrozenBatchNorm Returns: the block itself """ for p in self.parameters(): p.requires_grad = False FrozenBatchNorm2d.convert_frozen_batchnorm(self) return self class DepthwiseSeparableConv2d(nn.Module): """ A kxk depthwise convolution + a 1x1 convolution. In :paper:`xception`, norm & activation are applied on the second conv. :paper:`mobilenet` uses norm & activation on both convs. """ def __init__( self, in_channels, out_channels, kernel_size=3, padding=1, dilation=1, *, norm1=None, activation1=None, norm2=None, activation2=None, ): """ Args: norm1, norm2 (str or callable): normalization for the two conv layers. activation1, activation2 (callable(Tensor) -> Tensor): activation function for the two conv layers. """ super().__init__() self.depthwise = Conv2d( in_channels, in_channels, kernel_size=kernel_size, padding=padding, dilation=dilation, groups=in_channels, bias=not norm1, norm=get_norm(norm1, in_channels), activation=activation1, ) self.pointwise = Conv2d( in_channels, out_channels, kernel_size=1, bias=not norm2, norm=get_norm(norm2, out_channels), activation=activation2, ) # default initialization weight_init.c2_msra_fill(self.depthwise) weight_init.c2_msra_fill(self.pointwise) def forward(self, x): return self.pointwise(self.depthwise(x)) ================================================ FILE: detectron2/detectron2/layers/csrc/README.md ================================================ To add a new Op: 1. Create a new directory 2. Implement new ops there 3. Delcare its Python interface in `vision.cpp`. ================================================ FILE: detectron2/detectron2/layers/csrc/ROIAlignRotated/ROIAlignRotated.h ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #pragma once #include namespace detectron2 { at::Tensor ROIAlignRotated_forward_cpu( const at::Tensor& input, const at::Tensor& rois, const float spatial_scale, const int pooled_height, const int pooled_width, const int sampling_ratio); at::Tensor ROIAlignRotated_backward_cpu( const at::Tensor& grad, const at::Tensor& rois, const float spatial_scale, const int pooled_height, const int pooled_width, const int batch_size, const int channels, const int height, const int width, const int sampling_ratio); #if defined(WITH_CUDA) || defined(WITH_HIP) at::Tensor ROIAlignRotated_forward_cuda( const at::Tensor& input, const at::Tensor& rois, const float spatial_scale, const int pooled_height, const int pooled_width, const int sampling_ratio); at::Tensor ROIAlignRotated_backward_cuda( const at::Tensor& grad, const at::Tensor& rois, const float spatial_scale, const int pooled_height, const int pooled_width, const int batch_size, const int channels, const int height, const int width, const int sampling_ratio); #endif // Interface for Python inline at::Tensor ROIAlignRotated_forward( const at::Tensor& input, const at::Tensor& rois, const double spatial_scale, const int64_t pooled_height, const int64_t pooled_width, const int64_t sampling_ratio) { if (input.is_cuda()) { #if defined(WITH_CUDA) || defined(WITH_HIP) return ROIAlignRotated_forward_cuda( input, rois, spatial_scale, pooled_height, pooled_width, sampling_ratio); #else AT_ERROR("Detectron2 is not compiled with GPU support!"); #endif } return ROIAlignRotated_forward_cpu( input, rois, spatial_scale, pooled_height, pooled_width, sampling_ratio); } inline at::Tensor ROIAlignRotated_backward( const at::Tensor& grad, const at::Tensor& rois, const double spatial_scale, const int64_t pooled_height, const int64_t pooled_width, const int64_t batch_size, const int64_t channels, const int64_t height, const int64_t width, const int64_t sampling_ratio) { if (grad.is_cuda()) { #if defined(WITH_CUDA) || defined(WITH_HIP) return ROIAlignRotated_backward_cuda( grad, rois, spatial_scale, pooled_height, pooled_width, batch_size, channels, height, width, sampling_ratio); #else AT_ERROR("Detectron2 is not compiled with GPU support!"); #endif } return ROIAlignRotated_backward_cpu( grad, rois, spatial_scale, pooled_height, pooled_width, batch_size, channels, height, width, sampling_ratio); } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/ROIAlignRotated/ROIAlignRotated_cpu.cpp ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #include #include "ROIAlignRotated.h" // Note: this implementation originates from the Caffe2 ROIAlignRotated Op // and PyTorch ROIAlign (non-rotated) Op implementations. // The key difference between this implementation and those ones is // we don't do "legacy offset" in this version, as there aren't many previous // works, if any, using the "legacy" ROIAlignRotated Op. // This would make the interface a bit cleaner. namespace detectron2 { namespace { template struct PreCalc { int pos1; int pos2; int pos3; int pos4; T w1; T w2; T w3; T w4; }; template void pre_calc_for_bilinear_interpolate( const int height, const int width, const int pooled_height, const int pooled_width, const int iy_upper, const int ix_upper, T roi_start_h, T roi_start_w, T bin_size_h, T bin_size_w, int roi_bin_grid_h, int roi_bin_grid_w, T roi_center_h, T roi_center_w, T cos_theta, T sin_theta, std::vector>& pre_calc) { int pre_calc_index = 0; for (int ph = 0; ph < pooled_height; ph++) { for (int pw = 0; pw < pooled_width; pw++) { for (int iy = 0; iy < iy_upper; iy++) { const T yy = roi_start_h + ph * bin_size_h + static_cast(iy + .5f) * bin_size_h / static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 for (int ix = 0; ix < ix_upper; ix++) { const T xx = roi_start_w + pw * bin_size_w + static_cast(ix + .5f) * bin_size_w / static_cast(roi_bin_grid_w); // Rotate by theta around the center and translate // In image space, (y, x) is the order for Right Handed System, // and this is essentially multiplying the point by a rotation matrix // to rotate it counterclockwise through angle theta. T y = yy * cos_theta - xx * sin_theta + roi_center_h; T x = yy * sin_theta + xx * cos_theta + roi_center_w; // deal with: inverse elements are out of feature map boundary if (y < -1.0 || y > height || x < -1.0 || x > width) { // empty PreCalc pc; pc.pos1 = 0; pc.pos2 = 0; pc.pos3 = 0; pc.pos4 = 0; pc.w1 = 0; pc.w2 = 0; pc.w3 = 0; pc.w4 = 0; pre_calc[pre_calc_index] = pc; pre_calc_index += 1; continue; } if (y < 0) { y = 0; } if (x < 0) { x = 0; } int y_low = (int)y; int x_low = (int)x; int y_high; int x_high; if (y_low >= height - 1) { y_high = y_low = height - 1; y = (T)y_low; } else { y_high = y_low + 1; } if (x_low >= width - 1) { x_high = x_low = width - 1; x = (T)x_low; } else { x_high = x_low + 1; } T ly = y - y_low; T lx = x - x_low; T hy = 1. - ly, hx = 1. - lx; T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; // save weights and indices PreCalc pc; pc.pos1 = y_low * width + x_low; pc.pos2 = y_low * width + x_high; pc.pos3 = y_high * width + x_low; pc.pos4 = y_high * width + x_high; pc.w1 = w1; pc.w2 = w2; pc.w3 = w3; pc.w4 = w4; pre_calc[pre_calc_index] = pc; pre_calc_index += 1; } } } } } template void bilinear_interpolate_gradient( const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, int& x_low, int& x_high, int& y_low, int& y_high) { // deal with cases that inverse elements are out of feature map boundary if (y < -1.0 || y > height || x < -1.0 || x > width) { // empty w1 = w2 = w3 = w4 = 0.; x_low = x_high = y_low = y_high = -1; return; } if (y < 0) { y = 0; } if (x < 0) { x = 0; } y_low = (int)y; x_low = (int)x; if (y_low >= height - 1) { y_high = y_low = height - 1; y = (T)y_low; } else { y_high = y_low + 1; } if (x_low >= width - 1) { x_high = x_low = width - 1; x = (T)x_low; } else { x_high = x_low + 1; } T ly = y - y_low; T lx = x - x_low; T hy = 1. - ly, hx = 1. - lx; // reference in forward // T v1 = input[y_low * width + x_low]; // T v2 = input[y_low * width + x_high]; // T v3 = input[y_high * width + x_low]; // T v4 = input[y_high * width + x_high]; // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; return; } template inline void add(T* address, const T& val) { *address += val; } } // namespace template void ROIAlignRotatedForward( const int nthreads, const T* input, const T& spatial_scale, const int channels, const int height, const int width, const int pooled_height, const int pooled_width, const int sampling_ratio, const T* rois, T* output) { int n_rois = nthreads / channels / pooled_width / pooled_height; // (n, c, ph, pw) is an element in the pooled output // can be parallelized using omp // #pragma omp parallel for num_threads(32) for (int n = 0; n < n_rois; n++) { int index_n = n * channels * pooled_width * pooled_height; const T* current_roi = rois + n * 6; int roi_batch_ind = current_roi[0]; // Do not use rounding; this implementation detail is critical // ROIAlignRotated supports align == true, i.e., continuous coordinate // by default, thus the 0.5 offset T offset = (T)0.5; T roi_center_w = current_roi[1] * spatial_scale - offset; T roi_center_h = current_roi[2] * spatial_scale - offset; T roi_width = current_roi[3] * spatial_scale; T roi_height = current_roi[4] * spatial_scale; T theta = current_roi[5] * M_PI / 180.0; T cos_theta = cos(theta); T sin_theta = sin(theta); AT_ASSERTM( roi_width >= 0 && roi_height >= 0, "ROIs in ROIAlignRotated do not have non-negative size!"); T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); // We use roi_bin_grid to sample the grid and mimic integral int roi_bin_grid_h = (sampling_ratio > 0) ? sampling_ratio : ceil(roi_height / pooled_height); // e.g., = 2 int roi_bin_grid_w = (sampling_ratio > 0) ? sampling_ratio : ceil(roi_width / pooled_width); // We do average (integral) pooling inside a bin const T count = std::max(roi_bin_grid_h * roi_bin_grid_w, 1); // e.g. = 4 // we want to precalculate indices and weights shared by all channels, // this is the key point of optimization std::vector> pre_calc( roi_bin_grid_h * roi_bin_grid_w * pooled_width * pooled_height); // roi_start_h and roi_start_w are computed wrt the center of RoI (x, y). // Appropriate translation needs to be applied after. T roi_start_h = -roi_height / 2.0; T roi_start_w = -roi_width / 2.0; pre_calc_for_bilinear_interpolate( height, width, pooled_height, pooled_width, roi_bin_grid_h, roi_bin_grid_w, roi_start_h, roi_start_w, bin_size_h, bin_size_w, roi_bin_grid_h, roi_bin_grid_w, roi_center_h, roi_center_w, cos_theta, sin_theta, pre_calc); for (int c = 0; c < channels; c++) { int index_n_c = index_n + c * pooled_width * pooled_height; const T* offset_input = input + (roi_batch_ind * channels + c) * height * width; int pre_calc_index = 0; for (int ph = 0; ph < pooled_height; ph++) { for (int pw = 0; pw < pooled_width; pw++) { int index = index_n_c + ph * pooled_width + pw; T output_val = 0.; for (int iy = 0; iy < roi_bin_grid_h; iy++) { for (int ix = 0; ix < roi_bin_grid_w; ix++) { PreCalc pc = pre_calc[pre_calc_index]; output_val += pc.w1 * offset_input[pc.pos1] + pc.w2 * offset_input[pc.pos2] + pc.w3 * offset_input[pc.pos3] + pc.w4 * offset_input[pc.pos4]; pre_calc_index += 1; } } output_val /= count; output[index] = output_val; } // for pw } // for ph } // for c } // for n } template void ROIAlignRotatedBackward( const int nthreads, // may not be contiguous. should index using n_stride, etc const T* grad_output, const T& spatial_scale, const int channels, const int height, const int width, const int pooled_height, const int pooled_width, const int sampling_ratio, T* grad_input, const T* rois, const int n_stride, const int c_stride, const int h_stride, const int w_stride) { for (int index = 0; index < nthreads; index++) { // (n, c, ph, pw) is an element in the pooled output int pw = index % pooled_width; int ph = (index / pooled_width) % pooled_height; int c = (index / pooled_width / pooled_height) % channels; int n = index / pooled_width / pooled_height / channels; const T* current_roi = rois + n * 6; int roi_batch_ind = current_roi[0]; // Do not use rounding; this implementation detail is critical // ROIAlignRotated supports align == true, i.e., continuous coordinate // by default, thus the 0.5 offset T offset = (T)0.5; T roi_center_w = current_roi[1] * spatial_scale - offset; T roi_center_h = current_roi[2] * spatial_scale - offset; T roi_width = current_roi[3] * spatial_scale; T roi_height = current_roi[4] * spatial_scale; T theta = current_roi[5] * M_PI / 180.0; T cos_theta = cos(theta); T sin_theta = sin(theta); AT_ASSERTM( roi_width >= 0 && roi_height >= 0, "ROIs in ROIAlignRotated do not have non-negative size!"); T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); T* offset_grad_input = grad_input + ((roi_batch_ind * channels + c) * height * width); int output_offset = n * n_stride + c * c_stride; const T* offset_grad_output = grad_output + output_offset; const T grad_output_this_bin = offset_grad_output[ph * h_stride + pw * w_stride]; // We use roi_bin_grid to sample the grid and mimic integral int roi_bin_grid_h = (sampling_ratio > 0) ? sampling_ratio : ceil(roi_height / pooled_height); // e.g., = 2 int roi_bin_grid_w = (sampling_ratio > 0) ? sampling_ratio : ceil(roi_width / pooled_width); // roi_start_h and roi_start_w are computed wrt the center of RoI (x, y). // Appropriate translation needs to be applied after. T roi_start_h = -roi_height / 2.0; T roi_start_w = -roi_width / 2.0; // We do average (integral) pooling inside a bin const T count = roi_bin_grid_h * roi_bin_grid_w; // e.g. = 4 for (int iy = 0; iy < roi_bin_grid_h; iy++) { const T yy = roi_start_h + ph * bin_size_h + static_cast(iy + .5f) * bin_size_h / static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 for (int ix = 0; ix < roi_bin_grid_w; ix++) { const T xx = roi_start_w + pw * bin_size_w + static_cast(ix + .5f) * bin_size_w / static_cast(roi_bin_grid_w); // Rotate by theta around the center and translate T y = yy * cos_theta - xx * sin_theta + roi_center_h; T x = yy * sin_theta + xx * cos_theta + roi_center_w; T w1, w2, w3, w4; int x_low, x_high, y_low, y_high; bilinear_interpolate_gradient( height, width, y, x, w1, w2, w3, w4, x_low, x_high, y_low, y_high); T g1 = grad_output_this_bin * w1 / count; T g2 = grad_output_this_bin * w2 / count; T g3 = grad_output_this_bin * w3 / count; T g4 = grad_output_this_bin * w4 / count; if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { // atomic add is not needed for now since it is single threaded add(offset_grad_input + y_low * width + x_low, static_cast(g1)); add(offset_grad_input + y_low * width + x_high, static_cast(g2)); add(offset_grad_input + y_high * width + x_low, static_cast(g3)); add(offset_grad_input + y_high * width + x_high, static_cast(g4)); } // if } // ix } // iy } // for } // ROIAlignRotatedBackward at::Tensor ROIAlignRotated_forward_cpu( const at::Tensor& input, const at::Tensor& rois, const float spatial_scale, const int pooled_height, const int pooled_width, const int sampling_ratio) { AT_ASSERTM(input.device().is_cpu(), "input must be a CPU tensor"); AT_ASSERTM(rois.device().is_cpu(), "rois must be a CPU tensor"); at::TensorArg input_t{input, "input", 1}, rois_t{rois, "rois", 2}; at::CheckedFrom c = "ROIAlign_forward_cpu"; at::checkAllSameType(c, {input_t, rois_t}); auto num_rois = rois.size(0); auto channels = input.size(1); auto height = input.size(2); auto width = input.size(3); at::Tensor output = at::zeros( {num_rois, channels, pooled_height, pooled_width}, input.options()); auto output_size = num_rois * pooled_height * pooled_width * channels; if (output.numel() == 0) { return output; } auto input_ = input.contiguous(), rois_ = rois.contiguous(); AT_DISPATCH_FLOATING_TYPES_AND_HALF( input.scalar_type(), "ROIAlignRotated_forward", [&] { ROIAlignRotatedForward( output_size, input_.data_ptr(), spatial_scale, channels, height, width, pooled_height, pooled_width, sampling_ratio, rois_.data_ptr(), output.data_ptr()); }); return output; } at::Tensor ROIAlignRotated_backward_cpu( const at::Tensor& grad, const at::Tensor& rois, const float spatial_scale, const int pooled_height, const int pooled_width, const int batch_size, const int channels, const int height, const int width, const int sampling_ratio) { AT_ASSERTM(grad.device().is_cpu(), "grad must be a CPU tensor"); AT_ASSERTM(rois.device().is_cpu(), "rois must be a CPU tensor"); at::TensorArg grad_t{grad, "grad", 1}, rois_t{rois, "rois", 2}; at::CheckedFrom c = "ROIAlignRotated_backward_cpu"; at::checkAllSameType(c, {grad_t, rois_t}); at::Tensor grad_input = at::zeros({batch_size, channels, height, width}, grad.options()); // handle possibly empty gradients if (grad.numel() == 0) { return grad_input; } // get stride values to ensure indexing into gradients is correct. int n_stride = grad.stride(0); int c_stride = grad.stride(1); int h_stride = grad.stride(2); int w_stride = grad.stride(3); auto rois_ = rois.contiguous(); AT_DISPATCH_FLOATING_TYPES_AND_HALF( grad.scalar_type(), "ROIAlignRotated_forward", [&] { ROIAlignRotatedBackward( grad.numel(), grad.data_ptr(), spatial_scale, channels, height, width, pooled_height, pooled_width, sampling_ratio, grad_input.data_ptr(), rois_.data_ptr(), n_stride, c_stride, h_stride, w_stride); }); return grad_input; } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/ROIAlignRotated/ROIAlignRotated_cuda.cu ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #include #include #include #include // TODO make it in a common file #define CUDA_1D_KERNEL_LOOP(i, n) \ for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < n; \ i += blockDim.x * gridDim.x) // Note: this implementation originates from the Caffe2 ROIAlignRotated Op // and PyTorch ROIAlign (non-rotated) Op implementations. // The key difference between this implementation and those ones is // we don't do "legacy offset" in this version, as there aren't many previous // works, if any, using the "legacy" ROIAlignRotated Op. // This would make the interface a bit cleaner. namespace detectron2 { namespace { template __device__ T bilinear_interpolate( const T* input, const int height, const int width, T y, T x) { // deal with cases that inverse elements are out of feature map boundary if (y < -1.0 || y > height || x < -1.0 || x > width) { // empty return 0; } if (y < 0) { y = 0; } if (x < 0) { x = 0; } int y_low = (int)y; int x_low = (int)x; int y_high; int x_high; if (y_low >= height - 1) { y_high = y_low = height - 1; y = (T)y_low; } else { y_high = y_low + 1; } if (x_low >= width - 1) { x_high = x_low = width - 1; x = (T)x_low; } else { x_high = x_low + 1; } T ly = y - y_low; T lx = x - x_low; T hy = 1. - ly, hx = 1. - lx; // do bilinear interpolation T v1 = input[y_low * width + x_low]; T v2 = input[y_low * width + x_high]; T v3 = input[y_high * width + x_low]; T v4 = input[y_high * width + x_high]; T w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); return val; } template __device__ void bilinear_interpolate_gradient( const int height, const int width, T y, T x, T& w1, T& w2, T& w3, T& w4, int& x_low, int& x_high, int& y_low, int& y_high) { // deal with cases that inverse elements are out of feature map boundary if (y < -1.0 || y > height || x < -1.0 || x > width) { // empty w1 = w2 = w3 = w4 = 0.; x_low = x_high = y_low = y_high = -1; return; } if (y < 0) { y = 0; } if (x < 0) { x = 0; } y_low = (int)y; x_low = (int)x; if (y_low >= height - 1) { y_high = y_low = height - 1; y = (T)y_low; } else { y_high = y_low + 1; } if (x_low >= width - 1) { x_high = x_low = width - 1; x = (T)x_low; } else { x_high = x_low + 1; } T ly = y - y_low; T lx = x - x_low; T hy = 1. - ly, hx = 1. - lx; // reference in forward // T v1 = input[y_low * width + x_low]; // T v2 = input[y_low * width + x_high]; // T v3 = input[y_high * width + x_low]; // T v4 = input[y_high * width + x_high]; // T val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); w1 = hy * hx, w2 = hy * lx, w3 = ly * hx, w4 = ly * lx; return; } } // namespace template __global__ void RoIAlignRotatedForward( const int nthreads, const T* input, const T spatial_scale, const int channels, const int height, const int width, const int pooled_height, const int pooled_width, const int sampling_ratio, const T* rois, T* top_data) { CUDA_1D_KERNEL_LOOP(index, nthreads) { // (n, c, ph, pw) is an element in the pooled output int pw = index % pooled_width; int ph = (index / pooled_width) % pooled_height; int c = (index / pooled_width / pooled_height) % channels; int n = index / pooled_width / pooled_height / channels; const T* current_roi = rois + n * 6; int roi_batch_ind = current_roi[0]; // Do not use rounding; this implementation detail is critical // ROIAlignRotated supports align == true, i.e., continuous coordinate // by default, thus the 0.5 offset T offset = (T)0.5; T roi_center_w = current_roi[1] * spatial_scale - offset; T roi_center_h = current_roi[2] * spatial_scale - offset; T roi_width = current_roi[3] * spatial_scale; T roi_height = current_roi[4] * spatial_scale; T theta = current_roi[5] * M_PI / 180.0; T cos_theta = cos(theta); T sin_theta = sin(theta); T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); const T* offset_input = input + (roi_batch_ind * channels + c) * height * width; // We use roi_bin_grid to sample the grid and mimic integral int roi_bin_grid_h = (sampling_ratio > 0) ? sampling_ratio : ceil(roi_height / pooled_height); // e.g., = 2 int roi_bin_grid_w = (sampling_ratio > 0) ? sampling_ratio : ceil(roi_width / pooled_width); // roi_start_h and roi_start_w are computed wrt the center of RoI (x, y). // Appropriate translation needs to be applied after. T roi_start_h = -roi_height / 2.0; T roi_start_w = -roi_width / 2.0; // We do average (inte gral) pooling inside a bin const T count = max(roi_bin_grid_h * roi_bin_grid_w, 1); // e.g. = 4 T output_val = 0.; for (int iy = 0; iy < roi_bin_grid_h; iy++) // e.g., iy = 0, 1 { const T yy = roi_start_h + ph * bin_size_h + static_cast(iy + .5f) * bin_size_h / static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 for (int ix = 0; ix < roi_bin_grid_w; ix++) { const T xx = roi_start_w + pw * bin_size_w + static_cast(ix + .5f) * bin_size_w / static_cast(roi_bin_grid_w); // Rotate by theta around the center and translate T y = yy * cos_theta - xx * sin_theta + roi_center_h; T x = yy * sin_theta + xx * cos_theta + roi_center_w; T val = bilinear_interpolate(offset_input, height, width, y, x); output_val += val; } } output_val /= count; top_data[index] = output_val; } } template __global__ void RoIAlignRotatedBackwardFeature( const int nthreads, const T* top_diff, const int num_rois, const T spatial_scale, const int channels, const int height, const int width, const int pooled_height, const int pooled_width, const int sampling_ratio, T* bottom_diff, const T* rois) { CUDA_1D_KERNEL_LOOP(index, nthreads) { // (n, c, ph, pw) is an element in the pooled output int pw = index % pooled_width; int ph = (index / pooled_width) % pooled_height; int c = (index / pooled_width / pooled_height) % channels; int n = index / pooled_width / pooled_height / channels; const T* current_roi = rois + n * 6; int roi_batch_ind = current_roi[0]; // Do not use rounding; this implementation detail is critical // ROIAlignRotated supports align == true, i.e., continuous coordinate // by default, thus the 0.5 offset T offset = (T)0.5; T roi_center_w = current_roi[1] * spatial_scale - offset; T roi_center_h = current_roi[2] * spatial_scale - offset; T roi_width = current_roi[3] * spatial_scale; T roi_height = current_roi[4] * spatial_scale; T theta = current_roi[5] * M_PI / 180.0; T cos_theta = cos(theta); T sin_theta = sin(theta); T bin_size_h = static_cast(roi_height) / static_cast(pooled_height); T bin_size_w = static_cast(roi_width) / static_cast(pooled_width); T* offset_bottom_diff = bottom_diff + (roi_batch_ind * channels + c) * height * width; int top_offset = (n * channels + c) * pooled_height * pooled_width; const T* offset_top_diff = top_diff + top_offset; const T top_diff_this_bin = offset_top_diff[ph * pooled_width + pw]; // We use roi_bin_grid to sample the grid and mimic integral int roi_bin_grid_h = (sampling_ratio > 0) ? sampling_ratio : ceil(roi_height / pooled_height); // e.g., = 2 int roi_bin_grid_w = (sampling_ratio > 0) ? sampling_ratio : ceil(roi_width / pooled_width); // roi_start_h and roi_start_w are computed wrt the center of RoI (x, y). // Appropriate translation needs to be applied after. T roi_start_h = -roi_height / 2.0; T roi_start_w = -roi_width / 2.0; // We do average (integral) pooling inside a bin const T count = roi_bin_grid_h * roi_bin_grid_w; // e.g. = 4 for (int iy = 0; iy < roi_bin_grid_h; iy++) // e.g., iy = 0, 1 { const T yy = roi_start_h + ph * bin_size_h + static_cast(iy + .5f) * bin_size_h / static_cast(roi_bin_grid_h); // e.g., 0.5, 1.5 for (int ix = 0; ix < roi_bin_grid_w; ix++) { const T xx = roi_start_w + pw * bin_size_w + static_cast(ix + .5f) * bin_size_w / static_cast(roi_bin_grid_w); // Rotate by theta around the center and translate T y = yy * cos_theta - xx * sin_theta + roi_center_h; T x = yy * sin_theta + xx * cos_theta + roi_center_w; T w1, w2, w3, w4; int x_low, x_high, y_low, y_high; bilinear_interpolate_gradient( height, width, y, x, w1, w2, w3, w4, x_low, x_high, y_low, y_high); T g1 = top_diff_this_bin * w1 / count; T g2 = top_diff_this_bin * w2 / count; T g3 = top_diff_this_bin * w3 / count; T g4 = top_diff_this_bin * w4 / count; if (x_low >= 0 && x_high >= 0 && y_low >= 0 && y_high >= 0) { atomicAdd( offset_bottom_diff + y_low * width + x_low, static_cast(g1)); atomicAdd( offset_bottom_diff + y_low * width + x_high, static_cast(g2)); atomicAdd( offset_bottom_diff + y_high * width + x_low, static_cast(g3)); atomicAdd( offset_bottom_diff + y_high * width + x_high, static_cast(g4)); } // if } // ix } // iy } // CUDA_1D_KERNEL_LOOP } // RoIAlignRotatedBackward at::Tensor ROIAlignRotated_forward_cuda( const at::Tensor& input, const at::Tensor& rois, const float spatial_scale, const int pooled_height, const int pooled_width, const int sampling_ratio) { AT_ASSERTM(input.device().is_cuda(), "input must be a CUDA tensor"); AT_ASSERTM(rois.device().is_cuda(), "rois must be a CUDA tensor"); at::TensorArg input_t{input, "input", 1}, rois_t{rois, "rois", 2}; at::CheckedFrom c = "ROIAlignRotated_forward_cuda"; at::checkAllSameGPU(c, {input_t, rois_t}); at::checkAllSameType(c, {input_t, rois_t}); at::cuda::CUDAGuard device_guard(input.device()); auto num_rois = rois.size(0); auto channels = input.size(1); auto height = input.size(2); auto width = input.size(3); auto output = at::empty( {num_rois, channels, pooled_height, pooled_width}, input.options()); auto output_size = num_rois * pooled_height * pooled_width * channels; cudaStream_t stream = at::cuda::getCurrentCUDAStream(); dim3 grid(std::min( at::cuda::ATenCeilDiv( static_cast(output_size), static_cast(512)), static_cast(4096))); dim3 block(512); if (output.numel() == 0) { AT_CUDA_CHECK(cudaGetLastError()); return output; } auto input_ = input.contiguous(), rois_ = rois.contiguous(); AT_DISPATCH_FLOATING_TYPES( input.scalar_type(), "ROIAlignRotated_forward", [&] { RoIAlignRotatedForward<<>>( output_size, input_.data_ptr(), spatial_scale, channels, height, width, pooled_height, pooled_width, sampling_ratio, rois_.data_ptr(), output.data_ptr()); }); cudaDeviceSynchronize(); AT_CUDA_CHECK(cudaGetLastError()); return output; } // TODO remove the dependency on input and use instead its sizes -> save memory at::Tensor ROIAlignRotated_backward_cuda( const at::Tensor& grad, const at::Tensor& rois, const float spatial_scale, const int pooled_height, const int pooled_width, const int batch_size, const int channels, const int height, const int width, const int sampling_ratio) { AT_ASSERTM(grad.device().is_cuda(), "grad must be a CUDA tensor"); AT_ASSERTM(rois.device().is_cuda(), "rois must be a CUDA tensor"); at::TensorArg grad_t{grad, "grad", 1}, rois_t{rois, "rois", 2}; at::CheckedFrom c = "ROIAlign_backward_cuda"; at::checkAllSameGPU(c, {grad_t, rois_t}); at::checkAllSameType(c, {grad_t, rois_t}); at::cuda::CUDAGuard device_guard(grad.device()); auto num_rois = rois.size(0); auto grad_input = at::zeros({batch_size, channels, height, width}, grad.options()); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); dim3 grid(std::min( at::cuda::ATenCeilDiv( static_cast(grad.numel()), static_cast(512)), static_cast(4096))); dim3 block(512); // handle possibly empty gradients if (grad.numel() == 0) { AT_CUDA_CHECK(cudaGetLastError()); return grad_input; } auto grad_ = grad.contiguous(), rois_ = rois.contiguous(); AT_DISPATCH_FLOATING_TYPES( grad.scalar_type(), "ROIAlignRotated_backward", [&] { RoIAlignRotatedBackwardFeature<<>>( grad.numel(), grad_.data_ptr(), num_rois, spatial_scale, channels, height, width, pooled_height, pooled_width, sampling_ratio, grad_input.data_ptr(), rois_.data_ptr()); }); AT_CUDA_CHECK(cudaGetLastError()); return grad_input; } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/box_iou_rotated/box_iou_rotated.h ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #pragma once #include namespace detectron2 { at::Tensor box_iou_rotated_cpu( const at::Tensor& boxes1, const at::Tensor& boxes2); #if defined(WITH_CUDA) || defined(WITH_HIP) at::Tensor box_iou_rotated_cuda( const at::Tensor& boxes1, const at::Tensor& boxes2); #endif // Interface for Python // inline is needed to prevent multiple function definitions when this header is // included by different cpps inline at::Tensor box_iou_rotated( const at::Tensor& boxes1, const at::Tensor& boxes2) { assert(boxes1.device().is_cuda() == boxes2.device().is_cuda()); if (boxes1.device().is_cuda()) { #if defined(WITH_CUDA) || defined(WITH_HIP) return box_iou_rotated_cuda(boxes1.contiguous(), boxes2.contiguous()); #else AT_ERROR("Detectron2 is not compiled with GPU support!"); #endif } return box_iou_rotated_cpu(boxes1.contiguous(), boxes2.contiguous()); } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/box_iou_rotated/box_iou_rotated_cpu.cpp ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #include "box_iou_rotated.h" #include "box_iou_rotated_utils.h" namespace detectron2 { template void box_iou_rotated_cpu_kernel( const at::Tensor& boxes1, const at::Tensor& boxes2, at::Tensor& ious) { auto num_boxes1 = boxes1.size(0); auto num_boxes2 = boxes2.size(0); for (int i = 0; i < num_boxes1; i++) { for (int j = 0; j < num_boxes2; j++) { ious[i * num_boxes2 + j] = single_box_iou_rotated( boxes1[i].data_ptr(), boxes2[j].data_ptr()); } } } at::Tensor box_iou_rotated_cpu( // input must be contiguous: const at::Tensor& boxes1, const at::Tensor& boxes2) { auto num_boxes1 = boxes1.size(0); auto num_boxes2 = boxes2.size(0); at::Tensor ious = at::empty({num_boxes1 * num_boxes2}, boxes1.options().dtype(at::kFloat)); box_iou_rotated_cpu_kernel(boxes1, boxes2, ious); // reshape from 1d array to 2d array auto shape = std::vector{num_boxes1, num_boxes2}; return ious.reshape(shape); } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/box_iou_rotated/box_iou_rotated_cuda.cu ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #include #include #include #include #include "box_iou_rotated_utils.h" namespace detectron2 { // 2D block with 32 * 16 = 512 threads per block const int BLOCK_DIM_X = 32; const int BLOCK_DIM_Y = 16; template __global__ void box_iou_rotated_cuda_kernel( const int n_boxes1, const int n_boxes2, const T* dev_boxes1, const T* dev_boxes2, T* dev_ious) { const int row_start = blockIdx.x * blockDim.x; const int col_start = blockIdx.y * blockDim.y; const int row_size = min(n_boxes1 - row_start, blockDim.x); const int col_size = min(n_boxes2 - col_start, blockDim.y); __shared__ float block_boxes1[BLOCK_DIM_X * 5]; __shared__ float block_boxes2[BLOCK_DIM_Y * 5]; // It's safe to copy using threadIdx.x since BLOCK_DIM_X >= BLOCK_DIM_Y if (threadIdx.x < row_size && threadIdx.y == 0) { block_boxes1[threadIdx.x * 5 + 0] = dev_boxes1[(row_start + threadIdx.x) * 5 + 0]; block_boxes1[threadIdx.x * 5 + 1] = dev_boxes1[(row_start + threadIdx.x) * 5 + 1]; block_boxes1[threadIdx.x * 5 + 2] = dev_boxes1[(row_start + threadIdx.x) * 5 + 2]; block_boxes1[threadIdx.x * 5 + 3] = dev_boxes1[(row_start + threadIdx.x) * 5 + 3]; block_boxes1[threadIdx.x * 5 + 4] = dev_boxes1[(row_start + threadIdx.x) * 5 + 4]; } if (threadIdx.x < col_size && threadIdx.y == 0) { block_boxes2[threadIdx.x * 5 + 0] = dev_boxes2[(col_start + threadIdx.x) * 5 + 0]; block_boxes2[threadIdx.x * 5 + 1] = dev_boxes2[(col_start + threadIdx.x) * 5 + 1]; block_boxes2[threadIdx.x * 5 + 2] = dev_boxes2[(col_start + threadIdx.x) * 5 + 2]; block_boxes2[threadIdx.x * 5 + 3] = dev_boxes2[(col_start + threadIdx.x) * 5 + 3]; block_boxes2[threadIdx.x * 5 + 4] = dev_boxes2[(col_start + threadIdx.x) * 5 + 4]; } __syncthreads(); if (threadIdx.x < row_size && threadIdx.y < col_size) { int offset = (row_start + threadIdx.x) * n_boxes2 + col_start + threadIdx.y; dev_ious[offset] = single_box_iou_rotated( block_boxes1 + threadIdx.x * 5, block_boxes2 + threadIdx.y * 5); } } at::Tensor box_iou_rotated_cuda( // input must be contiguous const at::Tensor& boxes1, const at::Tensor& boxes2) { using scalar_t = float; AT_ASSERTM( boxes1.scalar_type() == at::kFloat, "boxes1 must be a float tensor"); AT_ASSERTM( boxes2.scalar_type() == at::kFloat, "boxes2 must be a float tensor"); AT_ASSERTM(boxes1.is_cuda(), "boxes1 must be a CUDA tensor"); AT_ASSERTM(boxes2.is_cuda(), "boxes2 must be a CUDA tensor"); at::cuda::CUDAGuard device_guard(boxes1.device()); auto num_boxes1 = boxes1.size(0); auto num_boxes2 = boxes2.size(0); at::Tensor ious = at::empty({num_boxes1 * num_boxes2}, boxes1.options().dtype(at::kFloat)); bool transpose = false; if (num_boxes1 > 0 && num_boxes2 > 0) { scalar_t *data1 = boxes1.data_ptr(), *data2 = boxes2.data_ptr(); if (num_boxes2 > 65535 * BLOCK_DIM_Y) { AT_ASSERTM( num_boxes1 <= 65535 * BLOCK_DIM_Y, "Too many boxes for box_iou_rotated_cuda!"); // x dim is allowed to be large, but y dim cannot, // so we transpose the two to avoid "invalid configuration argument" // error. We assume one of them is small. Otherwise the result is hard to // fit in memory anyway. std::swap(num_boxes1, num_boxes2); std::swap(data1, data2); transpose = true; } const int blocks_x = at::cuda::ATenCeilDiv(static_cast(num_boxes1), BLOCK_DIM_X); const int blocks_y = at::cuda::ATenCeilDiv(static_cast(num_boxes2), BLOCK_DIM_Y); dim3 blocks(blocks_x, blocks_y); dim3 threads(BLOCK_DIM_X, BLOCK_DIM_Y); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); box_iou_rotated_cuda_kernel<<>>( num_boxes1, num_boxes2, data1, data2, (scalar_t*)ious.data_ptr()); AT_CUDA_CHECK(cudaGetLastError()); } // reshape from 1d array to 2d array auto shape = std::vector{num_boxes1, num_boxes2}; if (transpose) { return ious.view(shape).t(); } else { return ious.view(shape); } } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/box_iou_rotated/box_iou_rotated_utils.h ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #pragma once #include #include #if defined(__CUDACC__) || __HCC__ == 1 || __HIP__ == 1 // Designates functions callable from the host (CPU) and the device (GPU) #define HOST_DEVICE __host__ __device__ #define HOST_DEVICE_INLINE HOST_DEVICE __forceinline__ #else #include #define HOST_DEVICE #define HOST_DEVICE_INLINE HOST_DEVICE inline #endif namespace detectron2 { namespace { template struct RotatedBox { T x_ctr, y_ctr, w, h, a; }; template struct Point { T x, y; HOST_DEVICE_INLINE Point(const T& px = 0, const T& py = 0) : x(px), y(py) {} HOST_DEVICE_INLINE Point operator+(const Point& p) const { return Point(x + p.x, y + p.y); } HOST_DEVICE_INLINE Point& operator+=(const Point& p) { x += p.x; y += p.y; return *this; } HOST_DEVICE_INLINE Point operator-(const Point& p) const { return Point(x - p.x, y - p.y); } HOST_DEVICE_INLINE Point operator*(const T coeff) const { return Point(x * coeff, y * coeff); } }; template HOST_DEVICE_INLINE T dot_2d(const Point& A, const Point& B) { return A.x * B.x + A.y * B.y; } // R: result type. can be different from input type template HOST_DEVICE_INLINE R cross_2d(const Point& A, const Point& B) { return static_cast(A.x) * static_cast(B.y) - static_cast(B.x) * static_cast(A.y); } template HOST_DEVICE_INLINE void get_rotated_vertices( const RotatedBox& box, Point (&pts)[4]) { // M_PI / 180. == 0.01745329251 double theta = box.a * 0.01745329251; T cosTheta2 = (T)cos(theta) * 0.5f; T sinTheta2 = (T)sin(theta) * 0.5f; // y: top --> down; x: left --> right pts[0].x = box.x_ctr + sinTheta2 * box.h + cosTheta2 * box.w; pts[0].y = box.y_ctr + cosTheta2 * box.h - sinTheta2 * box.w; pts[1].x = box.x_ctr - sinTheta2 * box.h + cosTheta2 * box.w; pts[1].y = box.y_ctr - cosTheta2 * box.h - sinTheta2 * box.w; pts[2].x = 2 * box.x_ctr - pts[0].x; pts[2].y = 2 * box.y_ctr - pts[0].y; pts[3].x = 2 * box.x_ctr - pts[1].x; pts[3].y = 2 * box.y_ctr - pts[1].y; } template HOST_DEVICE_INLINE int get_intersection_points( const Point (&pts1)[4], const Point (&pts2)[4], Point (&intersections)[24]) { // Line vector // A line from p1 to p2 is: p1 + (p2-p1)*t, t=[0,1] Point vec1[4], vec2[4]; for (int i = 0; i < 4; i++) { vec1[i] = pts1[(i + 1) % 4] - pts1[i]; vec2[i] = pts2[(i + 1) % 4] - pts2[i]; } // When computing the intersection area, it doesn't hurt if we have // more (duplicated/approximate) intersections/vertices than needed, // while it can cause drastic difference if we miss an intersection/vertex. // Therefore, we add an epsilon to relax the comparisons between // the float point numbers that decide the intersection points. double EPS = 1e-5; // Line test - test all line combos for intersection int num = 0; // number of intersections for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { // Solve for 2x2 Ax=b T det = cross_2d(vec2[j], vec1[i]); // This takes care of parallel lines if (fabs(det) <= 1e-14) { continue; } auto vec12 = pts2[j] - pts1[i]; T t1 = cross_2d(vec2[j], vec12) / det; T t2 = cross_2d(vec1[i], vec12) / det; if (t1 > -EPS && t1 < 1.0f + EPS && t2 > -EPS && t2 < 1.0f + EPS) { intersections[num++] = pts1[i] + vec1[i] * t1; } } } // Check for vertices of rect1 inside rect2 { const auto& AB = vec2[0]; const auto& DA = vec2[3]; auto ABdotAB = dot_2d(AB, AB); auto ADdotAD = dot_2d(DA, DA); for (int i = 0; i < 4; i++) { // assume ABCD is the rectangle, and P is the point to be judged // P is inside ABCD iff. P's projection on AB lies within AB // and P's projection on AD lies within AD auto AP = pts1[i] - pts2[0]; auto APdotAB = dot_2d(AP, AB); auto APdotAD = -dot_2d(AP, DA); if ((APdotAB > -EPS) && (APdotAD > -EPS) && (APdotAB < ABdotAB + EPS) && (APdotAD < ADdotAD + EPS)) { intersections[num++] = pts1[i]; } } } // Reverse the check - check for vertices of rect2 inside rect1 { const auto& AB = vec1[0]; const auto& DA = vec1[3]; auto ABdotAB = dot_2d(AB, AB); auto ADdotAD = dot_2d(DA, DA); for (int i = 0; i < 4; i++) { auto AP = pts2[i] - pts1[0]; auto APdotAB = dot_2d(AP, AB); auto APdotAD = -dot_2d(AP, DA); if ((APdotAB > -EPS) && (APdotAD > -EPS) && (APdotAB < ABdotAB + EPS) && (APdotAD < ADdotAD + EPS)) { intersections[num++] = pts2[i]; } } } return num; } template HOST_DEVICE_INLINE int convex_hull_graham( const Point (&p)[24], const int& num_in, Point (&q)[24], bool shift_to_zero = false) { assert(num_in >= 2); // Step 1: // Find point with minimum y // if more than 1 points have the same minimum y, // pick the one with the minimum x. int t = 0; for (int i = 1; i < num_in; i++) { if (p[i].y < p[t].y || (p[i].y == p[t].y && p[i].x < p[t].x)) { t = i; } } auto& start = p[t]; // starting point // Step 2: // Subtract starting point from every points (for sorting in the next step) for (int i = 0; i < num_in; i++) { q[i] = p[i] - start; } // Swap the starting point to position 0 auto tmp = q[0]; q[0] = q[t]; q[t] = tmp; // Step 3: // Sort point 1 ~ num_in according to their relative cross-product values // (essentially sorting according to angles) // If the angles are the same, sort according to their distance to origin T dist[24]; #if defined(__CUDACC__) || __HCC__ == 1 || __HIP__ == 1 // compute distance to origin before sort, and sort them together with the // points for (int i = 0; i < num_in; i++) { dist[i] = dot_2d(q[i], q[i]); } // CUDA version // In the future, we can potentially use thrust // for sorting here to improve speed (though not guaranteed) for (int i = 1; i < num_in - 1; i++) { for (int j = i + 1; j < num_in; j++) { T crossProduct = cross_2d(q[i], q[j]); if ((crossProduct < -1e-6) || (fabs(crossProduct) < 1e-6 && dist[i] > dist[j])) { auto q_tmp = q[i]; q[i] = q[j]; q[j] = q_tmp; auto dist_tmp = dist[i]; dist[i] = dist[j]; dist[j] = dist_tmp; } } } #else // CPU version std::sort( q + 1, q + num_in, [](const Point& A, const Point& B) -> bool { T temp = cross_2d(A, B); if (fabs(temp) < 1e-6) { return dot_2d(A, A) < dot_2d(B, B); } else { return temp > 0; } }); // compute distance to origin after sort, since the points are now different. for (int i = 0; i < num_in; i++) { dist[i] = dot_2d(q[i], q[i]); } #endif // Step 4: // Make sure there are at least 2 points (that don't overlap with each other) // in the stack int k; // index of the non-overlapped second point for (k = 1; k < num_in; k++) { if (dist[k] > 1e-8) { break; } } if (k == num_in) { // We reach the end, which means the convex hull is just one point q[0] = p[t]; return 1; } q[1] = q[k]; int m = 2; // 2 points in the stack // Step 5: // Finally we can start the scanning process. // When a non-convex relationship between the 3 points is found // (either concave shape or duplicated points), // we pop the previous point from the stack // until the 3-point relationship is convex again, or // until the stack only contains two points for (int i = k + 1; i < num_in; i++) { while (m > 1) { auto q1 = q[i] - q[m - 2], q2 = q[m - 1] - q[m - 2]; // cross_2d() uses FMA and therefore computes round(round(q1.x*q2.y) - // q2.x*q1.y) So it may not return 0 even when q1==q2. Therefore we // compare round(q1.x*q2.y) and round(q2.x*q1.y) directly. (round means // round to nearest floating point). if (q1.x * q2.y >= q2.x * q1.y) m--; else break; } // Using double also helps, but float can solve the issue for now. // while (m > 1 && cross_2d(q[i] - q[m - 2], q[m - 1] - q[m - 2]) // >= 0) { // m--; // } q[m++] = q[i]; } // Step 6 (Optional): // In general sense we need the original coordinates, so we // need to shift the points back (reverting Step 2) // But if we're only interested in getting the area/perimeter of the shape // We can simply return. if (!shift_to_zero) { for (int i = 0; i < m; i++) { q[i] += start; } } return m; } template HOST_DEVICE_INLINE T polygon_area(const Point (&q)[24], const int& m) { if (m <= 2) { return 0; } T area = 0; for (int i = 1; i < m - 1; i++) { area += fabs(cross_2d(q[i] - q[0], q[i + 1] - q[0])); } return area / 2.0; } template HOST_DEVICE_INLINE T rotated_boxes_intersection( const RotatedBox& box1, const RotatedBox& box2) { // There are up to 4 x 4 + 4 + 4 = 24 intersections (including dups) returned // from rotated_rect_intersection_pts Point intersectPts[24], orderedPts[24]; Point pts1[4]; Point pts2[4]; get_rotated_vertices(box1, pts1); get_rotated_vertices(box2, pts2); int num = get_intersection_points(pts1, pts2, intersectPts); if (num <= 2) { return 0.0; } // Convex Hull to order the intersection points in clockwise order and find // the contour area. int num_convex = convex_hull_graham(intersectPts, num, orderedPts, true); return polygon_area(orderedPts, num_convex); } } // namespace template HOST_DEVICE_INLINE T single_box_iou_rotated(T const* const box1_raw, T const* const box2_raw) { // shift center to the middle point to achieve higher precision in result RotatedBox box1, box2; auto center_shift_x = (box1_raw[0] + box2_raw[0]) / 2.0; auto center_shift_y = (box1_raw[1] + box2_raw[1]) / 2.0; box1.x_ctr = box1_raw[0] - center_shift_x; box1.y_ctr = box1_raw[1] - center_shift_y; box1.w = box1_raw[2]; box1.h = box1_raw[3]; box1.a = box1_raw[4]; box2.x_ctr = box2_raw[0] - center_shift_x; box2.y_ctr = box2_raw[1] - center_shift_y; box2.w = box2_raw[2]; box2.h = box2_raw[3]; box2.a = box2_raw[4]; T area1 = box1.w * box1.h; T area2 = box2.w * box2.h; if (area1 < 1e-14 || area2 < 1e-14) { return 0.f; } T intersection = rotated_boxes_intersection(box1, box2); T iou = intersection / (area1 + area2 - intersection); return iou; } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/cocoeval/cocoeval.cpp ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #include "cocoeval.h" #include #include #include #include using namespace pybind11::literals; namespace detectron2 { namespace COCOeval { // Sort detections from highest score to lowest, such that // detection_instances[detection_sorted_indices[t]] >= // detection_instances[detection_sorted_indices[t+1]]. Use stable_sort to match // original COCO API void SortInstancesByDetectionScore( const std::vector& detection_instances, std::vector* detection_sorted_indices) { detection_sorted_indices->resize(detection_instances.size()); std::iota( detection_sorted_indices->begin(), detection_sorted_indices->end(), 0); std::stable_sort( detection_sorted_indices->begin(), detection_sorted_indices->end(), [&detection_instances](size_t j1, size_t j2) { return detection_instances[j1].score > detection_instances[j2].score; }); } // Partition the ground truth objects based on whether or not to ignore them // based on area void SortInstancesByIgnore( const std::array& area_range, const std::vector& ground_truth_instances, std::vector* ground_truth_sorted_indices, std::vector* ignores) { ignores->clear(); ignores->reserve(ground_truth_instances.size()); for (auto o : ground_truth_instances) { ignores->push_back( o.ignore || o.area < area_range[0] || o.area > area_range[1]); } ground_truth_sorted_indices->resize(ground_truth_instances.size()); std::iota( ground_truth_sorted_indices->begin(), ground_truth_sorted_indices->end(), 0); std::stable_sort( ground_truth_sorted_indices->begin(), ground_truth_sorted_indices->end(), [&ignores](size_t j1, size_t j2) { return (int)(*ignores)[j1] < (int)(*ignores)[j2]; }); } // For each IOU threshold, greedily match each detected instance to a ground // truth instance (if possible) and store the results void MatchDetectionsToGroundTruth( const std::vector& detection_instances, const std::vector& detection_sorted_indices, const std::vector& ground_truth_instances, const std::vector& ground_truth_sorted_indices, const std::vector& ignores, const std::vector>& ious, const std::vector& iou_thresholds, const std::array& area_range, ImageEvaluation* results) { // Initialize memory to store return data matches and ignore const int num_iou_thresholds = iou_thresholds.size(); const int num_ground_truth = ground_truth_sorted_indices.size(); const int num_detections = detection_sorted_indices.size(); std::vector ground_truth_matches( num_iou_thresholds * num_ground_truth, 0); std::vector& detection_matches = results->detection_matches; std::vector& detection_ignores = results->detection_ignores; std::vector& ground_truth_ignores = results->ground_truth_ignores; detection_matches.resize(num_iou_thresholds * num_detections, 0); detection_ignores.resize(num_iou_thresholds * num_detections, false); ground_truth_ignores.resize(num_ground_truth); for (auto g = 0; g < num_ground_truth; ++g) { ground_truth_ignores[g] = ignores[ground_truth_sorted_indices[g]]; } for (auto t = 0; t < num_iou_thresholds; ++t) { for (auto d = 0; d < num_detections; ++d) { // information about best match so far (match=-1 -> unmatched) double best_iou = std::min(iou_thresholds[t], 1 - 1e-10); int match = -1; for (auto g = 0; g < num_ground_truth; ++g) { // if this ground truth instance is already matched and not a // crowd, it cannot be matched to another detection if (ground_truth_matches[t * num_ground_truth + g] > 0 && !ground_truth_instances[ground_truth_sorted_indices[g]].is_crowd) { continue; } // if detected instance matched to a regular ground truth // instance, we can break on the first ground truth instance // tagged as ignore (because they are sorted by the ignore tag) if (match >= 0 && !ground_truth_ignores[match] && ground_truth_ignores[g]) { break; } // if IOU overlap is the best so far, store the match appropriately if (ious[d][ground_truth_sorted_indices[g]] >= best_iou) { best_iou = ious[d][ground_truth_sorted_indices[g]]; match = g; } } // if match was made, store id of match for both detection and // ground truth if (match >= 0) { detection_ignores[t * num_detections + d] = ground_truth_ignores[match]; detection_matches[t * num_detections + d] = ground_truth_instances[ground_truth_sorted_indices[match]].id; ground_truth_matches[t * num_ground_truth + match] = detection_instances[detection_sorted_indices[d]].id; } // set unmatched detections outside of area range to ignore const InstanceAnnotation& detection = detection_instances[detection_sorted_indices[d]]; detection_ignores[t * num_detections + d] = detection_ignores[t * num_detections + d] || (detection_matches[t * num_detections + d] == 0 && (detection.area < area_range[0] || detection.area > area_range[1])); } } // store detection score results results->detection_scores.resize(detection_sorted_indices.size()); for (size_t d = 0; d < detection_sorted_indices.size(); ++d) { results->detection_scores[d] = detection_instances[detection_sorted_indices[d]].score; } } std::vector EvaluateImages( const std::vector>& area_ranges, int max_detections, const std::vector& iou_thresholds, const ImageCategoryInstances>& image_category_ious, const ImageCategoryInstances& image_category_ground_truth_instances, const ImageCategoryInstances& image_category_detection_instances) { const int num_area_ranges = area_ranges.size(); const int num_images = image_category_ground_truth_instances.size(); const int num_categories = image_category_ious.size() > 0 ? image_category_ious[0].size() : 0; std::vector detection_sorted_indices; std::vector ground_truth_sorted_indices; std::vector ignores; std::vector results_all( num_images * num_area_ranges * num_categories); // Store results for each image, category, and area range combination. Results // for each IOU threshold are packed into the same ImageEvaluation object for (auto i = 0; i < num_images; ++i) { for (auto c = 0; c < num_categories; ++c) { const std::vector& ground_truth_instances = image_category_ground_truth_instances[i][c]; const std::vector& detection_instances = image_category_detection_instances[i][c]; SortInstancesByDetectionScore( detection_instances, &detection_sorted_indices); if ((int)detection_sorted_indices.size() > max_detections) { detection_sorted_indices.resize(max_detections); } for (size_t a = 0; a < area_ranges.size(); ++a) { SortInstancesByIgnore( area_ranges[a], ground_truth_instances, &ground_truth_sorted_indices, &ignores); MatchDetectionsToGroundTruth( detection_instances, detection_sorted_indices, ground_truth_instances, ground_truth_sorted_indices, ignores, image_category_ious[i][c], iou_thresholds, area_ranges[a], &results_all [c * num_area_ranges * num_images + a * num_images + i]); } } } return results_all; } // Convert a python list to a vector template std::vector list_to_vec(const py::list& l) { std::vector v(py::len(l)); for (int i = 0; i < (int)py::len(l); ++i) { v[i] = l[i].cast(); } return v; } // Helper function to Accumulate() // Considers the evaluation results applicable to a particular category, area // range, and max_detections parameter setting, which begin at // evaluations[evaluation_index]. Extracts a sorted list of length n of all // applicable detection instances concatenated across all images in the dataset, // which are represented by the outputs evaluation_indices, detection_scores, // image_detection_indices, and detection_sorted_indices--all of which are // length n. evaluation_indices[i] stores the applicable index into // evaluations[] for instance i, which has detection score detection_score[i], // and is the image_detection_indices[i]'th of the list of detections // for the image containing i. detection_sorted_indices[] defines a sorted // permutation of the 3 other outputs int BuildSortedDetectionList( const std::vector& evaluations, const int64_t evaluation_index, const int64_t num_images, const int max_detections, std::vector* evaluation_indices, std::vector* detection_scores, std::vector* detection_sorted_indices, std::vector* image_detection_indices) { assert(evaluations.size() >= evaluation_index + num_images); // Extract a list of object instances of the applicable category, area // range, and max detections requirements such that they can be sorted image_detection_indices->clear(); evaluation_indices->clear(); detection_scores->clear(); image_detection_indices->reserve(num_images * max_detections); evaluation_indices->reserve(num_images * max_detections); detection_scores->reserve(num_images * max_detections); int num_valid_ground_truth = 0; for (auto i = 0; i < num_images; ++i) { const ImageEvaluation& evaluation = evaluations[evaluation_index + i]; for (int d = 0; d < (int)evaluation.detection_scores.size() && d < max_detections; ++d) { // detected instances evaluation_indices->push_back(evaluation_index + i); image_detection_indices->push_back(d); detection_scores->push_back(evaluation.detection_scores[d]); } for (auto ground_truth_ignore : evaluation.ground_truth_ignores) { if (!ground_truth_ignore) { ++num_valid_ground_truth; } } } // Sort detections by decreasing score, using stable sort to match // python implementation detection_sorted_indices->resize(detection_scores->size()); std::iota( detection_sorted_indices->begin(), detection_sorted_indices->end(), 0); std::stable_sort( detection_sorted_indices->begin(), detection_sorted_indices->end(), [&detection_scores](size_t j1, size_t j2) { return (*detection_scores)[j1] > (*detection_scores)[j2]; }); return num_valid_ground_truth; } // Helper function to Accumulate() // Compute a precision recall curve given a sorted list of detected instances // encoded in evaluations, evaluation_indices, detection_scores, // detection_sorted_indices, image_detection_indices (see // BuildSortedDetectionList()). Using vectors precisions and recalls // and temporary storage, output the results into precisions_out, recalls_out, // and scores_out, which are large buffers containing many precion/recall curves // for all possible parameter settings, with precisions_out_index and // recalls_out_index defining the applicable indices to store results. void ComputePrecisionRecallCurve( const int64_t precisions_out_index, const int64_t precisions_out_stride, const int64_t recalls_out_index, const std::vector& recall_thresholds, const int iou_threshold_index, const int num_iou_thresholds, const int num_valid_ground_truth, const std::vector& evaluations, const std::vector& evaluation_indices, const std::vector& detection_scores, const std::vector& detection_sorted_indices, const std::vector& image_detection_indices, std::vector* precisions, std::vector* recalls, std::vector* precisions_out, std::vector* scores_out, std::vector* recalls_out) { assert(recalls_out->size() > recalls_out_index); // Compute precision/recall for each instance in the sorted list of detections int64_t true_positives_sum = 0, false_positives_sum = 0; precisions->clear(); recalls->clear(); precisions->reserve(detection_sorted_indices.size()); recalls->reserve(detection_sorted_indices.size()); assert(!evaluations.empty() || detection_sorted_indices.empty()); for (auto detection_sorted_index : detection_sorted_indices) { const ImageEvaluation& evaluation = evaluations[evaluation_indices[detection_sorted_index]]; const auto num_detections = evaluation.detection_matches.size() / num_iou_thresholds; const auto detection_index = iou_threshold_index * num_detections + image_detection_indices[detection_sorted_index]; assert(evaluation.detection_matches.size() > detection_index); assert(evaluation.detection_ignores.size() > detection_index); const int64_t detection_match = evaluation.detection_matches[detection_index]; const bool detection_ignores = evaluation.detection_ignores[detection_index]; const auto true_positive = detection_match > 0 && !detection_ignores; const auto false_positive = detection_match == 0 && !detection_ignores; if (true_positive) { ++true_positives_sum; } if (false_positive) { ++false_positives_sum; } const double recall = static_cast(true_positives_sum) / num_valid_ground_truth; recalls->push_back(recall); const int64_t num_valid_detections = true_positives_sum + false_positives_sum; const double precision = num_valid_detections > 0 ? static_cast(true_positives_sum) / num_valid_detections : 0.0; precisions->push_back(precision); } (*recalls_out)[recalls_out_index] = !recalls->empty() ? recalls->back() : 0; for (int64_t i = static_cast(precisions->size()) - 1; i > 0; --i) { if ((*precisions)[i] > (*precisions)[i - 1]) { (*precisions)[i - 1] = (*precisions)[i]; } } // Sample the per instance precision/recall list at each recall threshold for (size_t r = 0; r < recall_thresholds.size(); ++r) { // first index in recalls >= recall_thresholds[r] std::vector::iterator low = std::lower_bound( recalls->begin(), recalls->end(), recall_thresholds[r]); size_t precisions_index = low - recalls->begin(); const auto results_ind = precisions_out_index + r * precisions_out_stride; assert(results_ind < precisions_out->size()); assert(results_ind < scores_out->size()); if (precisions_index < precisions->size()) { (*precisions_out)[results_ind] = (*precisions)[precisions_index]; (*scores_out)[results_ind] = detection_scores[detection_sorted_indices[precisions_index]]; } else { (*precisions_out)[results_ind] = 0; (*scores_out)[results_ind] = 0; } } } py::dict Accumulate( const py::object& params, const std::vector& evaluations) { const std::vector recall_thresholds = list_to_vec(params.attr("recThrs")); const std::vector max_detections = list_to_vec(params.attr("maxDets")); const int num_iou_thresholds = py::len(params.attr("iouThrs")); const int num_recall_thresholds = py::len(params.attr("recThrs")); const int num_categories = params.attr("useCats").cast() == 1 ? py::len(params.attr("catIds")) : 1; const int num_area_ranges = py::len(params.attr("areaRng")); const int num_max_detections = py::len(params.attr("maxDets")); const int num_images = py::len(params.attr("imgIds")); std::vector precisions_out( num_iou_thresholds * num_recall_thresholds * num_categories * num_area_ranges * num_max_detections, -1); std::vector recalls_out( num_iou_thresholds * num_categories * num_area_ranges * num_max_detections, -1); std::vector scores_out( num_iou_thresholds * num_recall_thresholds * num_categories * num_area_ranges * num_max_detections, -1); // Consider the list of all detected instances in the entire dataset in one // large list. evaluation_indices, detection_scores, // image_detection_indices, and detection_sorted_indices all have the same // length as this list, such that each entry corresponds to one detected // instance std::vector evaluation_indices; // indices into evaluations[] std::vector detection_scores; // detection scores of each instance std::vector detection_sorted_indices; // sorted indices of all // instances in the dataset std::vector image_detection_indices; // indices into the list of detected instances in // the same image as each instance std::vector precisions, recalls; for (auto c = 0; c < num_categories; ++c) { for (auto a = 0; a < num_area_ranges; ++a) { for (auto m = 0; m < num_max_detections; ++m) { // The COCO PythonAPI assumes evaluations[] (the return value of // COCOeval::EvaluateImages() is one long list storing results for each // combination of category, area range, and image id, with categories in // the outermost loop and images in the innermost loop. const int64_t evaluations_index = c * num_area_ranges * num_images + a * num_images; int num_valid_ground_truth = BuildSortedDetectionList( evaluations, evaluations_index, num_images, max_detections[m], &evaluation_indices, &detection_scores, &detection_sorted_indices, &image_detection_indices); if (num_valid_ground_truth == 0) { continue; } for (auto t = 0; t < num_iou_thresholds; ++t) { // recalls_out is a flattened vectors representing a // num_iou_thresholds X num_categories X num_area_ranges X // num_max_detections matrix const int64_t recalls_out_index = t * num_categories * num_area_ranges * num_max_detections + c * num_area_ranges * num_max_detections + a * num_max_detections + m; // precisions_out and scores_out are flattened vectors // representing a num_iou_thresholds X num_recall_thresholds X // num_categories X num_area_ranges X num_max_detections matrix const int64_t precisions_out_stride = num_categories * num_area_ranges * num_max_detections; const int64_t precisions_out_index = t * num_recall_thresholds * num_categories * num_area_ranges * num_max_detections + c * num_area_ranges * num_max_detections + a * num_max_detections + m; ComputePrecisionRecallCurve( precisions_out_index, precisions_out_stride, recalls_out_index, recall_thresholds, t, num_iou_thresholds, num_valid_ground_truth, evaluations, evaluation_indices, detection_scores, detection_sorted_indices, image_detection_indices, &precisions, &recalls, &precisions_out, &scores_out, &recalls_out); } } } } time_t rawtime; struct tm local_time; std::array buffer; time(&rawtime); #ifdef _WIN32 localtime_s(&local_time, &rawtime); #else localtime_r(&rawtime, &local_time); #endif strftime( buffer.data(), 200, "%Y-%m-%d %H:%num_max_detections:%S", &local_time); return py::dict( "params"_a = params, "counts"_a = std::vector( {num_iou_thresholds, num_recall_thresholds, num_categories, num_area_ranges, num_max_detections}), "date"_a = buffer, "precision"_a = precisions_out, "recall"_a = recalls_out, "scores"_a = scores_out); } } // namespace COCOeval } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/cocoeval/cocoeval.h ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #pragma once #include #include #include #include #include namespace py = pybind11; namespace detectron2 { namespace COCOeval { // Annotation data for a single object instance in an image struct InstanceAnnotation { InstanceAnnotation( uint64_t id, double score, double area, bool is_crowd, bool ignore) : id{id}, score{score}, area{area}, is_crowd{is_crowd}, ignore{ignore} {} uint64_t id; double score = 0.; double area = 0.; bool is_crowd = false; bool ignore = false; }; // Stores intermediate results for evaluating detection results for a single // image that has D detected instances and G ground truth instances. This stores // matches between detected and ground truth instances struct ImageEvaluation { // For each of the D detected instances, the id of the matched ground truth // instance, or 0 if unmatched std::vector detection_matches; // The detection score of each of the D detected instances std::vector detection_scores; // Marks whether or not each of G instances was ignored from evaluation (e.g., // because it's outside area_range) std::vector ground_truth_ignores; // Marks whether or not each of D instances was ignored from evaluation (e.g., // because it's outside aRng) std::vector detection_ignores; }; template using ImageCategoryInstances = std::vector>>; // C++ implementation of COCO API cocoeval.py::COCOeval.evaluateImg(). For each // combination of image, category, area range settings, and IOU thresholds to // evaluate, it matches detected instances to ground truth instances and stores // the results into a vector of ImageEvaluation results, which will be // interpreted by the COCOeval::Accumulate() function to produce precion-recall // curves. The parameters of nested vectors have the following semantics: // image_category_ious[i][c][d][g] is the intersection over union of the d'th // detected instance and g'th ground truth instance of // category category_ids[c] in image image_ids[i] // image_category_ground_truth_instances[i][c] is a vector of ground truth // instances in image image_ids[i] of category category_ids[c] // image_category_detection_instances[i][c] is a vector of detected // instances in image image_ids[i] of category category_ids[c] std::vector EvaluateImages( const std::vector>& area_ranges, // vector of 2-tuples int max_detections, const std::vector& iou_thresholds, const ImageCategoryInstances>& image_category_ious, const ImageCategoryInstances& image_category_ground_truth_instances, const ImageCategoryInstances& image_category_detection_instances); // C++ implementation of COCOeval.accumulate(), which generates precision // recall curves for each set of category, IOU threshold, detection area range, // and max number of detections parameters. It is assumed that the parameter // evaluations is the return value of the functon COCOeval::EvaluateImages(), // which was called with the same parameter settings params py::dict Accumulate( const py::object& params, const std::vector& evalutations); } // namespace COCOeval } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/cuda_version.cu ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #include namespace detectron2 { int get_cudart_version() { // Not a ROCM platform: Either HIP is not used, or // it is used, but platform is not ROCM (i.e. it is CUDA) #if !defined(__HIP_PLATFORM_HCC__) return CUDART_VERSION; #else int version = 0; #if HIP_VERSION_MAJOR != 0 // Create a convention similar to that of CUDA, as assumed by other // parts of the code. version = HIP_VERSION_MINOR; version += (HIP_VERSION_MAJOR * 100); #else hipRuntimeGetVersion(&version); #endif return version; #endif } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/deformable/deform_conv.h ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #pragma once #include namespace detectron2 { #if defined(WITH_CUDA) || defined(WITH_HIP) int deform_conv_forward_cuda( at::Tensor input, at::Tensor weight, at::Tensor offset, at::Tensor output, at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, int padW, int padH, int dilationW, int dilationH, int group, int deformable_group, int im2col_step); int deform_conv_backward_input_cuda( at::Tensor input, at::Tensor offset, at::Tensor gradOutput, at::Tensor gradInput, at::Tensor gradOffset, at::Tensor weight, at::Tensor columns, int kW, int kH, int dW, int dH, int padW, int padH, int dilationW, int dilationH, int group, int deformable_group, int im2col_step); int deform_conv_backward_parameters_cuda( at::Tensor input, at::Tensor offset, at::Tensor gradOutput, at::Tensor gradWeight, // at::Tensor gradBias, at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, int padW, int padH, int dilationW, int dilationH, int group, int deformable_group, float scale, int im2col_step); void modulated_deform_conv_cuda_forward( at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, at::Tensor offset, at::Tensor mask, at::Tensor output, at::Tensor columns, int kernel_h, int kernel_w, const int stride_h, const int stride_w, const int pad_h, const int pad_w, const int dilation_h, const int dilation_w, const int group, const int deformable_group, const bool with_bias); void modulated_deform_conv_cuda_backward( at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, at::Tensor offset, at::Tensor mask, at::Tensor columns, at::Tensor grad_input, at::Tensor grad_weight, at::Tensor grad_bias, at::Tensor grad_offset, at::Tensor grad_mask, at::Tensor grad_output, int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, const bool with_bias); #endif inline int deform_conv_forward( at::Tensor input, at::Tensor weight, at::Tensor offset, at::Tensor output, at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, int padW, int padH, int dilationW, int dilationH, int group, int deformable_group, int im2col_step) { if (input.is_cuda()) { #if defined(WITH_CUDA) || defined(WITH_HIP) TORCH_CHECK(weight.is_cuda(), "weight tensor is not on GPU!"); TORCH_CHECK(offset.is_cuda(), "offset tensor is not on GPU!"); return deform_conv_forward_cuda( input, weight, offset, output, columns, ones, kW, kH, dW, dH, padW, padH, dilationW, dilationH, group, deformable_group, im2col_step); #else AT_ERROR("Detectron2 is not compiled with GPU support!"); #endif } AT_ERROR("This operator is not implemented on CPU"); } inline int deform_conv_backward_input( at::Tensor input, at::Tensor offset, at::Tensor gradOutput, at::Tensor gradInput, at::Tensor gradOffset, at::Tensor weight, at::Tensor columns, int kW, int kH, int dW, int dH, int padW, int padH, int dilationW, int dilationH, int group, int deformable_group, int im2col_step) { if (gradOutput.is_cuda()) { #if defined(WITH_CUDA) || defined(WITH_HIP) TORCH_CHECK(input.is_cuda(), "input tensor is not on GPU!"); TORCH_CHECK(weight.is_cuda(), "weight tensor is not on GPU!"); TORCH_CHECK(offset.is_cuda(), "offset tensor is not on GPU!"); return deform_conv_backward_input_cuda( input, offset, gradOutput, gradInput, gradOffset, weight, columns, kW, kH, dW, dH, padW, padH, dilationW, dilationH, group, deformable_group, im2col_step); #else AT_ERROR("Detectron2 is not compiled with GPU support!"); #endif } AT_ERROR("This operator is not implemented on CPU"); } inline int deform_conv_backward_filter( at::Tensor input, at::Tensor offset, at::Tensor gradOutput, at::Tensor gradWeight, // at::Tensor gradBias, at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, int padW, int padH, int dilationW, int dilationH, int group, int deformable_group, float scale, int im2col_step) { if (gradOutput.is_cuda()) { #if defined(WITH_CUDA) || defined(WITH_HIP) TORCH_CHECK(input.is_cuda(), "input tensor is not on GPU!"); TORCH_CHECK(offset.is_cuda(), "offset tensor is not on GPU!"); return deform_conv_backward_parameters_cuda( input, offset, gradOutput, gradWeight, columns, ones, kW, kH, dW, dH, padW, padH, dilationW, dilationH, group, deformable_group, scale, im2col_step); #else AT_ERROR("Detectron2 is not compiled with GPU support!"); #endif } AT_ERROR("This operator is not implemented on CPU"); } inline void modulated_deform_conv_forward( at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, at::Tensor offset, at::Tensor mask, at::Tensor output, at::Tensor columns, int kernel_h, int kernel_w, const int stride_h, const int stride_w, const int pad_h, const int pad_w, const int dilation_h, const int dilation_w, const int group, const int deformable_group, const bool with_bias) { if (input.is_cuda()) { #if defined(WITH_CUDA) || defined(WITH_HIP) TORCH_CHECK(weight.is_cuda(), "weight tensor is not on GPU!"); TORCH_CHECK(bias.is_cuda(), "bias tensor is not on GPU!"); TORCH_CHECK(offset.is_cuda(), "offset tensor is not on GPU!"); return modulated_deform_conv_cuda_forward( input, weight, bias, ones, offset, mask, output, columns, kernel_h, kernel_w, stride_h, stride_w, pad_h, pad_w, dilation_h, dilation_w, group, deformable_group, with_bias); #else AT_ERROR("Detectron2 is not compiled with GPU support!"); #endif } AT_ERROR("This operator is not implemented on CPU"); } inline void modulated_deform_conv_backward( at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, at::Tensor offset, at::Tensor mask, at::Tensor columns, at::Tensor grad_input, at::Tensor grad_weight, at::Tensor grad_bias, at::Tensor grad_offset, at::Tensor grad_mask, at::Tensor grad_output, int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, const bool with_bias) { if (grad_output.is_cuda()) { #if defined(WITH_CUDA) || defined(WITH_HIP) TORCH_CHECK(input.is_cuda(), "input tensor is not on GPU!"); TORCH_CHECK(weight.is_cuda(), "weight tensor is not on GPU!"); TORCH_CHECK(bias.is_cuda(), "bias tensor is not on GPU!"); TORCH_CHECK(offset.is_cuda(), "offset tensor is not on GPU!"); return modulated_deform_conv_cuda_backward( input, weight, bias, ones, offset, mask, columns, grad_input, grad_weight, grad_bias, grad_offset, grad_mask, grad_output, kernel_h, kernel_w, stride_h, stride_w, pad_h, pad_w, dilation_h, dilation_w, group, deformable_group, with_bias); #else AT_ERROR("Detectron2 is not compiled with GPU support!"); #endif } AT_ERROR("This operator is not implemented on CPU"); } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/deformable/deform_conv_cuda.cu ================================================ // Copyright (c) Facebook, Inc. and its affiliates. // modified from // https://github.com/open-mmlab/mmdetection/blob/master/mmdet/ops/dcn/src/deform_conv_cuda.cpp // Original license: Apache 2.0 // modify from // https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/blob/mmdetection/mmdet/ops/dcn/src/deform_conv_cuda.c // Original license: Apache 2.0 #include #include "deform_conv.h" #include #include namespace detectron2 { void deformable_im2col( const at::Tensor data_im, const at::Tensor data_offset, const int channels, const int height, const int width, const int ksize_h, const int ksize_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int parallel_imgs, const int deformable_group, at::Tensor data_col); void deformable_col2im( const at::Tensor data_col, const at::Tensor data_offset, const int channels, const int height, const int width, const int ksize_h, const int ksize_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int parallel_imgs, const int deformable_group, at::Tensor grad_im); void deformable_col2im_coord( const at::Tensor data_col, const at::Tensor data_im, const at::Tensor data_offset, const int channels, const int height, const int width, const int ksize_h, const int ksize_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int parallel_imgs, const int deformable_group, at::Tensor grad_offset); void modulated_deformable_im2col_cuda( const at::Tensor data_im, const at::Tensor data_offset, const at::Tensor data_mask, const int batch_size, const int channels, const int height_im, const int width_im, const int height_col, const int width_col, const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int deformable_group, at::Tensor data_col); void modulated_deformable_col2im_cuda( const at::Tensor data_col, const at::Tensor data_offset, const at::Tensor data_mask, const int batch_size, const int channels, const int height_im, const int width_im, const int height_col, const int width_col, const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int deformable_group, at::Tensor grad_im); void modulated_deformable_col2im_coord_cuda( const at::Tensor data_col, const at::Tensor data_im, const at::Tensor data_offset, const at::Tensor data_mask, const int batch_size, const int channels, const int height_im, const int width_im, const int height_col, const int width_col, const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int deformable_group, at::Tensor grad_offset, at::Tensor grad_mask); void shape_check( at::Tensor input, at::Tensor offset, at::Tensor* gradOutput, at::Tensor weight, int kH, int kW, int dH, int dW, int padH, int padW, int dilationH, int dilationW, int group, int deformable_group) { TORCH_CHECK( weight.ndimension() == 4, "4D weight tensor (nOutputPlane,nInputPlane,kH,kW) expected, " "but got: %s", weight.ndimension()); TORCH_CHECK(weight.is_contiguous(), "weight tensor has to be contiguous"); TORCH_CHECK( kW > 0 && kH > 0, "kernel size should be greater than zero, but got kH: %d kW: %d", kH, kW); TORCH_CHECK( (weight.size(2) == kH && weight.size(3) == kW), "kernel size should be consistent with weight, ", "but got kH: %d kW: %d weight.size(2): %d, weight.size(3): %d", kH, kW, weight.size(2), weight.size(3)); TORCH_CHECK( dW > 0 && dH > 0, "stride should be greater than zero, but got dH: %d dW: %d", dH, dW); TORCH_CHECK( dilationW > 0 && dilationH > 0, "dilation should be greater than 0, but got dilationH: %d dilationW: %d", dilationH, dilationW); int ndim = input.ndimension(); int dimf = 0; int dimh = 1; int dimw = 2; if (ndim == 4) { dimf++; dimh++; dimw++; } TORCH_CHECK( ndim == 3 || ndim == 4, "3D or 4D input tensor expected but got: %s", ndim); long nInputPlane = weight.size(1) * group; long inputHeight = input.size(dimh); long inputWidth = input.size(dimw); long nOutputPlane = weight.size(0); long outputHeight = (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; long outputWidth = (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; TORCH_CHECK( nInputPlane % deformable_group == 0, "input channels must divide deformable group size"); if (outputWidth < 1 || outputHeight < 1) AT_ERROR( "Given input size: (%ld x %ld x %ld). " "Calculated output size: (%ld x %ld x %ld). Output size is too small", nInputPlane, inputHeight, inputWidth, nOutputPlane, outputHeight, outputWidth); TORCH_CHECK( input.size(1) == nInputPlane, "invalid number of input planes, expected: %d, but got: %d", nInputPlane, input.size(1)); TORCH_CHECK( (inputHeight + 2 * padH >= kH && inputWidth + 2 * padW >= kW), "input image is smaller than kernel"); TORCH_CHECK( (offset.size(2) == outputHeight && offset.size(3) == outputWidth), "invalid spatial size of offset, expected height: %d width: %d, but " "got height: %d width: %d", outputHeight, outputWidth, offset.size(2), offset.size(3)); TORCH_CHECK( (offset.size(1) == deformable_group * 2 * kH * kW), "invalid number of channels of offset"); if (gradOutput != NULL) { TORCH_CHECK( gradOutput->size(dimf) == nOutputPlane, "invalid number of gradOutput planes, expected: %d, but got: %d", nOutputPlane, gradOutput->size(dimf)); TORCH_CHECK( (gradOutput->size(dimh) == outputHeight && gradOutput->size(dimw) == outputWidth), "invalid size of gradOutput, expected height: %d width: %d , but " "got height: %d width: %d", outputHeight, outputWidth, gradOutput->size(dimh), gradOutput->size(dimw)); } } int deform_conv_forward_cuda( at::Tensor input, at::Tensor weight, at::Tensor offset, at::Tensor output, at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, int padW, int padH, int dilationW, int dilationH, int group, int deformable_group, int im2col_step) { // todo: resize columns to include im2col: done // todo: add im2col_step as input // todo: add new output buffer and transpose it to output (or directly // transpose output) todo: possibly change data indexing because of // parallel_imgs shape_check( input, offset, NULL, weight, kH, kW, dH, dW, padH, padW, dilationH, dilationW, group, deformable_group); input = input.contiguous(); offset = offset.contiguous(); weight = weight.contiguous(); int batch = 1; if (input.ndimension() == 3) { // Force batch batch = 0; input.unsqueeze_(0); offset.unsqueeze_(0); } // todo: assert batchsize dividable by im2col_step long batchSize = input.size(0); long nInputPlane = input.size(1); long inputHeight = input.size(2); long inputWidth = input.size(3); long nOutputPlane = weight.size(0); long outputWidth = (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; long outputHeight = (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; TORCH_CHECK((offset.size(0) == batchSize), "invalid batch size of offset"); output = output.view( {batchSize / im2col_step, im2col_step, nOutputPlane, outputHeight, outputWidth}); columns = at::zeros( {nInputPlane * kW * kH, im2col_step * outputHeight * outputWidth}, input.options()); if (ones.ndimension() != 2 || ones.size(0) * ones.size(1) < outputHeight * outputWidth) { ones = at::ones({outputHeight, outputWidth}, input.options()); } input = input.view( {batchSize / im2col_step, im2col_step, nInputPlane, inputHeight, inputWidth}); offset = offset.view( {batchSize / im2col_step, im2col_step, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); at::Tensor output_buffer = at::zeros( {batchSize / im2col_step, nOutputPlane, im2col_step * outputHeight, outputWidth}, output.options()); output_buffer = output_buffer.view( {output_buffer.size(0), group, output_buffer.size(1) / group, output_buffer.size(2), output_buffer.size(3)}); for (int elt = 0; elt < batchSize / im2col_step; elt++) { deformable_im2col( input[elt], offset[elt], nInputPlane, inputHeight, inputWidth, kH, kW, padH, padW, dH, dW, dilationH, dilationW, im2col_step, deformable_group, columns); columns = columns.view({group, columns.size(0) / group, columns.size(1)}); weight = weight.view( {group, weight.size(0) / group, weight.size(1), weight.size(2), weight.size(3)}); for (int g = 0; g < group; g++) { output_buffer[elt][g] = output_buffer[elt][g] .flatten(1) .addmm_(weight[g].flatten(1), columns[g]) .view_as(output_buffer[elt][g]); } } output_buffer = output_buffer.view( {output_buffer.size(0), output_buffer.size(1) * output_buffer.size(2), output_buffer.size(3), output_buffer.size(4)}); output_buffer = output_buffer.view( {batchSize / im2col_step, nOutputPlane, im2col_step, outputHeight, outputWidth}); output_buffer.transpose_(1, 2); output.copy_(output_buffer); output = output.view({batchSize, nOutputPlane, outputHeight, outputWidth}); input = input.view({batchSize, nInputPlane, inputHeight, inputWidth}); offset = offset.view( {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); if (batch == 0) { output = output.view({nOutputPlane, outputHeight, outputWidth}); input = input.view({nInputPlane, inputHeight, inputWidth}); offset = offset.view({offset.size(1), offset.size(2), offset.size(3)}); } return 1; } int deform_conv_backward_input_cuda( at::Tensor input, at::Tensor offset, at::Tensor gradOutput, at::Tensor gradInput, at::Tensor gradOffset, at::Tensor weight, at::Tensor columns, int kW, int kH, int dW, int dH, int padW, int padH, int dilationW, int dilationH, int group, int deformable_group, int im2col_step) { shape_check( input, offset, &gradOutput, weight, kH, kW, dH, dW, padH, padW, dilationH, dilationW, group, deformable_group); input = input.contiguous(); offset = offset.contiguous(); gradOutput = gradOutput.contiguous(); weight = weight.contiguous(); int batch = 1; if (input.ndimension() == 3) { // Force batch batch = 0; input = input.view({1, input.size(0), input.size(1), input.size(2)}); offset = offset.view({1, offset.size(0), offset.size(1), offset.size(2)}); gradOutput = gradOutput.view( {1, gradOutput.size(0), gradOutput.size(1), gradOutput.size(2)}); } long batchSize = input.size(0); long nInputPlane = input.size(1); long inputHeight = input.size(2); long inputWidth = input.size(3); long nOutputPlane = weight.size(0); long outputWidth = (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; long outputHeight = (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; TORCH_CHECK((offset.size(0) == batchSize), 3, "invalid batch size of offset"); gradInput = gradInput.view({batchSize, nInputPlane, inputHeight, inputWidth}); columns = at::zeros( {nInputPlane * kW * kH, im2col_step * outputHeight * outputWidth}, input.options()); // change order of grad output gradOutput = gradOutput.view( {batchSize / im2col_step, im2col_step, nOutputPlane, outputHeight, outputWidth}); gradOutput.transpose_(1, 2); gradInput = gradInput.view( {batchSize / im2col_step, im2col_step, nInputPlane, inputHeight, inputWidth}); input = input.view( {batchSize / im2col_step, im2col_step, nInputPlane, inputHeight, inputWidth}); gradOffset = gradOffset.view( {batchSize / im2col_step, im2col_step, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); offset = offset.view( {batchSize / im2col_step, im2col_step, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); for (int elt = 0; elt < batchSize / im2col_step; elt++) { // divide into groups columns = columns.view({group, columns.size(0) / group, columns.size(1)}); weight = weight.view( {group, weight.size(0) / group, weight.size(1), weight.size(2), weight.size(3)}); gradOutput = gradOutput.view( {gradOutput.size(0), group, gradOutput.size(1) / group, gradOutput.size(2), gradOutput.size(3), gradOutput.size(4)}); for (int g = 0; g < group; g++) { columns[g] = columns[g].addmm_( weight[g].flatten(1).transpose(0, 1), gradOutput[elt][g].flatten(1), 0.0f, 1.0f); } columns = columns.view({columns.size(0) * columns.size(1), columns.size(2)}); gradOutput = gradOutput.view( {gradOutput.size(0), gradOutput.size(1) * gradOutput.size(2), gradOutput.size(3), gradOutput.size(4), gradOutput.size(5)}); deformable_col2im_coord( columns, input[elt], offset[elt], nInputPlane, inputHeight, inputWidth, kH, kW, padH, padW, dH, dW, dilationH, dilationW, im2col_step, deformable_group, gradOffset[elt]); deformable_col2im( columns, offset[elt], nInputPlane, inputHeight, inputWidth, kH, kW, padH, padW, dH, dW, dilationH, dilationW, im2col_step, deformable_group, gradInput[elt]); } gradOutput.transpose_(1, 2); gradOutput = gradOutput.view({batchSize, nOutputPlane, outputHeight, outputWidth}); gradInput = gradInput.view({batchSize, nInputPlane, inputHeight, inputWidth}); input = input.view({batchSize, nInputPlane, inputHeight, inputWidth}); gradOffset = gradOffset.view( {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); offset = offset.view( {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); if (batch == 0) { gradOutput = gradOutput.view({nOutputPlane, outputHeight, outputWidth}); input = input.view({nInputPlane, inputHeight, inputWidth}); gradInput = gradInput.view({nInputPlane, inputHeight, inputWidth}); offset = offset.view({offset.size(1), offset.size(2), offset.size(3)}); gradOffset = gradOffset.view({offset.size(1), offset.size(2), offset.size(3)}); } return 1; } int deform_conv_backward_parameters_cuda( at::Tensor input, at::Tensor offset, at::Tensor gradOutput, at::Tensor gradWeight, // at::Tensor gradBias, at::Tensor columns, at::Tensor ones, int kW, int kH, int dW, int dH, int padW, int padH, int dilationW, int dilationH, int group, int deformable_group, float scale, int im2col_step) { // todo: transpose and reshape outGrad // todo: reshape columns // todo: add im2col_step as input shape_check( input, offset, &gradOutput, gradWeight, kH, kW, dH, dW, padH, padW, dilationH, dilationW, group, deformable_group); input = input.contiguous(); offset = offset.contiguous(); gradOutput = gradOutput.contiguous(); int batch = 1; if (input.ndimension() == 3) { // Force batch batch = 0; input = input.view( at::IntList({1, input.size(0), input.size(1), input.size(2)})); gradOutput = gradOutput.view( {1, gradOutput.size(0), gradOutput.size(1), gradOutput.size(2)}); } long batchSize = input.size(0); long nInputPlane = input.size(1); long inputHeight = input.size(2); long inputWidth = input.size(3); long nOutputPlane = gradWeight.size(0); long outputWidth = (inputWidth + 2 * padW - (dilationW * (kW - 1) + 1)) / dW + 1; long outputHeight = (inputHeight + 2 * padH - (dilationH * (kH - 1) + 1)) / dH + 1; TORCH_CHECK((offset.size(0) == batchSize), "invalid batch size of offset"); columns = at::zeros( {nInputPlane * kW * kH, im2col_step * outputHeight * outputWidth}, input.options()); gradOutput = gradOutput.view( {batchSize / im2col_step, im2col_step, nOutputPlane, outputHeight, outputWidth}); gradOutput.transpose_(1, 2); at::Tensor gradOutputBuffer = at::zeros_like(gradOutput); gradOutputBuffer = gradOutputBuffer.view( {batchSize / im2col_step, nOutputPlane, im2col_step, outputHeight, outputWidth}); gradOutputBuffer.copy_(gradOutput); // gradOutput is not contiguous, so we do reshape (instead of view) next gradOutputBuffer = gradOutputBuffer.reshape( {batchSize / im2col_step, nOutputPlane, im2col_step * outputHeight, outputWidth}); gradOutput.transpose_(1, 2); gradOutput = gradOutput.view({batchSize, nOutputPlane, outputHeight, outputWidth}); input = input.view( {batchSize / im2col_step, im2col_step, nInputPlane, inputHeight, inputWidth}); offset = offset.view( {batchSize / im2col_step, im2col_step, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); for (int elt = 0; elt < batchSize / im2col_step; elt++) { deformable_im2col( input[elt], offset[elt], nInputPlane, inputHeight, inputWidth, kH, kW, padH, padW, dH, dW, dilationH, dilationW, im2col_step, deformable_group, columns); // divide into group gradOutputBuffer = gradOutputBuffer.view( {gradOutputBuffer.size(0), group, gradOutputBuffer.size(1) / group, gradOutputBuffer.size(2), gradOutputBuffer.size(3)}); columns = columns.view({group, columns.size(0) / group, columns.size(1)}); gradWeight = gradWeight.view( {group, gradWeight.size(0) / group, gradWeight.size(1), gradWeight.size(2), gradWeight.size(3)}); for (int g = 0; g < group; g++) { gradWeight[g] = gradWeight[g] .flatten(1) .addmm_( gradOutputBuffer[elt][g].flatten(1), columns[g].transpose(1, 0), 1.0, scale) .view_as(gradWeight[g]); } gradOutputBuffer = gradOutputBuffer.view( {gradOutputBuffer.size(0), gradOutputBuffer.size(1) * gradOutputBuffer.size(2), gradOutputBuffer.size(3), gradOutputBuffer.size(4)}); columns = columns.view({columns.size(0) * columns.size(1), columns.size(2)}); gradWeight = gradWeight.view( {gradWeight.size(0) * gradWeight.size(1), gradWeight.size(2), gradWeight.size(3), gradWeight.size(4)}); } input = input.view({batchSize, nInputPlane, inputHeight, inputWidth}); offset = offset.view( {batchSize, deformable_group * 2 * kH * kW, outputHeight, outputWidth}); if (batch == 0) { gradOutput = gradOutput.view({nOutputPlane, outputHeight, outputWidth}); input = input.view({nInputPlane, inputHeight, inputWidth}); } return 1; } void modulated_deform_conv_cuda_forward( at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, at::Tensor offset, at::Tensor mask, at::Tensor output, at::Tensor columns, int kernel_h, int kernel_w, const int stride_h, const int stride_w, const int pad_h, const int pad_w, const int dilation_h, const int dilation_w, const int group, const int deformable_group, const bool with_bias) { shape_check( input, offset, NULL, weight, kernel_h, kernel_w, stride_h, stride_w, pad_h, pad_w, dilation_h, dilation_w, group, deformable_group); TORCH_CHECK(input.is_contiguous(), "input tensor has to be contiguous"); TORCH_CHECK(weight.is_contiguous(), "weight tensor has to be contiguous"); const int batch = input.size(0); const int channels = input.size(1); const int height = input.size(2); const int width = input.size(3); const int channels_out = weight.size(0); const int channels_kernel = weight.size(1); const int kernel_h_ = weight.size(2); const int kernel_w_ = weight.size(3); if (kernel_h_ != kernel_h || kernel_w_ != kernel_w) AT_ERROR( "Input shape and kernel shape wont match: (%d x %d vs %d x %d).", kernel_h_, kernel_w, kernel_h_, kernel_w_); if (channels != channels_kernel * group) AT_ERROR( "Input shape and kernel channels wont match: (%d vs %d).", channels, channels_kernel * group); const int height_out = (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1; const int width_out = (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1; // mask shape check TORCH_CHECK( (mask.size(2) == height_out && mask.size(3) == width_out), "invalid spatial size of mask, expected height: %d width: %d, but " "got height: %d width: %d", height_out, width_out, mask.size(2), mask.size(3)); TORCH_CHECK( (mask.size(1) == deformable_group * kernel_h * kernel_w), "invalid number of channels of mask"); if (ones.ndimension() != 2 || ones.size(0) * ones.size(1) < height_out * width_out) { // Resize plane and fill with ones... ones = at::ones({height_out, width_out}, input.options()); } // resize output output = output.view({batch, channels_out, height_out, width_out}).zero_(); // resize temporary columns columns = at::zeros( {channels * kernel_h * kernel_w, 1 * height_out * width_out}, input.options()); output = output.view( {output.size(0), group, output.size(1) / group, output.size(2), output.size(3)}); for (int b = 0; b < batch; b++) { modulated_deformable_im2col_cuda( input[b], offset[b], mask[b], 1, channels, height, width, height_out, width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, deformable_group, columns); // divide into group weight = weight.view( {group, weight.size(0) / group, weight.size(1), weight.size(2), weight.size(3)}); columns = columns.view({group, columns.size(0) / group, columns.size(1)}); for (int g = 0; g < group; g++) { output[b][g] = output[b][g] .flatten(1) .addmm_(weight[g].flatten(1), columns[g]) .view_as(output[b][g]); } weight = weight.view( {weight.size(0) * weight.size(1), weight.size(2), weight.size(3), weight.size(4)}); columns = columns.view({columns.size(0) * columns.size(1), columns.size(2)}); } output = output.view( {output.size(0), output.size(1) * output.size(2), output.size(3), output.size(4)}); if (with_bias) { output += bias.view({1, bias.size(0), 1, 1}); } } void modulated_deform_conv_cuda_backward( at::Tensor input, at::Tensor weight, at::Tensor bias, at::Tensor ones, at::Tensor offset, at::Tensor mask, at::Tensor columns, at::Tensor grad_input, at::Tensor grad_weight, at::Tensor grad_bias, at::Tensor grad_offset, at::Tensor grad_mask, at::Tensor grad_output, int kernel_h, int kernel_w, int stride_h, int stride_w, int pad_h, int pad_w, int dilation_h, int dilation_w, int group, int deformable_group, const bool with_bias) { shape_check( input, offset, &grad_output, weight, kernel_h, kernel_w, stride_h, stride_w, pad_h, pad_w, dilation_h, dilation_w, group, deformable_group); TORCH_CHECK(input.is_contiguous(), "input tensor has to be contiguous"); TORCH_CHECK(weight.is_contiguous(), "weight tensor has to be contiguous"); const int batch = input.size(0); const int channels = input.size(1); const int height = input.size(2); const int width = input.size(3); const int channels_kernel = weight.size(1); const int kernel_h_ = weight.size(2); const int kernel_w_ = weight.size(3); if (kernel_h_ != kernel_h || kernel_w_ != kernel_w) AT_ERROR( "Input shape and kernel shape wont match: (%d x %d vs %d x %d).", kernel_h_, kernel_w, kernel_h_, kernel_w_); if (channels != channels_kernel * group) AT_ERROR( "Input shape and kernel channels wont match: (%d vs %d).", channels, channels_kernel * group); const int height_out = (height + 2 * pad_h - (dilation_h * (kernel_h - 1) + 1)) / stride_h + 1; const int width_out = (width + 2 * pad_w - (dilation_w * (kernel_w - 1) + 1)) / stride_w + 1; // mask shape check TORCH_CHECK( (mask.size(2) == height_out && mask.size(3) == width_out), "invalid spatial size of mask, expected height: %d width: %d, but " "got height: %d width: %d", height_out, width_out, mask.size(2), mask.size(3)); TORCH_CHECK( (mask.size(1) == deformable_group * kernel_h * kernel_w), "invalid number of channels of mask"); if (ones.ndimension() != 2 || ones.size(0) * ones.size(1) < height_out * width_out) { // Resize plane and fill with ones... ones = at::ones({height_out, width_out}, input.options()); } grad_input = grad_input.view({batch, channels, height, width}); columns = at::zeros( {channels * kernel_h * kernel_w, height_out * width_out}, input.options()); grad_output = grad_output.view( {grad_output.size(0), group, grad_output.size(1) / group, grad_output.size(2), grad_output.size(3)}); for (int b = 0; b < batch; b++) { // divide int group columns = columns.view({group, columns.size(0) / group, columns.size(1)}); weight = weight.view( {group, weight.size(0) / group, weight.size(1), weight.size(2), weight.size(3)}); for (int g = 0; g < group; g++) { columns[g].addmm_( weight[g].flatten(1).transpose(0, 1), grad_output[b][g].flatten(1), 0.0f, 1.0f); } columns = columns.view({columns.size(0) * columns.size(1), columns.size(2)}); weight = weight.view( {weight.size(0) * weight.size(1), weight.size(2), weight.size(3), weight.size(4)}); // gradient w.r.t. input coordinate data modulated_deformable_col2im_coord_cuda( columns, input[b], offset[b], mask[b], 1, channels, height, width, height_out, width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, deformable_group, grad_offset[b], grad_mask[b]); // gradient w.r.t. input data modulated_deformable_col2im_cuda( columns, offset[b], mask[b], 1, channels, height, width, height_out, width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, deformable_group, grad_input[b]); // gradient w.r.t. weight, dWeight should accumulate across the batch and // group modulated_deformable_im2col_cuda( input[b], offset[b], mask[b], 1, channels, height, width, height_out, width_out, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, deformable_group, columns); columns = columns.view({group, columns.size(0) / group, columns.size(1)}); grad_weight = grad_weight.view( {group, grad_weight.size(0) / group, grad_weight.size(1), grad_weight.size(2), grad_weight.size(3)}); if (with_bias) grad_bias = grad_bias.view({group, grad_bias.size(0) / group}); for (int g = 0; g < group; g++) { grad_weight[g] = grad_weight[g] .flatten(1) .addmm_(grad_output[b][g].flatten(1), columns[g].transpose(0, 1)) .view_as(grad_weight[g]); if (with_bias) { grad_bias[g] = grad_bias[g] .view({-1, 1}) .addmm_(grad_output[b][g].flatten(1), ones.view({-1, 1})) .view(-1); } } columns = columns.view({columns.size(0) * columns.size(1), columns.size(2)}); grad_weight = grad_weight.view( {grad_weight.size(0) * grad_weight.size(1), grad_weight.size(2), grad_weight.size(3), grad_weight.size(4)}); if (with_bias) grad_bias = grad_bias.view({grad_bias.size(0) * grad_bias.size(1)}); } grad_output = grad_output.view( {grad_output.size(0) * grad_output.size(1), grad_output.size(2), grad_output.size(3), grad_output.size(4)}); } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/deformable/deform_conv_cuda_kernel.cu ================================================ // Copyright (c) Facebook, Inc. and its affiliates. // modified from // https://github.com/open-mmlab/mmdetection/blob/master/mmdet/ops/dcn/src/deform_conv_cuda_kernel.cu // Original license: Apache 2.0 // clang-format off // modify from // https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/blob/mmdetection/mmdet/ops/dcn/src/deform_conv_cuda_kernel.cu /*! ******************* BEGIN Caffe Copyright Notice and Disclaimer ***************** * * COPYRIGHT * * All contributions by the University of California: * Copyright (c) 2014-2017 The Regents of the University of California (Regents) * All rights reserved. * * All other contributions: * Copyright (c) 2014-2017, the respective contributors * All rights reserved. * * Caffe uses a shared copyright model: each contributor holds copyright over * their contributions to Caffe. The project versioning records all such * contribution and copyright details. If a contributor wants to further mark * their specific copyright on a particular contribution, they should indicate * their copyright solely in the commit message of the change when it is * committed. * * LICENSE * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" *AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE *IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE *FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL *DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER *CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE *OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * CONTRIBUTION AGREEMENT * * By contributing to the BVLC/caffe repository through pull-request, comment, * or otherwise, the contributor releases their content to the * license and copyright terms herein. * ***************** END Caffe Copyright Notice and Disclaimer ********************* * * Copyright (c) 2018 Microsoft * Licensed under The MIT License [see LICENSE for details] * \file modulated_deformable_im2col.cuh * \brief Function definitions of converting an image to * column matrix based on kernel, padding, dilation, and offset. * These functions are mainly used in deformable convolution operators. * \ref: https://arxiv.org/abs/1703.06211 * \author Yuwen Xiong, Haozhi Qi, Jifeng Dai, Xizhou Zhu, Han Hu, Dazhi Cheng */ #include #include #include #include #include #include using namespace at; #define CUDA_KERNEL_LOOP(i, n) \ for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < (n); \ i += blockDim.x * gridDim.x) namespace { const int CUDA_NUM_THREADS = 1024; const int kMaxGridNum = 65535; inline int GET_BLOCKS(const int N) { return std::min(kMaxGridNum, (N + CUDA_NUM_THREADS - 1) / CUDA_NUM_THREADS); } } template __device__ scalar_t deformable_im2col_bilinear( const scalar_t* bottom_data, const int data_width, const int height, const int width, scalar_t h, scalar_t w) { int h_low = floor(h); int w_low = floor(w); int h_high = h_low + 1; int w_high = w_low + 1; scalar_t lh = h - h_low; scalar_t lw = w - w_low; scalar_t hh = 1 - lh, hw = 1 - lw; scalar_t v1 = 0; if (h_low >= 0 && w_low >= 0) v1 = bottom_data[h_low * data_width + w_low]; scalar_t v2 = 0; if (h_low >= 0 && w_high <= width - 1) v2 = bottom_data[h_low * data_width + w_high]; scalar_t v3 = 0; if (h_high <= height - 1 && w_low >= 0) v3 = bottom_data[h_high * data_width + w_low]; scalar_t v4 = 0; if (h_high <= height - 1 && w_high <= width - 1) v4 = bottom_data[h_high * data_width + w_high]; scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); return val; } template __device__ scalar_t get_gradient_weight( scalar_t argmax_h, scalar_t argmax_w, const int h, const int w, const int height, const int width) { if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) { // empty return 0; } int argmax_h_low = floor(argmax_h); int argmax_w_low = floor(argmax_w); int argmax_h_high = argmax_h_low + 1; int argmax_w_high = argmax_w_low + 1; scalar_t weight = 0; if (h == argmax_h_low && w == argmax_w_low) weight = (h + 1 - argmax_h) * (w + 1 - argmax_w); if (h == argmax_h_low && w == argmax_w_high) weight = (h + 1 - argmax_h) * (argmax_w + 1 - w); if (h == argmax_h_high && w == argmax_w_low) weight = (argmax_h + 1 - h) * (w + 1 - argmax_w); if (h == argmax_h_high && w == argmax_w_high) weight = (argmax_h + 1 - h) * (argmax_w + 1 - w); return weight; } template __device__ scalar_t get_coordinate_weight( scalar_t argmax_h, scalar_t argmax_w, const int height, const int width, const scalar_t* im_data, const int data_width, const int bp_dir) { if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) { // empty return 0; } int argmax_h_low = floor(argmax_h); int argmax_w_low = floor(argmax_w); int argmax_h_high = argmax_h_low + 1; int argmax_w_high = argmax_w_low + 1; scalar_t weight = 0; if (bp_dir == 0) { if (argmax_h_low >= 0 && argmax_w_low >= 0) weight += -1 * (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_low * data_width + argmax_w_low]; if (argmax_h_low >= 0 && argmax_w_high <= width - 1) weight += -1 * (argmax_w - argmax_w_low) * im_data[argmax_h_low * data_width + argmax_w_high]; if (argmax_h_high <= height - 1 && argmax_w_low >= 0) weight += (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_high * data_width + argmax_w_low]; if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) weight += (argmax_w - argmax_w_low) * im_data[argmax_h_high * data_width + argmax_w_high]; } else if (bp_dir == 1) { if (argmax_h_low >= 0 && argmax_w_low >= 0) weight += -1 * (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_low]; if (argmax_h_low >= 0 && argmax_w_high <= width - 1) weight += (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_high]; if (argmax_h_high <= height - 1 && argmax_w_low >= 0) weight += -1 * (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_low]; if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) weight += (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_high]; } return weight; } template __global__ void deformable_im2col_gpu_kernel( const int n, const scalar_t* data_im, const scalar_t* data_offset, const int height, const int width, const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int channel_per_deformable_group, const int batch_size, const int num_channels, const int deformable_group, const int height_col, const int width_col, scalar_t* data_col) { CUDA_KERNEL_LOOP(index, n) { // index index of output matrix const int w_col = index % width_col; const int h_col = (index / width_col) % height_col; const int b_col = (index / width_col / height_col) % batch_size; const int c_im = (index / width_col / height_col) / batch_size; const int c_col = c_im * kernel_h * kernel_w; // compute deformable group index const int deformable_group_index = c_im / channel_per_deformable_group; const int h_in = h_col * stride_h - pad_h; const int w_in = w_col * stride_w - pad_w; scalar_t* data_col_ptr = data_col + ((c_col * batch_size + b_col) * height_col + h_col) * width_col + w_col; // const scalar_t* data_im_ptr = data_im + ((b_col * num_channels + c_im) * // height + h_in) * width + w_in; const scalar_t* data_im_ptr = data_im + (b_col * num_channels + c_im) * height * width; const scalar_t* data_offset_ptr = data_offset + (b_col * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; for (int i = 0; i < kernel_h; ++i) { for (int j = 0; j < kernel_w; ++j) { const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_col) * width_col + w_col; const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_col) * width_col + w_col; const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; scalar_t val = static_cast(0); const scalar_t h_im = h_in + i * dilation_h + offset_h; const scalar_t w_im = w_in + j * dilation_w + offset_w; if (h_im > -1 && w_im > -1 && h_im < height && w_im < width) { // const scalar_t map_h = i * dilation_h + offset_h; // const scalar_t map_w = j * dilation_w + offset_w; // const int cur_height = height - h_in; // const int cur_width = width - w_in; // val = deformable_im2col_bilinear(data_im_ptr, width, cur_height, // cur_width, map_h, map_w); val = deformable_im2col_bilinear( data_im_ptr, width, height, width, h_im, w_im); } *data_col_ptr = val; data_col_ptr += batch_size * height_col * width_col; } } } } template __global__ void deformable_col2im_gpu_kernel( const int n, const scalar_t* data_col, const scalar_t* data_offset, const int channels, const int height, const int width, const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int channel_per_deformable_group, const int batch_size, const int deformable_group, const int height_col, const int width_col, scalar_t* grad_im) { CUDA_KERNEL_LOOP(index, n) { const int j = (index / width_col / height_col / batch_size) % kernel_w; const int i = (index / width_col / height_col / batch_size / kernel_w) % kernel_h; const int c = index / width_col / height_col / batch_size / kernel_w / kernel_h; // compute the start and end of the output const int deformable_group_index = c / channel_per_deformable_group; int w_out = index % width_col; int h_out = (index / width_col) % height_col; int b = (index / width_col / height_col) % batch_size; int w_in = w_out * stride_w - pad_w; int h_in = h_out * stride_h - pad_h; const scalar_t* data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out; const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out; const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; const scalar_t cur_inv_h_data = h_in + i * dilation_h + offset_h; const scalar_t cur_inv_w_data = w_in + j * dilation_w + offset_w; const scalar_t cur_top_grad = data_col[index]; const int cur_h = (int)cur_inv_h_data; const int cur_w = (int)cur_inv_w_data; for (int dy = -2; dy <= 2; dy++) { for (int dx = -2; dx <= 2; dx++) { if (cur_h + dy >= 0 && cur_h + dy < height && cur_w + dx >= 0 && cur_w + dx < width && abs(cur_inv_h_data - (cur_h + dy)) < 1 && abs(cur_inv_w_data - (cur_w + dx)) < 1) { int cur_bottom_grad_pos = ((b * channels + c) * height + cur_h + dy) * width + cur_w + dx; scalar_t weight = get_gradient_weight( cur_inv_h_data, cur_inv_w_data, cur_h + dy, cur_w + dx, height, width); atomicAdd(grad_im + cur_bottom_grad_pos, weight * cur_top_grad); } } } } } template __global__ void deformable_col2im_coord_gpu_kernel( const int n, const scalar_t* data_col, const scalar_t* data_im, const scalar_t* data_offset, const int channels, const int height, const int width, const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int channel_per_deformable_group, const int batch_size, const int offset_channels, const int deformable_group, const int height_col, const int width_col, scalar_t* grad_offset) { CUDA_KERNEL_LOOP(index, n) { scalar_t val = 0; int w = index % width_col; int h = (index / width_col) % height_col; int c = (index / width_col / height_col) % offset_channels; int b = (index / width_col / height_col) / offset_channels; // compute the start and end of the output const int deformable_group_index = c / (2 * kernel_h * kernel_w); const int col_step = kernel_h * kernel_w; int cnt = 0; const scalar_t* data_col_ptr = data_col + deformable_group_index * channel_per_deformable_group * batch_size * width_col * height_col; const scalar_t* data_im_ptr = data_im + (b * deformable_group + deformable_group_index) * channel_per_deformable_group / kernel_h / kernel_w * height * width; const scalar_t* data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; const int offset_c = c - deformable_group_index * 2 * kernel_h * kernel_w; for (int col_c = (offset_c / 2); col_c < channel_per_deformable_group; col_c += col_step) { const int col_pos = (((col_c * batch_size + b) * height_col) + h) * width_col + w; const int bp_dir = offset_c % 2; int j = (col_pos / width_col / height_col / batch_size) % kernel_w; int i = (col_pos / width_col / height_col / batch_size / kernel_w) % kernel_h; int w_out = col_pos % width_col; int h_out = (col_pos / width_col) % height_col; int w_in = w_out * stride_w - pad_w; int h_in = h_out * stride_h - pad_h; const int data_offset_h_ptr = (((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out); const int data_offset_w_ptr = (((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out); const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; scalar_t inv_h = h_in + i * dilation_h + offset_h; scalar_t inv_w = w_in + j * dilation_w + offset_w; if (inv_h <= -1 || inv_w <= -1 || inv_h >= height || inv_w >= width) { inv_h = inv_w = -2; } const scalar_t weight = get_coordinate_weight( inv_h, inv_w, height, width, data_im_ptr + cnt * height * width, width, bp_dir); val += weight * data_col_ptr[col_pos]; cnt += 1; } grad_offset[index] = val; } } namespace detectron2 { void deformable_im2col( const at::Tensor data_im, const at::Tensor data_offset, const int channels, const int height, const int width, const int ksize_h, const int ksize_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int parallel_imgs, const int deformable_group, at::Tensor data_col) { // num_axes should be smaller than block size // todo: check parallel_imgs is correctly passed in int height_col = (height + 2 * pad_h - (dilation_h * (ksize_h - 1) + 1)) / stride_h + 1; int width_col = (width + 2 * pad_w - (dilation_w * (ksize_w - 1) + 1)) / stride_w + 1; int num_kernels = channels * height_col * width_col * parallel_imgs; int channel_per_deformable_group = channels / deformable_group; at::cuda::CUDAGuard device_guard(data_im.device()); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); AT_DISPATCH_FLOATING_TYPES_AND_HALF( data_im.scalar_type(), "deformable_im2col_gpu", ([&] { const scalar_t* data_im_ = data_im.data_ptr(); const scalar_t* data_offset_ = data_offset.data_ptr(); scalar_t* data_col_ = data_col.data_ptr(); deformable_im2col_gpu_kernel<<< GET_BLOCKS(num_kernels), CUDA_NUM_THREADS, 0, stream>>>( num_kernels, data_im_, data_offset_, height, width, ksize_h, ksize_w, pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, channel_per_deformable_group, parallel_imgs, channels, deformable_group, height_col, width_col, data_col_); })); cudaError_t err = cudaGetLastError(); if (err != cudaSuccess) { printf("error in deformable_im2col: %s\n", cudaGetErrorString(err)); } } void deformable_col2im( const at::Tensor data_col, const at::Tensor data_offset, const int channels, const int height, const int width, const int ksize_h, const int ksize_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int parallel_imgs, const int deformable_group, at::Tensor grad_im) { // todo: make sure parallel_imgs is passed in correctly int height_col = (height + 2 * pad_h - (dilation_h * (ksize_h - 1) + 1)) / stride_h + 1; int width_col = (width + 2 * pad_w - (dilation_w * (ksize_w - 1) + 1)) / stride_w + 1; int num_kernels = channels * ksize_h * ksize_w * height_col * width_col * parallel_imgs; int channel_per_deformable_group = channels / deformable_group; at::cuda::CUDAGuard device_guard(data_col.device()); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); AT_DISPATCH_FLOATING_TYPES_AND_HALF( data_col.scalar_type(), "deformable_col2im_gpu", ([&] { const scalar_t* data_col_ = data_col.data_ptr(); const scalar_t* data_offset_ = data_offset.data_ptr(); scalar_t* grad_im_ = grad_im.data_ptr(); deformable_col2im_gpu_kernel<<< GET_BLOCKS(num_kernels), CUDA_NUM_THREADS, 0, stream>>>( num_kernels, data_col_, data_offset_, channels, height, width, ksize_h, ksize_w, pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, channel_per_deformable_group, parallel_imgs, deformable_group, height_col, width_col, grad_im_); })); cudaError_t err = cudaGetLastError(); if (err != cudaSuccess) { printf("error in deformable_col2im: %s\n", cudaGetErrorString(err)); } } void deformable_col2im_coord( const at::Tensor data_col, const at::Tensor data_im, const at::Tensor data_offset, const int channels, const int height, const int width, const int ksize_h, const int ksize_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int parallel_imgs, const int deformable_group, at::Tensor grad_offset) { int height_col = (height + 2 * pad_h - (dilation_h * (ksize_h - 1) + 1)) / stride_h + 1; int width_col = (width + 2 * pad_w - (dilation_w * (ksize_w - 1) + 1)) / stride_w + 1; int num_kernels = height_col * width_col * 2 * ksize_h * ksize_w * deformable_group * parallel_imgs; int channel_per_deformable_group = channels * ksize_h * ksize_w / deformable_group; at::cuda::CUDAGuard device_guard(data_col.device()); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); AT_DISPATCH_FLOATING_TYPES_AND_HALF( data_col.scalar_type(), "deformable_col2im_coord_gpu", ([&] { const scalar_t* data_col_ = data_col.data_ptr(); const scalar_t* data_im_ = data_im.data_ptr(); const scalar_t* data_offset_ = data_offset.data_ptr(); scalar_t* grad_offset_ = grad_offset.data_ptr(); deformable_col2im_coord_gpu_kernel<<< GET_BLOCKS(num_kernels), CUDA_NUM_THREADS, 0, stream>>>( num_kernels, data_col_, data_im_, data_offset_, channels, height, width, ksize_h, ksize_w, pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, channel_per_deformable_group, parallel_imgs, 2 * ksize_h * ksize_w * deformable_group, deformable_group, height_col, width_col, grad_offset_); })); } } // namespace detectron2 template __device__ scalar_t dmcn_im2col_bilinear( const scalar_t* bottom_data, const int data_width, const int height, const int width, scalar_t h, scalar_t w) { int h_low = floor(h); int w_low = floor(w); int h_high = h_low + 1; int w_high = w_low + 1; scalar_t lh = h - h_low; scalar_t lw = w - w_low; scalar_t hh = 1 - lh, hw = 1 - lw; scalar_t v1 = 0; if (h_low >= 0 && w_low >= 0) v1 = bottom_data[h_low * data_width + w_low]; scalar_t v2 = 0; if (h_low >= 0 && w_high <= width - 1) v2 = bottom_data[h_low * data_width + w_high]; scalar_t v3 = 0; if (h_high <= height - 1 && w_low >= 0) v3 = bottom_data[h_high * data_width + w_low]; scalar_t v4 = 0; if (h_high <= height - 1 && w_high <= width - 1) v4 = bottom_data[h_high * data_width + w_high]; scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); return val; } template __device__ scalar_t dmcn_get_gradient_weight( scalar_t argmax_h, scalar_t argmax_w, const int h, const int w, const int height, const int width) { if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) { // empty return 0; } int argmax_h_low = floor(argmax_h); int argmax_w_low = floor(argmax_w); int argmax_h_high = argmax_h_low + 1; int argmax_w_high = argmax_w_low + 1; scalar_t weight = 0; if (h == argmax_h_low && w == argmax_w_low) weight = (h + 1 - argmax_h) * (w + 1 - argmax_w); if (h == argmax_h_low && w == argmax_w_high) weight = (h + 1 - argmax_h) * (argmax_w + 1 - w); if (h == argmax_h_high && w == argmax_w_low) weight = (argmax_h + 1 - h) * (w + 1 - argmax_w); if (h == argmax_h_high && w == argmax_w_high) weight = (argmax_h + 1 - h) * (argmax_w + 1 - w); return weight; } template __device__ scalar_t dmcn_get_coordinate_weight( scalar_t argmax_h, scalar_t argmax_w, const int height, const int width, const scalar_t* im_data, const int data_width, const int bp_dir) { if (argmax_h <= -1 || argmax_h >= height || argmax_w <= -1 || argmax_w >= width) { // empty return 0; } int argmax_h_low = floor(argmax_h); int argmax_w_low = floor(argmax_w); int argmax_h_high = argmax_h_low + 1; int argmax_w_high = argmax_w_low + 1; scalar_t weight = 0; if (bp_dir == 0) { if (argmax_h_low >= 0 && argmax_w_low >= 0) weight += -1 * (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_low * data_width + argmax_w_low]; if (argmax_h_low >= 0 && argmax_w_high <= width - 1) weight += -1 * (argmax_w - argmax_w_low) * im_data[argmax_h_low * data_width + argmax_w_high]; if (argmax_h_high <= height - 1 && argmax_w_low >= 0) weight += (argmax_w_low + 1 - argmax_w) * im_data[argmax_h_high * data_width + argmax_w_low]; if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) weight += (argmax_w - argmax_w_low) * im_data[argmax_h_high * data_width + argmax_w_high]; } else if (bp_dir == 1) { if (argmax_h_low >= 0 && argmax_w_low >= 0) weight += -1 * (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_low]; if (argmax_h_low >= 0 && argmax_w_high <= width - 1) weight += (argmax_h_low + 1 - argmax_h) * im_data[argmax_h_low * data_width + argmax_w_high]; if (argmax_h_high <= height - 1 && argmax_w_low >= 0) weight += -1 * (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_low]; if (argmax_h_high <= height - 1 && argmax_w_high <= width - 1) weight += (argmax_h - argmax_h_low) * im_data[argmax_h_high * data_width + argmax_w_high]; } return weight; } template __global__ void modulated_deformable_im2col_gpu_kernel( const int n, const scalar_t* data_im, const scalar_t* data_offset, const scalar_t* data_mask, const int height, const int width, const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int channel_per_deformable_group, const int batch_size, const int num_channels, const int deformable_group, const int height_col, const int width_col, scalar_t* data_col) { CUDA_KERNEL_LOOP(index, n) { // index index of output matrix const int w_col = index % width_col; const int h_col = (index / width_col) % height_col; const int b_col = (index / width_col / height_col) % batch_size; const int c_im = (index / width_col / height_col) / batch_size; const int c_col = c_im * kernel_h * kernel_w; // compute deformable group index const int deformable_group_index = c_im / channel_per_deformable_group; const int h_in = h_col * stride_h - pad_h; const int w_in = w_col * stride_w - pad_w; scalar_t* data_col_ptr = data_col + ((c_col * batch_size + b_col) * height_col + h_col) * width_col + w_col; // const float* data_im_ptr = data_im + ((b_col * num_channels + c_im) * // height + h_in) * width + w_in; const scalar_t* data_im_ptr = data_im + (b_col * num_channels + c_im) * height * width; const scalar_t* data_offset_ptr = data_offset + (b_col * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; const scalar_t* data_mask_ptr = data_mask + (b_col * deformable_group + deformable_group_index) * kernel_h * kernel_w * height_col * width_col; for (int i = 0; i < kernel_h; ++i) { for (int j = 0; j < kernel_w; ++j) { const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_col) * width_col + w_col; const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_col) * width_col + w_col; const int data_mask_hw_ptr = ((i * kernel_w + j) * height_col + h_col) * width_col + w_col; const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; const scalar_t mask = data_mask_ptr[data_mask_hw_ptr]; scalar_t val = static_cast(0); const scalar_t h_im = h_in + i * dilation_h + offset_h; const scalar_t w_im = w_in + j * dilation_w + offset_w; // if (h_im >= 0 && w_im >= 0 && h_im < height && w_im < width) { if (h_im > -1 && w_im > -1 && h_im < height && w_im < width) { // const float map_h = i * dilation_h + offset_h; // const float map_w = j * dilation_w + offset_w; // const int cur_height = height - h_in; // const int cur_width = width - w_in; // val = dmcn_im2col_bilinear(data_im_ptr, width, cur_height, // cur_width, map_h, map_w); val = dmcn_im2col_bilinear( data_im_ptr, width, height, width, h_im, w_im); } *data_col_ptr = val * mask; data_col_ptr += batch_size * height_col * width_col; // data_col_ptr += height_col * width_col; } } } } template __global__ void modulated_deformable_col2im_gpu_kernel( const int n, const scalar_t* data_col, const scalar_t* data_offset, const scalar_t* data_mask, const int channels, const int height, const int width, const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int channel_per_deformable_group, const int batch_size, const int deformable_group, const int height_col, const int width_col, scalar_t* grad_im) { CUDA_KERNEL_LOOP(index, n) { const int j = (index / width_col / height_col / batch_size) % kernel_w; const int i = (index / width_col / height_col / batch_size / kernel_w) % kernel_h; const int c = index / width_col / height_col / batch_size / kernel_w / kernel_h; // compute the start and end of the output const int deformable_group_index = c / channel_per_deformable_group; int w_out = index % width_col; int h_out = (index / width_col) % height_col; int b = (index / width_col / height_col) % batch_size; int w_in = w_out * stride_w - pad_w; int h_in = h_out * stride_h - pad_h; const scalar_t* data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; const scalar_t* data_mask_ptr = data_mask + (b * deformable_group + deformable_group_index) * kernel_h * kernel_w * height_col * width_col; const int data_offset_h_ptr = ((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out; const int data_offset_w_ptr = ((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out; const int data_mask_hw_ptr = ((i * kernel_w + j) * height_col + h_out) * width_col + w_out; const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; const scalar_t mask = data_mask_ptr[data_mask_hw_ptr]; const scalar_t cur_inv_h_data = h_in + i * dilation_h + offset_h; const scalar_t cur_inv_w_data = w_in + j * dilation_w + offset_w; const scalar_t cur_top_grad = data_col[index] * mask; const int cur_h = (int)cur_inv_h_data; const int cur_w = (int)cur_inv_w_data; for (int dy = -2; dy <= 2; dy++) { for (int dx = -2; dx <= 2; dx++) { if (cur_h + dy >= 0 && cur_h + dy < height && cur_w + dx >= 0 && cur_w + dx < width && abs(cur_inv_h_data - (cur_h + dy)) < 1 && abs(cur_inv_w_data - (cur_w + dx)) < 1) { int cur_bottom_grad_pos = ((b * channels + c) * height + cur_h + dy) * width + cur_w + dx; scalar_t weight = dmcn_get_gradient_weight( cur_inv_h_data, cur_inv_w_data, cur_h + dy, cur_w + dx, height, width); atomicAdd(grad_im + cur_bottom_grad_pos, weight * cur_top_grad); } } } } } template __global__ void modulated_deformable_col2im_coord_gpu_kernel( const int n, const scalar_t* data_col, const scalar_t* data_im, const scalar_t* data_offset, const scalar_t* data_mask, const int channels, const int height, const int width, const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int channel_per_deformable_group, const int batch_size, const int offset_channels, const int deformable_group, const int height_col, const int width_col, scalar_t* grad_offset, scalar_t* grad_mask) { CUDA_KERNEL_LOOP(index, n) { scalar_t val = 0, mval = 0; int w = index % width_col; int h = (index / width_col) % height_col; int c = (index / width_col / height_col) % offset_channels; int b = (index / width_col / height_col) / offset_channels; // compute the start and end of the output const int deformable_group_index = c / (2 * kernel_h * kernel_w); const int col_step = kernel_h * kernel_w; int cnt = 0; const scalar_t* data_col_ptr = data_col + deformable_group_index * channel_per_deformable_group * batch_size * width_col * height_col; const scalar_t* data_im_ptr = data_im + (b * deformable_group + deformable_group_index) * channel_per_deformable_group / kernel_h / kernel_w * height * width; const scalar_t* data_offset_ptr = data_offset + (b * deformable_group + deformable_group_index) * 2 * kernel_h * kernel_w * height_col * width_col; const scalar_t* data_mask_ptr = data_mask + (b * deformable_group + deformable_group_index) * kernel_h * kernel_w * height_col * width_col; const int offset_c = c - deformable_group_index * 2 * kernel_h * kernel_w; for (int col_c = (offset_c / 2); col_c < channel_per_deformable_group; col_c += col_step) { const int col_pos = (((col_c * batch_size + b) * height_col) + h) * width_col + w; const int bp_dir = offset_c % 2; int j = (col_pos / width_col / height_col / batch_size) % kernel_w; int i = (col_pos / width_col / height_col / batch_size / kernel_w) % kernel_h; int w_out = col_pos % width_col; int h_out = (col_pos / width_col) % height_col; int w_in = w_out * stride_w - pad_w; int h_in = h_out * stride_h - pad_h; const int data_offset_h_ptr = (((2 * (i * kernel_w + j)) * height_col + h_out) * width_col + w_out); const int data_offset_w_ptr = (((2 * (i * kernel_w + j) + 1) * height_col + h_out) * width_col + w_out); const int data_mask_hw_ptr = (((i * kernel_w + j) * height_col + h_out) * width_col + w_out); const scalar_t offset_h = data_offset_ptr[data_offset_h_ptr]; const scalar_t offset_w = data_offset_ptr[data_offset_w_ptr]; const scalar_t mask = data_mask_ptr[data_mask_hw_ptr]; scalar_t inv_h = h_in + i * dilation_h + offset_h; scalar_t inv_w = w_in + j * dilation_w + offset_w; if (inv_h <= -1 || inv_w <= -1 || inv_h >= height || inv_w >= width) { inv_h = inv_w = -2; } else { mval += data_col_ptr[col_pos] * dmcn_im2col_bilinear( data_im_ptr + cnt * height * width, width, height, width, inv_h, inv_w); } const scalar_t weight = dmcn_get_coordinate_weight( inv_h, inv_w, height, width, data_im_ptr + cnt * height * width, width, bp_dir); val += weight * data_col_ptr[col_pos] * mask; cnt += 1; } // KERNEL_ASSIGN(grad_offset[index], offset_req, val); grad_offset[index] = val; if (offset_c % 2 == 0) // KERNEL_ASSIGN(grad_mask[(((b * deformable_group + // deformable_group_index) * kernel_h * kernel_w + offset_c / 2) * // height_col + h) * width_col + w], mask_req, mval); grad_mask [(((b * deformable_group + deformable_group_index) * kernel_h * kernel_w + offset_c / 2) * height_col + h) * width_col + w] = mval; } } namespace detectron2 { void modulated_deformable_im2col_cuda( const at::Tensor data_im, const at::Tensor data_offset, const at::Tensor data_mask, const int batch_size, const int channels, const int height_im, const int width_im, const int height_col, const int width_col, const int kernel_h, const int kenerl_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int deformable_group, at::Tensor data_col) { // num_axes should be smaller than block size const int channel_per_deformable_group = channels / deformable_group; const int num_kernels = channels * batch_size * height_col * width_col; at::cuda::CUDAGuard device_guard(data_im.device()); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); AT_DISPATCH_FLOATING_TYPES_AND_HALF( data_im.scalar_type(), "modulated_deformable_im2col_gpu", ([&] { const scalar_t* data_im_ = data_im.data_ptr(); const scalar_t* data_offset_ = data_offset.data_ptr(); const scalar_t* data_mask_ = data_mask.data_ptr(); scalar_t* data_col_ = data_col.data_ptr(); modulated_deformable_im2col_gpu_kernel<<< GET_BLOCKS(num_kernels), CUDA_NUM_THREADS, 0, stream>>>( num_kernels, data_im_, data_offset_, data_mask_, height_im, width_im, kernel_h, kenerl_w, pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, channel_per_deformable_group, batch_size, channels, deformable_group, height_col, width_col, data_col_); })); cudaError_t err = cudaGetLastError(); if (err != cudaSuccess) { printf( "error in modulated_deformable_im2col_cuda: %s\n", cudaGetErrorString(err)); } } void modulated_deformable_col2im_cuda( const at::Tensor data_col, const at::Tensor data_offset, const at::Tensor data_mask, const int batch_size, const int channels, const int height_im, const int width_im, const int height_col, const int width_col, const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int deformable_group, at::Tensor grad_im) { const int channel_per_deformable_group = channels / deformable_group; const int num_kernels = channels * kernel_h * kernel_w * batch_size * height_col * width_col; at::cuda::CUDAGuard device_guard(data_col.device()); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); AT_DISPATCH_FLOATING_TYPES_AND_HALF( data_col.scalar_type(), "modulated_deformable_col2im_gpu", ([&] { const scalar_t* data_col_ = data_col.data_ptr(); const scalar_t* data_offset_ = data_offset.data_ptr(); const scalar_t* data_mask_ = data_mask.data_ptr(); scalar_t* grad_im_ = grad_im.data_ptr(); modulated_deformable_col2im_gpu_kernel<<< GET_BLOCKS(num_kernels), CUDA_NUM_THREADS, 0, stream>>>( num_kernels, data_col_, data_offset_, data_mask_, channels, height_im, width_im, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, channel_per_deformable_group, batch_size, deformable_group, height_col, width_col, grad_im_); })); cudaError_t err = cudaGetLastError(); if (err != cudaSuccess) { printf( "error in modulated_deformable_col2im_cuda: %s\n", cudaGetErrorString(err)); } } void modulated_deformable_col2im_coord_cuda( const at::Tensor data_col, const at::Tensor data_im, const at::Tensor data_offset, const at::Tensor data_mask, const int batch_size, const int channels, const int height_im, const int width_im, const int height_col, const int width_col, const int kernel_h, const int kernel_w, const int pad_h, const int pad_w, const int stride_h, const int stride_w, const int dilation_h, const int dilation_w, const int deformable_group, at::Tensor grad_offset, at::Tensor grad_mask) { const int num_kernels = batch_size * height_col * width_col * 2 * kernel_h * kernel_w * deformable_group; const int channel_per_deformable_group = channels * kernel_h * kernel_w / deformable_group; at::cuda::CUDAGuard device_guard(data_col.device()); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); AT_DISPATCH_FLOATING_TYPES_AND_HALF( data_col.scalar_type(), "modulated_deformable_col2im_coord_gpu", ([&] { const scalar_t* data_col_ = data_col.data_ptr(); const scalar_t* data_im_ = data_im.data_ptr(); const scalar_t* data_offset_ = data_offset.data_ptr(); const scalar_t* data_mask_ = data_mask.data_ptr(); scalar_t* grad_offset_ = grad_offset.data_ptr(); scalar_t* grad_mask_ = grad_mask.data_ptr(); modulated_deformable_col2im_coord_gpu_kernel<<< GET_BLOCKS(num_kernels), CUDA_NUM_THREADS, 0, stream>>>( num_kernels, data_col_, data_im_, data_offset_, data_mask_, channels, height_im, width_im, kernel_h, kernel_w, pad_h, pad_w, stride_h, stride_w, dilation_h, dilation_w, channel_per_deformable_group, batch_size, 2 * kernel_h * kernel_w * deformable_group, deformable_group, height_col, width_col, grad_offset_, grad_mask_); })); cudaError_t err = cudaGetLastError(); if (err != cudaSuccess) { printf( "error in modulated_deformable_col2im_coord_cuda: %s\n", cudaGetErrorString(err)); } } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/nms_rotated/nms_rotated.h ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #pragma once #include namespace detectron2 { at::Tensor nms_rotated_cpu( const at::Tensor& dets, const at::Tensor& scores, const double iou_threshold); #if defined(WITH_CUDA) || defined(WITH_HIP) at::Tensor nms_rotated_cuda( const at::Tensor& dets, const at::Tensor& scores, const double iou_threshold); #endif // Interface for Python // inline is needed to prevent multiple function definitions when this header is // included by different cpps inline at::Tensor nms_rotated( const at::Tensor& dets, const at::Tensor& scores, const double iou_threshold) { assert(dets.device().is_cuda() == scores.device().is_cuda()); if (dets.device().is_cuda()) { #if defined(WITH_CUDA) || defined(WITH_HIP) return nms_rotated_cuda( dets.contiguous(), scores.contiguous(), iou_threshold); #else AT_ERROR("Detectron2 is not compiled with GPU support!"); #endif } return nms_rotated_cpu(dets.contiguous(), scores.contiguous(), iou_threshold); } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/nms_rotated/nms_rotated_cpu.cpp ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #include "../box_iou_rotated/box_iou_rotated_utils.h" #include "nms_rotated.h" namespace detectron2 { template at::Tensor nms_rotated_cpu_kernel( const at::Tensor& dets, const at::Tensor& scores, const double iou_threshold) { // nms_rotated_cpu_kernel is modified from torchvision's nms_cpu_kernel, // however, the code in this function is much shorter because // we delegate the IoU computation for rotated boxes to // the single_box_iou_rotated function in box_iou_rotated_utils.h AT_ASSERTM(dets.device().is_cpu(), "dets must be a CPU tensor"); AT_ASSERTM(scores.device().is_cpu(), "scores must be a CPU tensor"); AT_ASSERTM( dets.scalar_type() == scores.scalar_type(), "dets should have the same type as scores"); if (dets.numel() == 0) { return at::empty({0}, dets.options().dtype(at::kLong)); } auto order_t = std::get<1>(scores.sort(0, /* descending=*/true)); auto ndets = dets.size(0); at::Tensor suppressed_t = at::zeros({ndets}, dets.options().dtype(at::kByte)); at::Tensor keep_t = at::zeros({ndets}, dets.options().dtype(at::kLong)); auto suppressed = suppressed_t.data_ptr(); auto keep = keep_t.data_ptr(); auto order = order_t.data_ptr(); int64_t num_to_keep = 0; for (int64_t _i = 0; _i < ndets; _i++) { auto i = order[_i]; if (suppressed[i] == 1) { continue; } keep[num_to_keep++] = i; for (int64_t _j = _i + 1; _j < ndets; _j++) { auto j = order[_j]; if (suppressed[j] == 1) { continue; } auto ovr = single_box_iou_rotated( dets[i].data_ptr(), dets[j].data_ptr()); if (ovr >= iou_threshold) { suppressed[j] = 1; } } } return keep_t.narrow(/*dim=*/0, /*start=*/0, /*length=*/num_to_keep); } at::Tensor nms_rotated_cpu( // input must be contiguous const at::Tensor& dets, const at::Tensor& scores, const double iou_threshold) { auto result = at::empty({0}, dets.options()); AT_DISPATCH_FLOATING_TYPES(dets.scalar_type(), "nms_rotated", [&] { result = nms_rotated_cpu_kernel(dets, scores, iou_threshold); }); return result; } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/nms_rotated/nms_rotated_cuda.cu ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #include #include #include #include #ifdef WITH_CUDA #include "../box_iou_rotated/box_iou_rotated_utils.h" #endif // TODO avoid this when pytorch supports "same directory" hipification #ifdef WITH_HIP #include "box_iou_rotated/box_iou_rotated_utils.h" #endif using namespace detectron2; namespace { int const threadsPerBlock = sizeof(unsigned long long) * 8; } template __global__ void nms_rotated_cuda_kernel( const int n_boxes, const double iou_threshold, const T* dev_boxes, unsigned long long* dev_mask) { // nms_rotated_cuda_kernel is modified from torchvision's nms_cuda_kernel const int row_start = blockIdx.y; const int col_start = blockIdx.x; // if (row_start > col_start) return; const int row_size = min(n_boxes - row_start * threadsPerBlock, threadsPerBlock); const int col_size = min(n_boxes - col_start * threadsPerBlock, threadsPerBlock); // Compared to nms_cuda_kernel, where each box is represented with 4 values // (x1, y1, x2, y2), each rotated box is represented with 5 values // (x_center, y_center, width, height, angle_degrees) here. __shared__ T block_boxes[threadsPerBlock * 5]; if (threadIdx.x < col_size) { block_boxes[threadIdx.x * 5 + 0] = dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 0]; block_boxes[threadIdx.x * 5 + 1] = dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 1]; block_boxes[threadIdx.x * 5 + 2] = dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 2]; block_boxes[threadIdx.x * 5 + 3] = dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 3]; block_boxes[threadIdx.x * 5 + 4] = dev_boxes[(threadsPerBlock * col_start + threadIdx.x) * 5 + 4]; } __syncthreads(); if (threadIdx.x < row_size) { const int cur_box_idx = threadsPerBlock * row_start + threadIdx.x; const T* cur_box = dev_boxes + cur_box_idx * 5; int i = 0; unsigned long long t = 0; int start = 0; if (row_start == col_start) { start = threadIdx.x + 1; } for (i = start; i < col_size; i++) { // Instead of devIoU used by original horizontal nms, here // we use the single_box_iou_rotated function from box_iou_rotated_utils.h if (single_box_iou_rotated(cur_box, block_boxes + i * 5) > iou_threshold) { t |= 1ULL << i; } } const int col_blocks = at::cuda::ATenCeilDiv(n_boxes, threadsPerBlock); dev_mask[cur_box_idx * col_blocks + col_start] = t; } } namespace detectron2 { at::Tensor nms_rotated_cuda( // input must be contiguous const at::Tensor& dets, const at::Tensor& scores, double iou_threshold) { // using scalar_t = float; AT_ASSERTM(dets.is_cuda(), "dets must be a CUDA tensor"); AT_ASSERTM(scores.is_cuda(), "scores must be a CUDA tensor"); at::cuda::CUDAGuard device_guard(dets.device()); auto order_t = std::get<1>(scores.sort(0, /* descending=*/true)); auto dets_sorted = dets.index_select(0, order_t); auto dets_num = dets.size(0); const int col_blocks = at::cuda::ATenCeilDiv(static_cast(dets_num), threadsPerBlock); at::Tensor mask = at::empty({dets_num * col_blocks}, dets.options().dtype(at::kLong)); dim3 blocks(col_blocks, col_blocks); dim3 threads(threadsPerBlock); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); AT_DISPATCH_FLOATING_TYPES( dets_sorted.scalar_type(), "nms_rotated_kernel_cuda", [&] { nms_rotated_cuda_kernel<<>>( dets_num, iou_threshold, dets_sorted.data_ptr(), (unsigned long long*)mask.data_ptr()); }); at::Tensor mask_cpu = mask.to(at::kCPU); unsigned long long* mask_host = (unsigned long long*)mask_cpu.data_ptr(); std::vector remv(col_blocks); memset(&remv[0], 0, sizeof(unsigned long long) * col_blocks); at::Tensor keep = at::empty({dets_num}, dets.options().dtype(at::kLong).device(at::kCPU)); int64_t* keep_out = keep.data_ptr(); int num_to_keep = 0; for (int i = 0; i < dets_num; i++) { int nblock = i / threadsPerBlock; int inblock = i % threadsPerBlock; if (!(remv[nblock] & (1ULL << inblock))) { keep_out[num_to_keep++] = i; unsigned long long* p = mask_host + i * col_blocks; for (int j = nblock; j < col_blocks; j++) { remv[j] |= p[j]; } } } AT_CUDA_CHECK(cudaGetLastError()); return order_t.index( {keep.narrow(/*dim=*/0, /*start=*/0, /*length=*/num_to_keep) .to(order_t.device(), keep.scalar_type())}); } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/csrc/vision.cpp ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #include #include "ROIAlignRotated/ROIAlignRotated.h" #include "box_iou_rotated/box_iou_rotated.h" #include "cocoeval/cocoeval.h" #include "deformable/deform_conv.h" #include "nms_rotated/nms_rotated.h" namespace detectron2 { #if defined(WITH_CUDA) || defined(WITH_HIP) extern int get_cudart_version(); #endif std::string get_cuda_version() { #if defined(WITH_CUDA) || defined(WITH_HIP) std::ostringstream oss; #if defined(WITH_CUDA) oss << "CUDA "; #else oss << "HIP "; #endif // copied from // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/cuda/detail/CUDAHooks.cpp#L231 auto printCudaStyleVersion = [&](int v) { oss << (v / 1000) << "." << (v / 10 % 100); if (v % 10 != 0) { oss << "." << (v % 10); } }; printCudaStyleVersion(get_cudart_version()); return oss.str(); #else // neither CUDA nor HIP return std::string("not available"); #endif } bool has_cuda() { #if defined(WITH_CUDA) return true; #else return false; #endif } // similar to // https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/Version.cpp std::string get_compiler_version() { std::ostringstream ss; #if defined(__GNUC__) #ifndef __clang__ #if ((__GNUC__ <= 4) && (__GNUC_MINOR__ <= 8)) #error "GCC >= 4.9 is required!" #endif { ss << "GCC " << __GNUC__ << "." << __GNUC_MINOR__; } #endif #endif #if defined(__clang_major__) { ss << "clang " << __clang_major__ << "." << __clang_minor__ << "." << __clang_patchlevel__; } #endif #if defined(_MSC_VER) { ss << "MSVC " << _MSC_FULL_VER; } #endif return ss.str(); } PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { m.def("get_compiler_version", &get_compiler_version, "get_compiler_version"); m.def("get_cuda_version", &get_cuda_version, "get_cuda_version"); m.def("has_cuda", &has_cuda, "has_cuda"); m.def("deform_conv_forward", &deform_conv_forward, "deform_conv_forward"); m.def( "deform_conv_backward_input", &deform_conv_backward_input, "deform_conv_backward_input"); m.def( "deform_conv_backward_filter", &deform_conv_backward_filter, "deform_conv_backward_filter"); m.def( "modulated_deform_conv_forward", &modulated_deform_conv_forward, "modulated_deform_conv_forward"); m.def( "modulated_deform_conv_backward", &modulated_deform_conv_backward, "modulated_deform_conv_backward"); m.def("COCOevalAccumulate", &COCOeval::Accumulate, "COCOeval::Accumulate"); m.def( "COCOevalEvaluateImages", &COCOeval::EvaluateImages, "COCOeval::EvaluateImages"); pybind11::class_(m, "InstanceAnnotation") .def(pybind11::init()); pybind11::class_(m, "ImageEvaluation") .def(pybind11::init<>()); } TORCH_LIBRARY(detectron2, m) { m.def("nms_rotated", &nms_rotated); m.def("box_iou_rotated", &box_iou_rotated); m.def("roi_align_rotated_forward", &ROIAlignRotated_forward); m.def("roi_align_rotated_backward", &ROIAlignRotated_backward); } } // namespace detectron2 ================================================ FILE: detectron2/detectron2/layers/deform_conv.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import math from functools import lru_cache import torch from torch import nn from torch.autograd import Function from torch.autograd.function import once_differentiable from torch.nn.modules.utils import _pair from torchvision.ops import deform_conv2d from detectron2.utils.develop import create_dummy_class, create_dummy_func from .wrappers import _NewEmptyTensorOp class _DeformConv(Function): @staticmethod def forward( ctx, input, offset, weight, stride=1, padding=0, dilation=1, groups=1, deformable_groups=1, im2col_step=64, ): if input is not None and input.dim() != 4: raise ValueError( "Expected 4D tensor as input, got {}D tensor instead.".format(input.dim()) ) ctx.stride = _pair(stride) ctx.padding = _pair(padding) ctx.dilation = _pair(dilation) ctx.groups = groups ctx.deformable_groups = deformable_groups ctx.im2col_step = im2col_step ctx.save_for_backward(input, offset, weight) output = input.new_empty( _DeformConv._output_size(input, weight, ctx.padding, ctx.dilation, ctx.stride) ) ctx.bufs_ = [input.new_empty(0), input.new_empty(0)] # columns, ones if not input.is_cuda: # TODO: let torchvision support full features of our deformconv. if deformable_groups != 1: raise NotImplementedError( "Deformable Conv with deformable_groups != 1 is not supported on CPUs!" ) return deform_conv2d( input, offset, weight, stride=stride, padding=padding, dilation=dilation ) else: cur_im2col_step = _DeformConv._cal_im2col_step(input.shape[0], ctx.im2col_step) assert (input.shape[0] % cur_im2col_step) == 0, "im2col step must divide batchsize" _C.deform_conv_forward( input, weight, offset, output, ctx.bufs_[0], ctx.bufs_[1], weight.size(3), weight.size(2), ctx.stride[1], ctx.stride[0], ctx.padding[1], ctx.padding[0], ctx.dilation[1], ctx.dilation[0], ctx.groups, ctx.deformable_groups, cur_im2col_step, ) return output @staticmethod @once_differentiable def backward(ctx, grad_output): input, offset, weight = ctx.saved_tensors grad_input = grad_offset = grad_weight = None if not grad_output.is_cuda: raise NotImplementedError("Deformable Conv is not supported on CPUs!") else: cur_im2col_step = _DeformConv._cal_im2col_step(input.shape[0], ctx.im2col_step) assert (input.shape[0] % cur_im2col_step) == 0, "im2col step must divide batchsize" if ctx.needs_input_grad[0] or ctx.needs_input_grad[1]: grad_input = torch.zeros_like(input) grad_offset = torch.zeros_like(offset) _C.deform_conv_backward_input( input, offset, grad_output, grad_input, grad_offset, weight, ctx.bufs_[0], weight.size(3), weight.size(2), ctx.stride[1], ctx.stride[0], ctx.padding[1], ctx.padding[0], ctx.dilation[1], ctx.dilation[0], ctx.groups, ctx.deformable_groups, cur_im2col_step, ) if ctx.needs_input_grad[2]: grad_weight = torch.zeros_like(weight) _C.deform_conv_backward_filter( input, offset, grad_output, grad_weight, ctx.bufs_[0], ctx.bufs_[1], weight.size(3), weight.size(2), ctx.stride[1], ctx.stride[0], ctx.padding[1], ctx.padding[0], ctx.dilation[1], ctx.dilation[0], ctx.groups, ctx.deformable_groups, 1, cur_im2col_step, ) return grad_input, grad_offset, grad_weight, None, None, None, None, None, None @staticmethod def _output_size(input, weight, padding, dilation, stride): channels = weight.size(0) output_size = (input.size(0), channels) for d in range(input.dim() - 2): in_size = input.size(d + 2) pad = padding[d] kernel = dilation[d] * (weight.size(d + 2) - 1) + 1 stride_ = stride[d] output_size += ((in_size + (2 * pad) - kernel) // stride_ + 1,) if not all(map(lambda s: s > 0, output_size)): raise ValueError( "convolution input is too small (output would be {})".format( "x".join(map(str, output_size)) ) ) return output_size @staticmethod @lru_cache(maxsize=128) def _cal_im2col_step(input_size, default_size): """ Calculate proper im2col step size, which should be divisible by input_size and not larger than prefer_size. Meanwhile the step size should be as large as possible to be more efficient. So we choose the largest one among all divisors of input_size which are smaller than prefer_size. :param input_size: input batch size . :param default_size: default preferred im2col step size. :return: the largest proper step size. """ if input_size <= default_size: return input_size best_step = 1 for step in range(2, min(int(math.sqrt(input_size)) + 1, default_size)): if input_size % step == 0: if input_size // step <= default_size: return input_size // step best_step = step return best_step class _ModulatedDeformConv(Function): @staticmethod def forward( ctx, input, offset, mask, weight, bias=None, stride=1, padding=0, dilation=1, groups=1, deformable_groups=1, ): ctx.stride = stride ctx.padding = padding ctx.dilation = dilation ctx.groups = groups ctx.deformable_groups = deformable_groups ctx.with_bias = bias is not None if not ctx.with_bias: bias = input.new_empty(1) # fake tensor if not input.is_cuda: raise NotImplementedError("Deformable Conv is not supported on CPUs!") if ( weight.requires_grad or mask.requires_grad or offset.requires_grad or input.requires_grad ): ctx.save_for_backward(input, offset, mask, weight, bias) output = input.new_empty(_ModulatedDeformConv._infer_shape(ctx, input, weight)) ctx._bufs = [input.new_empty(0), input.new_empty(0)] _C.modulated_deform_conv_forward( input, weight, bias, ctx._bufs[0], offset, mask, output, ctx._bufs[1], weight.shape[2], weight.shape[3], ctx.stride, ctx.stride, ctx.padding, ctx.padding, ctx.dilation, ctx.dilation, ctx.groups, ctx.deformable_groups, ctx.with_bias, ) return output @staticmethod @once_differentiable def backward(ctx, grad_output): if not grad_output.is_cuda: raise NotImplementedError("Deformable Conv is not supported on CPUs!") input, offset, mask, weight, bias = ctx.saved_tensors grad_input = torch.zeros_like(input) grad_offset = torch.zeros_like(offset) grad_mask = torch.zeros_like(mask) grad_weight = torch.zeros_like(weight) grad_bias = torch.zeros_like(bias) _C.modulated_deform_conv_backward( input, weight, bias, ctx._bufs[0], offset, mask, ctx._bufs[1], grad_input, grad_weight, grad_bias, grad_offset, grad_mask, grad_output, weight.shape[2], weight.shape[3], ctx.stride, ctx.stride, ctx.padding, ctx.padding, ctx.dilation, ctx.dilation, ctx.groups, ctx.deformable_groups, ctx.with_bias, ) if not ctx.with_bias: grad_bias = None return ( grad_input, grad_offset, grad_mask, grad_weight, grad_bias, None, None, None, None, None, ) @staticmethod def _infer_shape(ctx, input, weight): n = input.size(0) channels_out = weight.size(0) height, width = input.shape[2:4] kernel_h, kernel_w = weight.shape[2:4] height_out = ( height + 2 * ctx.padding - (ctx.dilation * (kernel_h - 1) + 1) ) // ctx.stride + 1 width_out = ( width + 2 * ctx.padding - (ctx.dilation * (kernel_w - 1) + 1) ) // ctx.stride + 1 return n, channels_out, height_out, width_out deform_conv = _DeformConv.apply modulated_deform_conv = _ModulatedDeformConv.apply class DeformConv(nn.Module): def __init__( self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, deformable_groups=1, bias=False, norm=None, activation=None, ): """ Deformable convolution from :paper:`deformconv`. Arguments are similar to :class:`Conv2D`. Extra arguments: Args: deformable_groups (int): number of groups used in deformable convolution. norm (nn.Module, optional): a normalization layer activation (callable(Tensor) -> Tensor): a callable activation function """ super(DeformConv, self).__init__() assert not bias assert in_channels % groups == 0, "in_channels {} cannot be divisible by groups {}".format( in_channels, groups ) assert ( out_channels % groups == 0 ), "out_channels {} cannot be divisible by groups {}".format(out_channels, groups) self.in_channels = in_channels self.out_channels = out_channels self.kernel_size = _pair(kernel_size) self.stride = _pair(stride) self.padding = _pair(padding) self.dilation = _pair(dilation) self.groups = groups self.deformable_groups = deformable_groups self.norm = norm self.activation = activation self.weight = nn.Parameter( torch.Tensor(out_channels, in_channels // self.groups, *self.kernel_size) ) self.bias = None nn.init.kaiming_uniform_(self.weight, nonlinearity="relu") def forward(self, x, offset): if x.numel() == 0: # When input is empty, we want to return a empty tensor with "correct" shape, # So that the following operations will not panic # if they check for the shape of the tensor. # This computes the height and width of the output tensor output_shape = [ (i + 2 * p - (di * (k - 1) + 1)) // s + 1 for i, p, di, k, s in zip( x.shape[-2:], self.padding, self.dilation, self.kernel_size, self.stride ) ] output_shape = [x.shape[0], self.weight.shape[0]] + output_shape return _NewEmptyTensorOp.apply(x, output_shape) x = deform_conv( x, offset, self.weight, self.stride, self.padding, self.dilation, self.groups, self.deformable_groups, ) if self.norm is not None: x = self.norm(x) if self.activation is not None: x = self.activation(x) return x def extra_repr(self): tmpstr = "in_channels=" + str(self.in_channels) tmpstr += ", out_channels=" + str(self.out_channels) tmpstr += ", kernel_size=" + str(self.kernel_size) tmpstr += ", stride=" + str(self.stride) tmpstr += ", padding=" + str(self.padding) tmpstr += ", dilation=" + str(self.dilation) tmpstr += ", groups=" + str(self.groups) tmpstr += ", deformable_groups=" + str(self.deformable_groups) tmpstr += ", bias=False" return tmpstr class ModulatedDeformConv(nn.Module): def __init__( self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, deformable_groups=1, bias=True, norm=None, activation=None, ): """ Modulated deformable convolution from :paper:`deformconv2`. Arguments are similar to :class:`Conv2D`. Extra arguments: Args: deformable_groups (int): number of groups used in deformable convolution. norm (nn.Module, optional): a normalization layer activation (callable(Tensor) -> Tensor): a callable activation function """ super(ModulatedDeformConv, self).__init__() self.in_channels = in_channels self.out_channels = out_channels self.kernel_size = _pair(kernel_size) self.stride = stride self.padding = padding self.dilation = dilation self.groups = groups self.deformable_groups = deformable_groups self.with_bias = bias self.norm = norm self.activation = activation self.weight = nn.Parameter( torch.Tensor(out_channels, in_channels // groups, *self.kernel_size) ) if bias: self.bias = nn.Parameter(torch.Tensor(out_channels)) else: self.bias = None nn.init.kaiming_uniform_(self.weight, nonlinearity="relu") if self.bias is not None: nn.init.constant_(self.bias, 0) def forward(self, x, offset, mask): if x.numel() == 0: output_shape = [ (i + 2 * p - (di * (k - 1) + 1)) // s + 1 for i, p, di, k, s in zip( x.shape[-2:], self.padding, self.dilation, self.kernel_size, self.stride ) ] output_shape = [x.shape[0], self.weight.shape[0]] + output_shape return _NewEmptyTensorOp.apply(x, output_shape) x = modulated_deform_conv( x, offset, mask, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups, self.deformable_groups, ) if self.norm is not None: x = self.norm(x) if self.activation is not None: x = self.activation(x) return x def extra_repr(self): tmpstr = "in_channels=" + str(self.in_channels) tmpstr += ", out_channels=" + str(self.out_channels) tmpstr += ", kernel_size=" + str(self.kernel_size) tmpstr += ", stride=" + str(self.stride) tmpstr += ", padding=" + str(self.padding) tmpstr += ", dilation=" + str(self.dilation) tmpstr += ", groups=" + str(self.groups) tmpstr += ", deformable_groups=" + str(self.deformable_groups) tmpstr += ", bias=" + str(self.with_bias) return tmpstr try: from detectron2 import _C except ImportError: # TODO: register ops natively so there is no need to import _C. _msg = "detectron2 is not compiled successfully, please build following the instructions!" _args = ("detectron2._C", _msg) DeformConv = create_dummy_class("DeformConv", *_args) ModulatedDeformConv = create_dummy_class("ModulatedDeformConv", *_args) deform_conv = create_dummy_func("deform_conv", *_args) modulated_deform_conv = create_dummy_func("modulated_deform_conv", *_args) ================================================ FILE: detectron2/detectron2/layers/losses.py ================================================ import math import torch def diou_loss( boxes1: torch.Tensor, boxes2: torch.Tensor, reduction: str = "none", eps: float = 1e-7, ) -> torch.Tensor: """ Distance Intersection over Union Loss (Zhaohui Zheng et. al) https://arxiv.org/abs/1911.08287 Args: boxes1, boxes2 (Tensor): box locations in XYXY format, shape (N, 4) or (4,). reduction: 'none' | 'mean' | 'sum' 'none': No reduction will be applied to the output. 'mean': The output will be averaged. 'sum': The output will be summed. eps (float): small number to prevent division by zero """ x1, y1, x2, y2 = boxes1.unbind(dim=-1) x1g, y1g, x2g, y2g = boxes2.unbind(dim=-1) # TODO: use torch._assert_async() when pytorch 1.8 support is dropped assert (x2 >= x1).all(), "bad box: x1 larger than x2" assert (y2 >= y1).all(), "bad box: y1 larger than y2" # Intersection keypoints xkis1 = torch.max(x1, x1g) ykis1 = torch.max(y1, y1g) xkis2 = torch.min(x2, x2g) ykis2 = torch.min(y2, y2g) intsct = torch.zeros_like(x1) mask = (ykis2 > ykis1) & (xkis2 > xkis1) intsct[mask] = (xkis2[mask] - xkis1[mask]) * (ykis2[mask] - ykis1[mask]) union = (x2 - x1) * (y2 - y1) + (x2g - x1g) * (y2g - y1g) - intsct + eps iou = intsct / union # smallest enclosing box xc1 = torch.min(x1, x1g) yc1 = torch.min(y1, y1g) xc2 = torch.max(x2, x2g) yc2 = torch.max(y2, y2g) diag_len = ((xc2 - xc1) ** 2) + ((yc2 - yc1) ** 2) + eps # centers of boxes x_p = (x2 + x1) / 2 y_p = (y2 + y1) / 2 x_g = (x1g + x2g) / 2 y_g = (y1g + y2g) / 2 distance = ((x_p - x_g) ** 2) + ((y_p - y_g) ** 2) # Eqn. (7) loss = 1 - iou + (distance / diag_len) if reduction == "mean": loss = loss.mean() if loss.numel() > 0 else 0.0 * loss.sum() elif reduction == "sum": loss = loss.sum() return loss def ciou_loss( boxes1: torch.Tensor, boxes2: torch.Tensor, reduction: str = "none", eps: float = 1e-7, ) -> torch.Tensor: """ Complete Intersection over Union Loss (Zhaohui Zheng et. al) https://arxiv.org/abs/1911.08287 Args: boxes1, boxes2 (Tensor): box locations in XYXY format, shape (N, 4) or (4,). reduction: 'none' | 'mean' | 'sum' 'none': No reduction will be applied to the output. 'mean': The output will be averaged. 'sum': The output will be summed. eps (float): small number to prevent division by zero """ x1, y1, x2, y2 = boxes1.unbind(dim=-1) x1g, y1g, x2g, y2g = boxes2.unbind(dim=-1) # TODO: use torch._assert_async() when pytorch 1.8 support is dropped assert (x2 >= x1).all(), "bad box: x1 larger than x2" assert (y2 >= y1).all(), "bad box: y1 larger than y2" # Intersection keypoints xkis1 = torch.max(x1, x1g) ykis1 = torch.max(y1, y1g) xkis2 = torch.min(x2, x2g) ykis2 = torch.min(y2, y2g) intsct = torch.zeros_like(x1) mask = (ykis2 > ykis1) & (xkis2 > xkis1) intsct[mask] = (xkis2[mask] - xkis1[mask]) * (ykis2[mask] - ykis1[mask]) union = (x2 - x1) * (y2 - y1) + (x2g - x1g) * (y2g - y1g) - intsct + eps iou = intsct / union # smallest enclosing box xc1 = torch.min(x1, x1g) yc1 = torch.min(y1, y1g) xc2 = torch.max(x2, x2g) yc2 = torch.max(y2, y2g) diag_len = ((xc2 - xc1) ** 2) + ((yc2 - yc1) ** 2) + eps # centers of boxes x_p = (x2 + x1) / 2 y_p = (y2 + y1) / 2 x_g = (x1g + x2g) / 2 y_g = (y1g + y2g) / 2 distance = ((x_p - x_g) ** 2) + ((y_p - y_g) ** 2) # width and height of boxes w_pred = x2 - x1 h_pred = y2 - y1 w_gt = x2g - x1g h_gt = y2g - y1g v = (4 / (math.pi**2)) * torch.pow((torch.atan(w_gt / h_gt) - torch.atan(w_pred / h_pred)), 2) with torch.no_grad(): alpha = v / (1 - iou + v + eps) # Eqn. (10) loss = 1 - iou + (distance / diag_len) + alpha * v if reduction == "mean": loss = loss.mean() if loss.numel() > 0 else 0.0 * loss.sum() elif reduction == "sum": loss = loss.sum() return loss ================================================ FILE: detectron2/detectron2/layers/mask_ops.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from typing import Tuple import torch from PIL import Image from torch.nn import functional as F __all__ = ["paste_masks_in_image"] BYTES_PER_FLOAT = 4 # TODO: This memory limit may be too much or too little. It would be better to # determine it based on available resources. GPU_MEM_LIMIT = 1024**3 # 1 GB memory limit def _do_paste_mask(masks, boxes, img_h: int, img_w: int, skip_empty: bool = True): """ Args: masks: N, 1, H, W boxes: N, 4 img_h, img_w (int): skip_empty (bool): only paste masks within the region that tightly bound all boxes, and returns the results this region only. An important optimization for CPU. Returns: if skip_empty == False, a mask of shape (N, img_h, img_w) if skip_empty == True, a mask of shape (N, h', w'), and the slice object for the corresponding region. """ # On GPU, paste all masks together (up to chunk size) # by using the entire image to sample the masks # Compared to pasting them one by one, # this has more operations but is faster on COCO-scale dataset. device = masks.device if skip_empty and not torch.jit.is_scripting(): x0_int, y0_int = torch.clamp(boxes.min(dim=0).values.floor()[:2] - 1, min=0).to( dtype=torch.int32 ) x1_int = torch.clamp(boxes[:, 2].max().ceil() + 1, max=img_w).to(dtype=torch.int32) y1_int = torch.clamp(boxes[:, 3].max().ceil() + 1, max=img_h).to(dtype=torch.int32) else: x0_int, y0_int = 0, 0 x1_int, y1_int = img_w, img_h x0, y0, x1, y1 = torch.split(boxes, 1, dim=1) # each is Nx1 N = masks.shape[0] img_y = torch.arange(y0_int, y1_int, device=device, dtype=torch.float32) + 0.5 img_x = torch.arange(x0_int, x1_int, device=device, dtype=torch.float32) + 0.5 img_y = (img_y - y0) / (y1 - y0) * 2 - 1 img_x = (img_x - x0) / (x1 - x0) * 2 - 1 # img_x, img_y have shapes (N, w), (N, h) gx = img_x[:, None, :].expand(N, img_y.size(1), img_x.size(1)) gy = img_y[:, :, None].expand(N, img_y.size(1), img_x.size(1)) grid = torch.stack([gx, gy], dim=3) if not torch.jit.is_scripting(): if not masks.dtype.is_floating_point: masks = masks.float() img_masks = F.grid_sample(masks, grid.to(masks.dtype), align_corners=False) if skip_empty and not torch.jit.is_scripting(): return img_masks[:, 0], (slice(y0_int, y1_int), slice(x0_int, x1_int)) else: return img_masks[:, 0], () # Annotate boxes as Tensor (but not Boxes) in order to use scripting @torch.jit.script_if_tracing def paste_masks_in_image( masks: torch.Tensor, boxes: torch.Tensor, image_shape: Tuple[int, int], threshold: float = 0.5 ): """ Paste a set of masks that are of a fixed resolution (e.g., 28 x 28) into an image. The location, height, and width for pasting each mask is determined by their corresponding bounding boxes in boxes. Note: This is a complicated but more accurate implementation. In actual deployment, it is often enough to use a faster but less accurate implementation. See :func:`paste_mask_in_image_old` in this file for an alternative implementation. Args: masks (tensor): Tensor of shape (Bimg, Hmask, Wmask), where Bimg is the number of detected object instances in the image and Hmask, Wmask are the mask width and mask height of the predicted mask (e.g., Hmask = Wmask = 28). Values are in [0, 1]. boxes (Boxes or Tensor): A Boxes of length Bimg or Tensor of shape (Bimg, 4). boxes[i] and masks[i] correspond to the same object instance. image_shape (tuple): height, width threshold (float): A threshold in [0, 1] for converting the (soft) masks to binary masks. Returns: img_masks (Tensor): A tensor of shape (Bimg, Himage, Wimage), where Bimg is the number of detected object instances and Himage, Wimage are the image width and height. img_masks[i] is a binary mask for object instance i. """ assert masks.shape[-1] == masks.shape[-2], "Only square mask predictions are supported" N = len(masks) if N == 0: return masks.new_empty((0,) + image_shape, dtype=torch.uint8) if not isinstance(boxes, torch.Tensor): boxes = boxes.tensor device = boxes.device assert len(boxes) == N, boxes.shape img_h, img_w = image_shape # The actual implementation split the input into chunks, # and paste them chunk by chunk. if device.type == "cpu" or torch.jit.is_scripting(): # CPU is most efficient when they are pasted one by one with skip_empty=True # so that it performs minimal number of operations. num_chunks = N else: # GPU benefits from parallelism for larger chunks, but may have memory issue # int(img_h) because shape may be tensors in tracing num_chunks = int(np.ceil(N * int(img_h) * int(img_w) * BYTES_PER_FLOAT / GPU_MEM_LIMIT)) assert ( num_chunks <= N ), "Default GPU_MEM_LIMIT in mask_ops.py is too small; try increasing it" chunks = torch.chunk(torch.arange(N, device=device), num_chunks) img_masks = torch.zeros( N, img_h, img_w, device=device, dtype=torch.bool if threshold >= 0 else torch.uint8 ) for inds in chunks: masks_chunk, spatial_inds = _do_paste_mask( masks[inds, None, :, :], boxes[inds], img_h, img_w, skip_empty=device.type == "cpu" ) if threshold >= 0: masks_chunk = (masks_chunk >= threshold).to(dtype=torch.bool) else: # for visualization and debugging masks_chunk = (masks_chunk * 255).to(dtype=torch.uint8) if torch.jit.is_scripting(): # Scripting does not use the optimized codepath img_masks[inds] = masks_chunk else: img_masks[(inds,) + spatial_inds] = masks_chunk return img_masks # The below are the original paste function (from Detectron1) which has # larger quantization error. # It is faster on CPU, while the aligned one is faster on GPU thanks to grid_sample. def paste_mask_in_image_old(mask, box, img_h, img_w, threshold): """ Paste a single mask in an image. This is a per-box implementation of :func:`paste_masks_in_image`. This function has larger quantization error due to incorrect pixel modeling and is not used any more. Args: mask (Tensor): A tensor of shape (Hmask, Wmask) storing the mask of a single object instance. Values are in [0, 1]. box (Tensor): A tensor of shape (4, ) storing the x0, y0, x1, y1 box corners of the object instance. img_h, img_w (int): Image height and width. threshold (float): Mask binarization threshold in [0, 1]. Returns: im_mask (Tensor): The resized and binarized object mask pasted into the original image plane (a tensor of shape (img_h, img_w)). """ # Conversion from continuous box coordinates to discrete pixel coordinates # via truncation (cast to int32). This determines which pixels to paste the # mask onto. box = box.to(dtype=torch.int32) # Continuous to discrete coordinate conversion # An example (1D) box with continuous coordinates (x0=0.7, x1=4.3) will map to # a discrete coordinates (x0=0, x1=4). Note that box is mapped to 5 = x1 - x0 + 1 # pixels (not x1 - x0 pixels). samples_w = box[2] - box[0] + 1 # Number of pixel samples, *not* geometric width samples_h = box[3] - box[1] + 1 # Number of pixel samples, *not* geometric height # Resample the mask from it's original grid to the new samples_w x samples_h grid mask = Image.fromarray(mask.cpu().numpy()) mask = mask.resize((samples_w, samples_h), resample=Image.BILINEAR) mask = np.array(mask, copy=False) if threshold >= 0: mask = np.array(mask > threshold, dtype=np.uint8) mask = torch.from_numpy(mask) else: # for visualization and debugging, we also # allow it to return an unmodified mask mask = torch.from_numpy(mask * 255).to(torch.uint8) im_mask = torch.zeros((img_h, img_w), dtype=torch.uint8) x_0 = max(box[0], 0) x_1 = min(box[2] + 1, img_w) y_0 = max(box[1], 0) y_1 = min(box[3] + 1, img_h) im_mask[y_0:y_1, x_0:x_1] = mask[ (y_0 - box[1]) : (y_1 - box[1]), (x_0 - box[0]) : (x_1 - box[0]) ] return im_mask # Our pixel modeling requires extrapolation for any continuous # coordinate < 0.5 or > length - 0.5. When sampling pixels on the masks, # we would like this extrapolation to be an interpolation between boundary values and zero, # instead of using absolute zero or boundary values. # Therefore `paste_mask_in_image_old` is often used with zero padding around the masks like this: # masks, scale = pad_masks(masks[:, 0, :, :], 1) # boxes = scale_boxes(boxes.tensor, scale) def pad_masks(masks, padding): """ Args: masks (tensor): A tensor of shape (B, M, M) representing B masks. padding (int): Number of cells to pad on all sides. Returns: The padded masks and the scale factor of the padding size / original size. """ B = masks.shape[0] M = masks.shape[-1] pad2 = 2 * padding scale = float(M + pad2) / M padded_masks = masks.new_zeros((B, M + pad2, M + pad2)) padded_masks[:, padding:-padding, padding:-padding] = masks return padded_masks, scale def scale_boxes(boxes, scale): """ Args: boxes (tensor): A tensor of shape (B, 4) representing B boxes with 4 coords representing the corners x0, y0, x1, y1, scale (float): The box scaling factor. Returns: Scaled boxes. """ w_half = (boxes[:, 2] - boxes[:, 0]) * 0.5 h_half = (boxes[:, 3] - boxes[:, 1]) * 0.5 x_c = (boxes[:, 2] + boxes[:, 0]) * 0.5 y_c = (boxes[:, 3] + boxes[:, 1]) * 0.5 w_half *= scale h_half *= scale scaled_boxes = torch.zeros_like(boxes) scaled_boxes[:, 0] = x_c - w_half scaled_boxes[:, 2] = x_c + w_half scaled_boxes[:, 1] = y_c - h_half scaled_boxes[:, 3] = y_c + h_half return scaled_boxes @torch.jit.script_if_tracing def _paste_masks_tensor_shape( masks: torch.Tensor, boxes: torch.Tensor, image_shape: Tuple[torch.Tensor, torch.Tensor], threshold: float = 0.5, ): """ A wrapper of paste_masks_in_image where image_shape is Tensor. During tracing, shapes might be tensors instead of ints. The Tensor->int conversion should be scripted rather than traced. """ return paste_masks_in_image(masks, boxes, (int(image_shape[0]), int(image_shape[1])), threshold) ================================================ FILE: detectron2/detectron2/layers/nms.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import torch from torchvision.ops import boxes as box_ops from torchvision.ops import nms # noqa . for compatibility def batched_nms( boxes: torch.Tensor, scores: torch.Tensor, idxs: torch.Tensor, iou_threshold: float ): """ Same as torchvision.ops.boxes.batched_nms, but with float(). """ assert boxes.shape[-1] == 4 # Note: Torchvision already has a strategy (https://github.com/pytorch/vision/issues/1311) # to decide whether to use coordinate trick or for loop to implement batched_nms. So we # just call it directly. # Fp16 does not have enough range for batched NMS, so adding float(). return box_ops.batched_nms(boxes.float(), scores, idxs, iou_threshold) # Note: this function (nms_rotated) might be moved into # torchvision/ops/boxes.py in the future def nms_rotated(boxes, scores, iou_threshold): """ Performs non-maximum suppression (NMS) on the rotated boxes according to their intersection-over-union (IoU). Rotated NMS iteratively removes lower scoring rotated boxes which have an IoU greater than iou_threshold with another (higher scoring) rotated box. Note that RotatedBox (5, 3, 4, 2, -90) covers exactly the same region as RotatedBox (5, 3, 4, 2, 90) does, and their IoU will be 1. However, they can be representing completely different objects in certain tasks, e.g., OCR. As for the question of whether rotated-NMS should treat them as faraway boxes even though their IOU is 1, it depends on the application and/or ground truth annotation. As an extreme example, consider a single character v and the square box around it. If the angle is 0 degree, the object (text) would be read as 'v'; If the angle is 90 degrees, the object (text) would become '>'; If the angle is 180 degrees, the object (text) would become '^'; If the angle is 270/-90 degrees, the object (text) would become '<' All of these cases have IoU of 1 to each other, and rotated NMS that only uses IoU as criterion would only keep one of them with the highest score - which, practically, still makes sense in most cases because typically only one of theses orientations is the correct one. Also, it does not matter as much if the box is only used to classify the object (instead of transcribing them with a sequential OCR recognition model) later. On the other hand, when we use IoU to filter proposals that are close to the ground truth during training, we should definitely take the angle into account if we know the ground truth is labeled with the strictly correct orientation (as in, upside-down words are annotated with -180 degrees even though they can be covered with a 0/90/-90 degree box, etc.) The way the original dataset is annotated also matters. For example, if the dataset is a 4-point polygon dataset that does not enforce ordering of vertices/orientation, we can estimate a minimum rotated bounding box to this polygon, but there's no way we can tell the correct angle with 100% confidence (as shown above, there could be 4 different rotated boxes, with angles differed by 90 degrees to each other, covering the exactly same region). In that case we have to just use IoU to determine the box proximity (as many detection benchmarks (even for text) do) unless there're other assumptions we can make (like width is always larger than height, or the object is not rotated by more than 90 degrees CCW/CW, etc.) In summary, not considering angles in rotated NMS seems to be a good option for now, but we should be aware of its implications. Args: boxes (Tensor[N, 5]): Rotated boxes to perform NMS on. They are expected to be in (x_center, y_center, width, height, angle_degrees) format. scores (Tensor[N]): Scores for each one of the rotated boxes iou_threshold (float): Discards all overlapping rotated boxes with IoU < iou_threshold Returns: keep (Tensor): int64 tensor with the indices of the elements that have been kept by Rotated NMS, sorted in decreasing order of scores """ return torch.ops.detectron2.nms_rotated(boxes, scores, iou_threshold) # Note: this function (batched_nms_rotated) might be moved into # torchvision/ops/boxes.py in the future def batched_nms_rotated(boxes, scores, idxs, iou_threshold): """ Performs non-maximum suppression in a batched fashion. Each index value correspond to a category, and NMS will not be applied between elements of different categories. Args: boxes (Tensor[N, 5]): boxes where NMS will be performed. They are expected to be in (x_ctr, y_ctr, width, height, angle_degrees) format scores (Tensor[N]): scores for each one of the boxes idxs (Tensor[N]): indices of the categories for each one of the boxes. iou_threshold (float): discards all overlapping boxes with IoU < iou_threshold Returns: Tensor: int64 tensor with the indices of the elements that have been kept by NMS, sorted in decreasing order of scores """ assert boxes.shape[-1] == 5 if boxes.numel() == 0: return torch.empty((0,), dtype=torch.int64, device=boxes.device) boxes = boxes.float() # fp16 does not have enough range for batched NMS # Strategy: in order to perform NMS independently per class, # we add an offset to all the boxes. The offset is dependent # only on the class idx, and is large enough so that boxes # from different classes do not overlap # Note that batched_nms in torchvision/ops/boxes.py only uses max_coordinate, # which won't handle negative coordinates correctly. # Here by using min_coordinate we can make sure the negative coordinates are # correctly handled. max_coordinate = ( torch.max(boxes[:, 0], boxes[:, 1]) + torch.max(boxes[:, 2], boxes[:, 3]) / 2 ).max() min_coordinate = ( torch.min(boxes[:, 0], boxes[:, 1]) - torch.max(boxes[:, 2], boxes[:, 3]) / 2 ).min() offsets = idxs.to(boxes) * (max_coordinate - min_coordinate + 1) boxes_for_nms = boxes.clone() # avoid modifying the original values in boxes boxes_for_nms[:, :2] += offsets[:, None] keep = nms_rotated(boxes_for_nms, scores, iou_threshold) return keep ================================================ FILE: detectron2/detectron2/layers/roi_align.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from torch import nn from torchvision.ops import roi_align # NOTE: torchvision's RoIAlign has a different default aligned=False class ROIAlign(nn.Module): def __init__(self, output_size, spatial_scale, sampling_ratio, aligned=True): """ Args: output_size (tuple): h, w spatial_scale (float): scale the input boxes by this number sampling_ratio (int): number of inputs samples to take for each output sample. 0 to take samples densely. aligned (bool): if False, use the legacy implementation in Detectron. If True, align the results more perfectly. Note: The meaning of aligned=True: Given a continuous coordinate c, its two neighboring pixel indices (in our pixel model) are computed by floor(c - 0.5) and ceil(c - 0.5). For example, c=1.3 has pixel neighbors with discrete indices [0] and [1] (which are sampled from the underlying signal at continuous coordinates 0.5 and 1.5). But the original roi_align (aligned=False) does not subtract the 0.5 when computing neighboring pixel indices and therefore it uses pixels with a slightly incorrect alignment (relative to our pixel model) when performing bilinear interpolation. With `aligned=True`, we first appropriately scale the ROI and then shift it by -0.5 prior to calling roi_align. This produces the correct neighbors; see detectron2/tests/test_roi_align.py for verification. The difference does not make a difference to the model's performance if ROIAlign is used together with conv layers. """ super().__init__() self.output_size = output_size self.spatial_scale = spatial_scale self.sampling_ratio = sampling_ratio self.aligned = aligned from torchvision import __version__ version = tuple(int(x) for x in __version__.split(".")[:2]) # https://github.com/pytorch/vision/pull/2438 assert version >= (0, 7), "Require torchvision >= 0.7" def forward(self, input, rois): """ Args: input: NCHW images rois: Bx5 boxes. First column is the index into N. The other 4 columns are xyxy. """ assert rois.dim() == 2 and rois.size(1) == 5 if input.is_quantized: input = input.dequantize() return roi_align( input, rois.to(dtype=input.dtype), self.output_size, self.spatial_scale, self.sampling_ratio, self.aligned, ) def __repr__(self): tmpstr = self.__class__.__name__ + "(" tmpstr += "output_size=" + str(self.output_size) tmpstr += ", spatial_scale=" + str(self.spatial_scale) tmpstr += ", sampling_ratio=" + str(self.sampling_ratio) tmpstr += ", aligned=" + str(self.aligned) tmpstr += ")" return tmpstr ================================================ FILE: detectron2/detectron2/layers/roi_align_rotated.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch from torch import nn from torch.autograd import Function from torch.autograd.function import once_differentiable from torch.nn.modules.utils import _pair class _ROIAlignRotated(Function): @staticmethod def forward(ctx, input, roi, output_size, spatial_scale, sampling_ratio): ctx.save_for_backward(roi) ctx.output_size = _pair(output_size) ctx.spatial_scale = spatial_scale ctx.sampling_ratio = sampling_ratio ctx.input_shape = input.size() output = torch.ops.detectron2.roi_align_rotated_forward( input, roi, spatial_scale, output_size[0], output_size[1], sampling_ratio ) return output @staticmethod @once_differentiable def backward(ctx, grad_output): (rois,) = ctx.saved_tensors output_size = ctx.output_size spatial_scale = ctx.spatial_scale sampling_ratio = ctx.sampling_ratio bs, ch, h, w = ctx.input_shape grad_input = torch.ops.detectron2.roi_align_rotated_backward( grad_output, rois, spatial_scale, output_size[0], output_size[1], bs, ch, h, w, sampling_ratio, ) return grad_input, None, None, None, None, None roi_align_rotated = _ROIAlignRotated.apply class ROIAlignRotated(nn.Module): def __init__(self, output_size, spatial_scale, sampling_ratio): """ Args: output_size (tuple): h, w spatial_scale (float): scale the input boxes by this number sampling_ratio (int): number of inputs samples to take for each output sample. 0 to take samples densely. Note: ROIAlignRotated supports continuous coordinate by default: Given a continuous coordinate c, its two neighboring pixel indices (in our pixel model) are computed by floor(c - 0.5) and ceil(c - 0.5). For example, c=1.3 has pixel neighbors with discrete indices [0] and [1] (which are sampled from the underlying signal at continuous coordinates 0.5 and 1.5). """ super(ROIAlignRotated, self).__init__() self.output_size = output_size self.spatial_scale = spatial_scale self.sampling_ratio = sampling_ratio def forward(self, input, rois): """ Args: input: NCHW images rois: Bx6 boxes. First column is the index into N. The other 5 columns are (x_ctr, y_ctr, width, height, angle_degrees). """ assert rois.dim() == 2 and rois.size(1) == 6 orig_dtype = input.dtype if orig_dtype == torch.float16: input = input.float() rois = rois.float() return roi_align_rotated( input, rois, self.output_size, self.spatial_scale, self.sampling_ratio ).to(dtype=orig_dtype) def __repr__(self): tmpstr = self.__class__.__name__ + "(" tmpstr += "output_size=" + str(self.output_size) tmpstr += ", spatial_scale=" + str(self.spatial_scale) tmpstr += ", sampling_ratio=" + str(self.sampling_ratio) tmpstr += ")" return tmpstr ================================================ FILE: detectron2/detectron2/layers/rotated_boxes.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from __future__ import absolute_import, division, print_function, unicode_literals import torch def pairwise_iou_rotated(boxes1, boxes2): """ Return intersection-over-union (Jaccard index) of boxes. Both sets of boxes are expected to be in (x_center, y_center, width, height, angle) format. Arguments: boxes1 (Tensor[N, 5]) boxes2 (Tensor[M, 5]) Returns: iou (Tensor[N, M]): the NxM matrix containing the pairwise IoU values for every element in boxes1 and boxes2 """ return torch.ops.detectron2.box_iou_rotated(boxes1, boxes2) ================================================ FILE: detectron2/detectron2/layers/shape_spec.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. from dataclasses import dataclass from typing import Optional @dataclass class ShapeSpec: """ A simple structure that contains basic shape specification about a tensor. It is often used as the auxiliary inputs/outputs of models, to complement the lack of shape inference ability among pytorch modules. """ channels: Optional[int] = None height: Optional[int] = None width: Optional[int] = None stride: Optional[int] = None ================================================ FILE: detectron2/detectron2/layers/wrappers.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. """ Wrappers around on some nn functions, mainly to support empty tensors. Ideally, add support directly in PyTorch to empty tensors in those functions. These can be removed once https://github.com/pytorch/pytorch/issues/12013 is implemented """ import warnings from typing import List, Optional import torch from torch.nn import functional as F def shapes_to_tensor(x: List[int], device: Optional[torch.device] = None) -> torch.Tensor: """ Turn a list of integer scalars or integer Tensor scalars into a vector, in a way that's both traceable and scriptable. In tracing, `x` should be a list of scalar Tensor, so the output can trace to the inputs. In scripting or eager, `x` should be a list of int. """ if torch.jit.is_scripting(): return torch.as_tensor(x, device=device) if torch.jit.is_tracing(): assert all( [isinstance(t, torch.Tensor) for t in x] ), "Shape should be tensor during tracing!" # as_tensor should not be used in tracing because it records a constant ret = torch.stack(x) if ret.device != device: # avoid recording a hard-coded device if not necessary ret = ret.to(device=device) return ret return torch.as_tensor(x, device=device) def cat(tensors: List[torch.Tensor], dim: int = 0): """ Efficient version of torch.cat that avoids a copy if there is only a single element in a list """ assert isinstance(tensors, (list, tuple)) if len(tensors) == 1: return tensors[0] return torch.cat(tensors, dim) def empty_input_loss_func_wrapper(loss_func): def wrapped_loss_func(input, target, *, reduction="mean", **kwargs): """ Same as `loss_func`, but returns 0 (instead of nan) for empty inputs. """ if target.numel() == 0 and reduction == "mean": return input.sum() * 0.0 # connect the gradient return loss_func(input, target, reduction=reduction, **kwargs) return wrapped_loss_func cross_entropy = empty_input_loss_func_wrapper(F.cross_entropy) class _NewEmptyTensorOp(torch.autograd.Function): @staticmethod def forward(ctx, x, new_shape): ctx.shape = x.shape return x.new_empty(new_shape) @staticmethod def backward(ctx, grad): shape = ctx.shape return _NewEmptyTensorOp.apply(grad, shape), None class Conv2d(torch.nn.Conv2d): """ A wrapper around :class:`torch.nn.Conv2d` to support empty inputs and more features. """ def __init__(self, *args, **kwargs): """ Extra keyword arguments supported in addition to those in `torch.nn.Conv2d`: Args: norm (nn.Module, optional): a normalization layer activation (callable(Tensor) -> Tensor): a callable activation function It assumes that norm layer is used before activation. """ norm = kwargs.pop("norm", None) activation = kwargs.pop("activation", None) super().__init__(*args, **kwargs) self.norm = norm self.activation = activation def forward(self, x): # torchscript does not support SyncBatchNorm yet # https://github.com/pytorch/pytorch/issues/40507 # and we skip these codes in torchscript since: # 1. currently we only support torchscript in evaluation mode # 2. features needed by exporting module to torchscript are added in PyTorch 1.6 or # later version, `Conv2d` in these PyTorch versions has already supported empty inputs. if not torch.jit.is_scripting(): with warnings.catch_warnings(record=True): if x.numel() == 0 and self.training: # https://github.com/pytorch/pytorch/issues/12013 assert not isinstance( self.norm, torch.nn.SyncBatchNorm ), "SyncBatchNorm does not support empty inputs!" x = F.conv2d( x, self.weight, self.bias, self.stride, self.padding, self.dilation, self.groups ) if self.norm is not None: x = self.norm(x) if self.activation is not None: x = self.activation(x) return x ConvTranspose2d = torch.nn.ConvTranspose2d BatchNorm2d = torch.nn.BatchNorm2d interpolate = F.interpolate Linear = torch.nn.Linear def nonzero_tuple(x): """ A 'as_tuple=True' version of torch.nonzero to support torchscript. because of https://github.com/pytorch/pytorch/issues/38718 """ if torch.jit.is_scripting(): if x.dim() == 0: return x.unsqueeze(0).nonzero().unbind(1) return x.nonzero().unbind(1) else: return x.nonzero(as_tuple=True) @torch.jit.script_if_tracing def move_device_like(src: torch.Tensor, dst: torch.Tensor) -> torch.Tensor: """ Tracing friendly way to cast tensor to another tensor's device. Device will be treated as constant during tracing, scripting the casting process as whole can workaround this issue. """ return src.to(dst.device) ================================================ FILE: detectron2/detectron2/model_zoo/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. """ Model Zoo API for Detectron2: a collection of functions to create common model architectures listed in `MODEL_ZOO.md `_, and optionally load their pre-trained weights. """ from .model_zoo import get, get_config_file, get_checkpoint_url, get_config __all__ = ["get_checkpoint_url", "get", "get_config_file", "get_config"] ================================================ FILE: detectron2/detectron2/model_zoo/configs/Base-RCNN-C4.yaml ================================================ MODEL: META_ARCHITECTURE: "GeneralizedRCNN" RPN: PRE_NMS_TOPK_TEST: 6000 POST_NMS_TOPK_TEST: 1000 ROI_HEADS: NAME: "Res5ROIHeads" DATASETS: TRAIN: ("coco_2017_train",) TEST: ("coco_2017_val",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.02 STEPS: (60000, 80000) MAX_ITER: 90000 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) VERSION: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Base-RCNN-DilatedC5.yaml ================================================ MODEL: META_ARCHITECTURE: "GeneralizedRCNN" RESNETS: OUT_FEATURES: ["res5"] RES5_DILATION: 2 RPN: IN_FEATURES: ["res5"] PRE_NMS_TOPK_TEST: 6000 POST_NMS_TOPK_TEST: 1000 ROI_HEADS: NAME: "StandardROIHeads" IN_FEATURES: ["res5"] ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_FC: 2 POOLER_RESOLUTION: 7 ROI_MASK_HEAD: NAME: "MaskRCNNConvUpsampleHead" NUM_CONV: 4 POOLER_RESOLUTION: 14 DATASETS: TRAIN: ("coco_2017_train",) TEST: ("coco_2017_val",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.02 STEPS: (60000, 80000) MAX_ITER: 90000 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) VERSION: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Base-RCNN-FPN.yaml ================================================ MODEL: META_ARCHITECTURE: "GeneralizedRCNN" BACKBONE: NAME: "build_resnet_fpn_backbone" RESNETS: OUT_FEATURES: ["res2", "res3", "res4", "res5"] FPN: IN_FEATURES: ["res2", "res3", "res4", "res5"] ANCHOR_GENERATOR: SIZES: [[32], [64], [128], [256], [512]] # One size for each in feature map ASPECT_RATIOS: [[0.5, 1.0, 2.0]] # Three aspect ratios (same for all in feature maps) RPN: IN_FEATURES: ["p2", "p3", "p4", "p5", "p6"] PRE_NMS_TOPK_TRAIN: 2000 # Per FPN level PRE_NMS_TOPK_TEST: 1000 # Per FPN level # Detectron1 uses 2000 proposals per-batch, # (See "modeling/rpn/rpn_outputs.py" for details of this legacy issue) # which is approximately 1000 proposals per-image since the default batch size for FPN is 2. POST_NMS_TOPK_TRAIN: 1000 POST_NMS_TOPK_TEST: 1000 ROI_HEADS: NAME: "StandardROIHeads" IN_FEATURES: ["p2", "p3", "p4", "p5"] ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_FC: 2 POOLER_RESOLUTION: 7 ROI_MASK_HEAD: NAME: "MaskRCNNConvUpsampleHead" NUM_CONV: 4 POOLER_RESOLUTION: 14 DATASETS: TRAIN: ("coco_2017_train",) TEST: ("coco_2017_val",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.02 STEPS: (60000, 80000) MAX_ITER: 90000 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) VERSION: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Base-RetinaNet.yaml ================================================ MODEL: META_ARCHITECTURE: "RetinaNet" BACKBONE: NAME: "build_retinanet_resnet_fpn_backbone" RESNETS: OUT_FEATURES: ["res3", "res4", "res5"] ANCHOR_GENERATOR: SIZES: !!python/object/apply:eval ["[[x, x * 2**(1.0/3), x * 2**(2.0/3) ] for x in [32, 64, 128, 256, 512 ]]"] FPN: IN_FEATURES: ["res3", "res4", "res5"] RETINANET: IOU_THRESHOLDS: [0.4, 0.5] IOU_LABELS: [0, -1, 1] SMOOTH_L1_LOSS_BETA: 0.0 DATASETS: TRAIN: ("coco_2017_train",) TEST: ("coco_2017_val",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.01 # Note that RetinaNet uses a different default learning rate STEPS: (60000, 80000) MAX_ITER: 90000 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) VERSION: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/fast_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False LOAD_PROPOSALS: True RESNETS: DEPTH: 50 PROPOSAL_GENERATOR: NAME: "PrecomputedProposals" DATASETS: TRAIN: ("coco_2017_train",) PROPOSAL_FILES_TRAIN: ("detectron2://COCO-Detection/rpn_R_50_FPN_1x/137258492/coco_2017_train_box_proposals_21bc3a.pkl", ) TEST: ("coco_2017_val",) PROPOSAL_FILES_TEST: ("detectron2://COCO-Detection/rpn_R_50_FPN_1x/137258492/coco_2017_val_box_proposals_ee0dad.pkl", ) DATALOADER: # proposals are part of the dataset_dicts, and take a lot of RAM NUM_WORKERS: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/faster_rcnn_R_101_C4_3x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: False RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/faster_rcnn_R_101_DC5_3x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: False RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/faster_rcnn_R_101_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: False RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/faster_rcnn_R_50_C4_1x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/faster_rcnn_R_50_C4_3x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/faster_rcnn_R_50_DC5_1x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/faster_rcnn_R_50_DC5_3x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/faster_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/faster_rcnn_R_50_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/faster_rcnn_X_101_32x8d_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: MASK_ON: False WEIGHTS: "detectron2://ImageNetPretrained/FAIR/X-101-32x8d.pkl" PIXEL_STD: [57.375, 57.120, 58.395] RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/fcos_R_50_FPN_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.fcos import model from ..common.train import train dataloader.train.mapper.use_instance_mask = False optimizer.lr = 0.01 model.backbone.bottom_up.freeze_at = 2 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/retinanet_R_101_FPN_3x.yaml ================================================ _BASE_: "../Base-RetinaNet.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/retinanet_R_50_FPN_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.retinanet import model from ..common.train import train dataloader.train.mapper.use_instance_mask = False model.backbone.bottom_up.freeze_at = 2 optimizer.lr = 0.01 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/retinanet_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RetinaNet.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/retinanet_R_50_FPN_3x.yaml ================================================ _BASE_: "../Base-RetinaNet.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/rpn_R_50_C4_1x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: META_ARCHITECTURE: "ProposalNetwork" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 RPN: PRE_NMS_TOPK_TEST: 12000 POST_NMS_TOPK_TEST: 2000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Detection/rpn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "ProposalNetwork" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 RPN: POST_NMS_TOPK_TEST: 2000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_101_C4_3x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: True RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_101_DC5_3x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: True RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_101_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: True RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_C4_1x.py ================================================ from ..common.train import train from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.mask_rcnn_c4 import model model.backbone.freeze_at = 2 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_C4_1x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_C4_3x.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_1x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_3x.yaml ================================================ _BASE_: "../Base-RCNN-DilatedC5.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.mask_rcnn_fpn import model from ..common.train import train model.backbone.bottom_up.freeze_at = 2 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x_giou.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 RPN: BBOX_REG_LOSS_TYPE: "giou" BBOX_REG_LOSS_WEIGHT: 2.0 ROI_BOX_HEAD: BBOX_REG_LOSS_TYPE: "giou" BBOX_REG_LOSS_WEIGHT: 10.0 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_X_101_32x8d_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: MASK_ON: True WEIGHTS: "detectron2://ImageNetPretrained/FAIR/X-101-32x8d.pkl" PIXEL_STD: [57.375, 57.120, 58.395] RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_regnetx_4gf_dds_fpn_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.mask_rcnn_fpn import model from ..common.train import train from detectron2.config import LazyCall as L from detectron2.modeling.backbone import RegNet from detectron2.modeling.backbone.regnet import SimpleStem, ResBottleneckBlock # Replace default ResNet with RegNetX-4GF from the DDS paper. Config source: # https://github.com/facebookresearch/pycls/blob/2c152a6e5d913e898cca4f0a758f41e6b976714d/configs/dds_baselines/regnetx/RegNetX-4.0GF_dds_8gpu.yaml#L4-L9 # noqa model.backbone.bottom_up = L(RegNet)( stem_class=SimpleStem, stem_width=32, block_class=ResBottleneckBlock, depth=23, w_a=38.65, w_0=96, w_m=2.43, group_width=40, freeze_at=2, norm="FrozenBN", out_features=["s1", "s2", "s3", "s4"], ) model.pixel_std = [57.375, 57.120, 58.395] optimizer.weight_decay = 5e-5 train.init_checkpoint = ( "https://dl.fbaipublicfiles.com/pycls/dds_baselines/160906383/RegNetX-4.0GF_dds_8gpu.pyth" ) # RegNets benefit from enabling cudnn benchmark mode train.cudnn_benchmark = True ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-InstanceSegmentation/mask_rcnn_regnety_4gf_dds_fpn_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco import dataloader from ..common.models.mask_rcnn_fpn import model from ..common.train import train from detectron2.config import LazyCall as L from detectron2.modeling.backbone import RegNet from detectron2.modeling.backbone.regnet import SimpleStem, ResBottleneckBlock # Replace default ResNet with RegNetY-4GF from the DDS paper. Config source: # https://github.com/facebookresearch/pycls/blob/2c152a6e5d913e898cca4f0a758f41e6b976714d/configs/dds_baselines/regnety/RegNetY-4.0GF_dds_8gpu.yaml#L4-L10 # noqa model.backbone.bottom_up = L(RegNet)( stem_class=SimpleStem, stem_width=32, block_class=ResBottleneckBlock, depth=22, w_a=31.41, w_0=96, w_m=2.24, group_width=64, se_ratio=0.25, freeze_at=2, norm="FrozenBN", out_features=["s1", "s2", "s3", "s4"], ) model.pixel_std = [57.375, 57.120, 58.395] optimizer.weight_decay = 5e-5 train.init_checkpoint = ( "https://dl.fbaipublicfiles.com/pycls/dds_baselines/160906838/RegNetY-4.0GF_dds_8gpu.pyth" ) # RegNets benefit from enabling cudnn benchmark mode train.cudnn_benchmark = True ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Keypoints/Base-Keypoint-RCNN-FPN.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: KEYPOINT_ON: True ROI_HEADS: NUM_CLASSES: 1 ROI_BOX_HEAD: SMOOTH_L1_BETA: 0.5 # Keypoint AP degrades (though box AP improves) when using plain L1 loss RPN: # Detectron1 uses 2000 proposals per-batch, but this option is per-image in detectron2. # 1000 proposals per-image is found to hurt box AP. # Therefore we increase it to 1500 per-image. POST_NMS_TOPK_TRAIN: 1500 DATASETS: TRAIN: ("keypoints_coco_2017_train",) TEST: ("keypoints_coco_2017_val",) ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Keypoints/keypoint_rcnn_R_101_FPN_3x.yaml ================================================ _BASE_: "Base-Keypoint-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Keypoints/keypoint_rcnn_R_50_FPN_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco_keypoint import dataloader from ..common.models.keypoint_rcnn_fpn import model from ..common.train import train model.backbone.bottom_up.freeze_at = 2 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Keypoints/keypoint_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "Base-Keypoint-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml ================================================ _BASE_: "Base-Keypoint-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-Keypoints/keypoint_rcnn_X_101_32x8d_FPN_3x.yaml ================================================ _BASE_: "Base-Keypoint-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/FAIR/X-101-32x8d.pkl" PIXEL_STD: [57.375, 57.120, 58.395] RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-PanopticSegmentation/Base-Panoptic-FPN.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "PanopticFPN" MASK_ON: True SEM_SEG_HEAD: LOSS_WEIGHT: 0.5 DATASETS: TRAIN: ("coco_2017_train_panoptic_separated",) TEST: ("coco_2017_val_panoptic_separated",) DATALOADER: FILTER_EMPTY_ANNOTATIONS: False ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-PanopticSegmentation/panoptic_fpn_R_101_3x.yaml ================================================ _BASE_: "Base-Panoptic-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-PanopticSegmentation/panoptic_fpn_R_50_1x.py ================================================ from ..common.optim import SGD as optimizer from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.data.coco_panoptic_separated import dataloader from ..common.models.panoptic_fpn import model from ..common.train import train model.backbone.bottom_up.freeze_at = 2 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-PanopticSegmentation/panoptic_fpn_R_50_1x.yaml ================================================ _BASE_: "Base-Panoptic-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ================================================ FILE: detectron2/detectron2/model_zoo/configs/COCO-PanopticSegmentation/panoptic_fpn_R_50_3x.yaml ================================================ _BASE_: "Base-Panoptic-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Cityscapes/mask_rcnn_R_50_FPN.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: # WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" # For better, more stable performance initialize from COCO WEIGHTS: "detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl" MASK_ON: True ROI_HEADS: NUM_CLASSES: 8 # This is similar to the setting used in Mask R-CNN paper, Appendix A # But there are some differences, e.g., we did not initialize the output # layer using the corresponding classes from COCO INPUT: MIN_SIZE_TRAIN: (800, 832, 864, 896, 928, 960, 992, 1024) MIN_SIZE_TRAIN_SAMPLING: "choice" MIN_SIZE_TEST: 1024 MAX_SIZE_TRAIN: 2048 MAX_SIZE_TEST: 2048 DATASETS: TRAIN: ("cityscapes_fine_instance_seg_train",) TEST: ("cityscapes_fine_instance_seg_val",) SOLVER: BASE_LR: 0.01 STEPS: (18000,) MAX_ITER: 24000 IMS_PER_BATCH: 8 TEST: EVAL_PERIOD: 8000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Detectron1-Comparisons/README.md ================================================ Detectron2 model zoo's experimental settings and a few implementation details are different from Detectron. The differences in implementation details are shared in [Compatibility with Other Libraries](../../docs/notes/compatibility.md). The differences in model zoo's experimental settings include: * Use scale augmentation during training. This improves AP with lower training cost. * Use L1 loss instead of smooth L1 loss for simplicity. This sometimes improves box AP but may affect other AP. * Use `POOLER_SAMPLING_RATIO=0` instead of 2. This does not significantly affect AP. * Use `ROIAlignV2`. This does not significantly affect AP. In this directory, we provide a few configs that __do not__ have the above changes. They mimic Detectron's behavior as close as possible, and provide a fair comparison of accuracy and speed against Detectron.
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
kp.
AP
model id download
Faster R-CNN 1x 0.219 0.038 3.1 36.9 137781054 model | metrics
Keypoint R-CNN 1x 0.313 0.071 5.0 53.1 64.2 137781195 model | metrics
Mask R-CNN 1x 0.273 0.043 3.4 37.8 34.9 137781281 model | metrics
## Comparisons: * Faster R-CNN: Detectron's AP is 36.7, similar to ours. * Keypoint R-CNN: Detectron's AP is box 53.6, keypoint 64.2. Fixing a Detectron's [bug](https://github.com/facebookresearch/Detectron/issues/459) lead to a drop in box AP, and can be compensated back by some parameter tuning. * Mask R-CNN: Detectron's AP is box 37.7, mask 33.9. We're 1 AP better in mask AP, due to more correct implementation. See [this article](https://ppwwyyxx.com/blog/2021/Where-are-Pixels/) for details. For speed comparison, see [benchmarks](https://detectron2.readthedocs.io/notes/benchmarks.html). ================================================ FILE: detectron2/detectron2/model_zoo/configs/Detectron1-Comparisons/faster_rcnn_R_50_FPN_noaug_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 # Detectron1 uses smooth L1 loss with some magic beta values. # The defaults are changed to L1 loss in Detectron2. RPN: SMOOTH_L1_BETA: 0.1111 ROI_BOX_HEAD: SMOOTH_L1_BETA: 1.0 POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" INPUT: # no scale augmentation MIN_SIZE_TRAIN: (800, ) ================================================ FILE: detectron2/detectron2/model_zoo/configs/Detectron1-Comparisons/keypoint_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" KEYPOINT_ON: True RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 1 ROI_KEYPOINT_HEAD: POOLER_RESOLUTION: 14 POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" # Detectron1 uses smooth L1 loss with some magic beta values. # The defaults are changed to L1 loss in Detectron2. ROI_BOX_HEAD: SMOOTH_L1_BETA: 1.0 POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" RPN: SMOOTH_L1_BETA: 0.1111 # Detectron1 uses 2000 proposals per-batch, but this option is per-image in detectron2 # 1000 proposals per-image is found to hurt box AP. # Therefore we increase it to 1500 per-image. POST_NMS_TOPK_TRAIN: 1500 DATASETS: TRAIN: ("keypoints_coco_2017_train",) TEST: ("keypoints_coco_2017_val",) ================================================ FILE: detectron2/detectron2/model_zoo/configs/Detectron1-Comparisons/mask_rcnn_R_50_FPN_noaug_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 # Detectron1 uses smooth L1 loss with some magic beta values. # The defaults are changed to L1 loss in Detectron2. RPN: SMOOTH_L1_BETA: 0.1111 ROI_BOX_HEAD: SMOOTH_L1_BETA: 1.0 POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" ROI_MASK_HEAD: POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" INPUT: # no scale augmentation MIN_SIZE_TRAIN: (800, ) ================================================ FILE: detectron2/detectron2/model_zoo/configs/LVISv0.5-InstanceSegmentation/mask_rcnn_R_101_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: True RESNETS: DEPTH: 101 ROI_HEADS: NUM_CLASSES: 1230 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v0.5_train",) TEST: ("lvis_v0.5_val",) TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/detectron2/model_zoo/configs/LVISv0.5-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 1230 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v0.5_train",) TEST: ("lvis_v0.5_val",) TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/detectron2/model_zoo/configs/LVISv0.5-InstanceSegmentation/mask_rcnn_X_101_32x8d_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/FAIR/X-101-32x8d.pkl" PIXEL_STD: [57.375, 57.120, 58.395] MASK_ON: True RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 101 ROI_HEADS: NUM_CLASSES: 1230 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v0.5_train",) TEST: ("lvis_v0.5_val",) TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/detectron2/model_zoo/configs/LVISv1-InstanceSegmentation/mask_rcnn_R_101_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: True RESNETS: DEPTH: 101 ROI_HEADS: NUM_CLASSES: 1203 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v1_train",) TEST: ("lvis_v1_val",) TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 SOLVER: STEPS: (120000, 160000) MAX_ITER: 180000 # 180000 * 16 / 100000 ~ 28.8 epochs DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/detectron2/model_zoo/configs/LVISv1-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 1203 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v1_train",) TEST: ("lvis_v1_val",) TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 SOLVER: STEPS: (120000, 160000) MAX_ITER: 180000 # 180000 * 16 / 100000 ~ 28.8 epochs DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/detectron2/model_zoo/configs/LVISv1-InstanceSegmentation/mask_rcnn_X_101_32x8d_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/FAIR/X-101-32x8d.pkl" PIXEL_STD: [57.375, 57.120, 58.395] MASK_ON: True RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 101 ROI_HEADS: NUM_CLASSES: 1203 SCORE_THRESH_TEST: 0.0001 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DATASETS: TRAIN: ("lvis_v1_train",) TEST: ("lvis_v1_val",) SOLVER: STEPS: (120000, 160000) MAX_ITER: 180000 # 180000 * 16 / 100000 ~ 28.8 epochs TEST: DETECTIONS_PER_IMAGE: 300 # LVIS allows up to 300 DATALOADER: SAMPLER_TRAIN: "RepeatFactorTrainingSampler" REPEAT_THRESHOLD: 0.001 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/cascade_mask_rcnn_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ROI_HEADS: NAME: CascadeROIHeads ROI_BOX_HEAD: CLS_AGNOSTIC_BBOX_REG: True RPN: POST_NMS_TOPK_TRAIN: 2000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/cascade_mask_rcnn_R_50_FPN_3x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ROI_HEADS: NAME: CascadeROIHeads ROI_BOX_HEAD: CLS_AGNOSTIC_BBOX_REG: True RPN: POST_NMS_TOPK_TRAIN: 2000 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/cascade_mask_rcnn_X_152_32x8d_FPN_IN5k_gn_dconv.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: MASK_ON: True WEIGHTS: "catalog://ImageNetPretrained/FAIR/X-152-32x8d-IN5k" RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 152 DEFORM_ON_PER_STAGE: [False, True, True, True] ROI_HEADS: NAME: "CascadeROIHeads" ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_CONV: 4 NUM_FC: 1 NORM: "GN" CLS_AGNOSTIC_BBOX_REG: True ROI_MASK_HEAD: NUM_CONV: 8 NORM: "GN" RPN: POST_NMS_TOPK_TRAIN: 2000 SOLVER: IMS_PER_BATCH: 128 STEPS: (35000, 45000) MAX_ITER: 50000 BASE_LR: 0.16 INPUT: MIN_SIZE_TRAIN: (640, 864) MIN_SIZE_TRAIN_SAMPLING: "range" MAX_SIZE_TRAIN: 1440 CROP: ENABLED: True TEST: EVAL_PERIOD: 2500 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/mask_rcnn_R_50_FPN_1x_cls_agnostic.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ROI_BOX_HEAD: CLS_AGNOSTIC_BBOX_REG: True ROI_MASK_HEAD: CLS_AGNOSTIC_MASK: True ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/mask_rcnn_R_50_FPN_1x_dconv_c3-c5.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 DEFORM_ON_PER_STAGE: [False, True, True, True] # on Res3,Res4,Res5 DEFORM_MODULATED: False ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/mask_rcnn_R_50_FPN_3x_dconv_c3-c5.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 DEFORM_ON_PER_STAGE: [False, True, True, True] # on Res3,Res4,Res5 DEFORM_MODULATED: False SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/mask_rcnn_R_50_FPN_3x_gn.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "catalog://ImageNetPretrained/FAIR/R-50-GN" MASK_ON: True RESNETS: DEPTH: 50 NORM: "GN" STRIDE_IN_1X1: False FPN: NORM: "GN" ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_CONV: 4 NUM_FC: 1 NORM: "GN" ROI_MASK_HEAD: NORM: "GN" SOLVER: # 3x schedule STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/mask_rcnn_R_50_FPN_3x_syncbn.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 NORM: "SyncBN" STRIDE_IN_1X1: True FPN: NORM: "SyncBN" ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_CONV: 4 NUM_FC: 1 NORM: "SyncBN" ROI_MASK_HEAD: NORM: "SyncBN" SOLVER: # 3x schedule STEPS: (210000, 250000) MAX_ITER: 270000 TEST: PRECISE_BN: ENABLED: True ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/mmdet_mask_rcnn_R_50_FPN_1x.py ================================================ # An example config to train a mmdetection model using detectron2. from ..common.data.coco import dataloader from ..common.coco_schedule import lr_multiplier_1x as lr_multiplier from ..common.optim import SGD as optimizer from ..common.train import train from ..common.data.constants import constants from detectron2.modeling.mmdet_wrapper import MMDetDetector from detectron2.config import LazyCall as L model = L(MMDetDetector)( detector=dict( type="MaskRCNN", pretrained="torchvision://resnet50", backbone=dict( type="ResNet", depth=50, num_stages=4, out_indices=(0, 1, 2, 3), frozen_stages=1, norm_cfg=dict(type="BN", requires_grad=True), norm_eval=True, style="pytorch", ), neck=dict(type="FPN", in_channels=[256, 512, 1024, 2048], out_channels=256, num_outs=5), rpn_head=dict( type="RPNHead", in_channels=256, feat_channels=256, anchor_generator=dict( type="AnchorGenerator", scales=[8], ratios=[0.5, 1.0, 2.0], strides=[4, 8, 16, 32, 64], ), bbox_coder=dict( type="DeltaXYWHBBoxCoder", target_means=[0.0, 0.0, 0.0, 0.0], target_stds=[1.0, 1.0, 1.0, 1.0], ), loss_cls=dict(type="CrossEntropyLoss", use_sigmoid=True, loss_weight=1.0), loss_bbox=dict(type="L1Loss", loss_weight=1.0), ), roi_head=dict( type="StandardRoIHead", bbox_roi_extractor=dict( type="SingleRoIExtractor", roi_layer=dict(type="RoIAlign", output_size=7, sampling_ratio=0), out_channels=256, featmap_strides=[4, 8, 16, 32], ), bbox_head=dict( type="Shared2FCBBoxHead", in_channels=256, fc_out_channels=1024, roi_feat_size=7, num_classes=80, bbox_coder=dict( type="DeltaXYWHBBoxCoder", target_means=[0.0, 0.0, 0.0, 0.0], target_stds=[0.1, 0.1, 0.2, 0.2], ), reg_class_agnostic=False, loss_cls=dict(type="CrossEntropyLoss", use_sigmoid=False, loss_weight=1.0), loss_bbox=dict(type="L1Loss", loss_weight=1.0), ), mask_roi_extractor=dict( type="SingleRoIExtractor", roi_layer=dict(type="RoIAlign", output_size=14, sampling_ratio=0), out_channels=256, featmap_strides=[4, 8, 16, 32], ), mask_head=dict( type="FCNMaskHead", num_convs=4, in_channels=256, conv_out_channels=256, num_classes=80, loss_mask=dict(type="CrossEntropyLoss", use_mask=True, loss_weight=1.0), ), ), # model training and testing settings train_cfg=dict( rpn=dict( assigner=dict( type="MaxIoUAssigner", pos_iou_thr=0.7, neg_iou_thr=0.3, min_pos_iou=0.3, match_low_quality=True, ignore_iof_thr=-1, ), sampler=dict( type="RandomSampler", num=256, pos_fraction=0.5, neg_pos_ub=-1, add_gt_as_proposals=False, ), allowed_border=-1, pos_weight=-1, debug=False, ), rpn_proposal=dict( nms_pre=2000, max_per_img=1000, nms=dict(type="nms", iou_threshold=0.7), min_bbox_size=0, ), rcnn=dict( assigner=dict( type="MaxIoUAssigner", pos_iou_thr=0.5, neg_iou_thr=0.5, min_pos_iou=0.5, match_low_quality=True, ignore_iof_thr=-1, ), sampler=dict( type="RandomSampler", num=512, pos_fraction=0.25, neg_pos_ub=-1, add_gt_as_proposals=True, ), mask_size=28, pos_weight=-1, debug=False, ), ), test_cfg=dict( rpn=dict( nms_pre=1000, max_per_img=1000, nms=dict(type="nms", iou_threshold=0.7), min_bbox_size=0, ), rcnn=dict( score_thr=0.05, nms=dict(type="nms", iou_threshold=0.5), max_per_img=100, mask_thr_binary=0.5, ), ), ), pixel_mean=constants.imagenet_rgb256_mean, pixel_std=constants.imagenet_rgb256_std, ) dataloader.train.mapper.image_format = "RGB" # torchvision pretrained model train.init_checkpoint = None # pretrained model is loaded inside backbone ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/panoptic_fpn_R_101_dconv_cascade_gn_3x.yaml ================================================ # A large PanopticFPN for demo purposes. # Use GN on backbone to support semantic seg. # Use Cascade + Deform Conv to improve localization. _BASE_: "../COCO-PanopticSegmentation/Base-Panoptic-FPN.yaml" MODEL: WEIGHTS: "catalog://ImageNetPretrained/FAIR/R-101-GN" RESNETS: DEPTH: 101 NORM: "GN" DEFORM_ON_PER_STAGE: [False, True, True, True] STRIDE_IN_1X1: False FPN: NORM: "GN" ROI_HEADS: NAME: CascadeROIHeads ROI_BOX_HEAD: CLS_AGNOSTIC_BBOX_REG: True ROI_MASK_HEAD: NORM: "GN" RPN: POST_NMS_TOPK_TRAIN: 2000 SOLVER: STEPS: (105000, 125000) MAX_ITER: 135000 IMS_PER_BATCH: 32 BASE_LR: 0.04 ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/scratch_mask_rcnn_R_50_FPN_3x_gn.yaml ================================================ _BASE_: "mask_rcnn_R_50_FPN_3x_gn.yaml" MODEL: # Train from random initialization. WEIGHTS: "" # It makes sense to divide by STD when training from scratch # But it seems to make no difference on the results and C2's models didn't do this. # So we keep things consistent with C2. # PIXEL_STD: [57.375, 57.12, 58.395] MASK_ON: True BACKBONE: FREEZE_AT: 0 # NOTE: Please refer to Rethinking ImageNet Pre-training https://arxiv.org/abs/1811.08883 # to learn what you need for training from scratch. ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/scratch_mask_rcnn_R_50_FPN_9x_gn.yaml ================================================ _BASE_: "mask_rcnn_R_50_FPN_3x_gn.yaml" MODEL: PIXEL_STD: [57.375, 57.12, 58.395] WEIGHTS: "" MASK_ON: True RESNETS: STRIDE_IN_1X1: False BACKBONE: FREEZE_AT: 0 SOLVER: # 9x schedule IMS_PER_BATCH: 64 # 4x the standard STEPS: (187500, 197500) # last 60/4==15k and last 20/4==5k MAX_ITER: 202500 # 90k * 9 / 4 BASE_LR: 0.08 TEST: EVAL_PERIOD: 2500 # NOTE: Please refer to Rethinking ImageNet Pre-training https://arxiv.org/abs/1811.08883 # to learn what you need for training from scratch. ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/scratch_mask_rcnn_R_50_FPN_9x_syncbn.yaml ================================================ _BASE_: "mask_rcnn_R_50_FPN_3x_syncbn.yaml" MODEL: PIXEL_STD: [57.375, 57.12, 58.395] WEIGHTS: "" MASK_ON: True RESNETS: STRIDE_IN_1X1: False BACKBONE: FREEZE_AT: 0 SOLVER: # 9x schedule IMS_PER_BATCH: 64 # 4x the standard STEPS: (187500, 197500) # last 60/4==15k and last 20/4==5k MAX_ITER: 202500 # 90k * 9 / 4 BASE_LR: 0.08 TEST: EVAL_PERIOD: 2500 # NOTE: Please refer to Rethinking ImageNet Pre-training https://arxiv.org/abs/1811.08883 # to learn what you need for training from scratch. ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/semantic_R_50_FPN_1x.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "SemanticSegmentor" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 DATASETS: TRAIN: ("coco_2017_train_panoptic_stuffonly",) TEST: ("coco_2017_val_panoptic_stuffonly",) INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) ================================================ FILE: detectron2/detectron2/model_zoo/configs/Misc/torchvision_imagenet_R_50.py ================================================ """ An example config file to train a ImageNet classifier with detectron2. Model and dataloader both come from torchvision. This shows how to use detectron2 as a general engine for any new models and tasks. To run, use the following command: python tools/lazyconfig_train_net.py --config-file configs/Misc/torchvision_imagenet_R_50.py \ --num-gpus 8 dataloader.train.dataset.root=/path/to/imagenet/ """ import torch from torch import nn from torch.nn import functional as F from omegaconf import OmegaConf import torchvision from torchvision.transforms import transforms as T from torchvision.models.resnet import ResNet, Bottleneck from fvcore.common.param_scheduler import MultiStepParamScheduler from detectron2.solver import WarmupParamScheduler from detectron2.solver.build import get_default_optimizer_params from detectron2.config import LazyCall as L from detectron2.model_zoo import get_config from detectron2.data.samplers import TrainingSampler, InferenceSampler from detectron2.evaluation import DatasetEvaluator from detectron2.utils import comm """ Note: Here we put reusable code (models, evaluation, data) together with configs just as a proof-of-concept, to easily demonstrate what's needed to train a ImageNet classifier in detectron2. Writing code in configs offers extreme flexibility but is often not a good engineering practice. In practice, you might want to put code in your project and import them instead. """ def build_data_loader(dataset, batch_size, num_workers, training=True): return torch.utils.data.DataLoader( dataset, sampler=(TrainingSampler if training else InferenceSampler)(len(dataset)), batch_size=batch_size, num_workers=num_workers, pin_memory=True, ) class ClassificationNet(nn.Module): def __init__(self, model: nn.Module): super().__init__() self.model = model @property def device(self): return list(self.model.parameters())[0].device def forward(self, inputs): image, label = inputs pred = self.model(image.to(self.device)) if self.training: label = label.to(self.device) return F.cross_entropy(pred, label) else: return pred class ClassificationAcc(DatasetEvaluator): def reset(self): self.corr = self.total = 0 def process(self, inputs, outputs): image, label = inputs self.corr += (outputs.argmax(dim=1).cpu() == label.cpu()).sum().item() self.total += len(label) def evaluate(self): all_corr_total = comm.all_gather([self.corr, self.total]) corr = sum(x[0] for x in all_corr_total) total = sum(x[1] for x in all_corr_total) return {"accuracy": corr / total} # --- End of code that could be in a project and be imported dataloader = OmegaConf.create() dataloader.train = L(build_data_loader)( dataset=L(torchvision.datasets.ImageNet)( root="/path/to/imagenet", split="train", transform=L(T.Compose)( transforms=[ L(T.RandomResizedCrop)(size=224), L(T.RandomHorizontalFlip)(), T.ToTensor(), L(T.Normalize)(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)), ] ), ), batch_size=256 // 8, num_workers=4, training=True, ) dataloader.test = L(build_data_loader)( dataset=L(torchvision.datasets.ImageNet)( root="${...train.dataset.root}", split="val", transform=L(T.Compose)( transforms=[ L(T.Resize)(size=256), L(T.CenterCrop)(size=224), T.ToTensor(), L(T.Normalize)(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225)), ] ), ), batch_size=256 // 8, num_workers=4, training=False, ) dataloader.evaluator = L(ClassificationAcc)() model = L(ClassificationNet)( model=(ResNet)(block=Bottleneck, layers=[3, 4, 6, 3], zero_init_residual=True) ) optimizer = L(torch.optim.SGD)( params=L(get_default_optimizer_params)(), lr=0.1, momentum=0.9, weight_decay=1e-4, ) lr_multiplier = L(WarmupParamScheduler)( scheduler=L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01, 0.001], milestones=[30, 60, 90, 100] ), warmup_length=1 / 100, warmup_factor=0.1, ) train = get_config("common/train.py").train train.init_checkpoint = None train.max_iter = 100 * 1281167 // 256 ================================================ FILE: detectron2/detectron2/model_zoo/configs/PascalVOC-Detection/faster_rcnn_R_50_C4.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 20 INPUT: MIN_SIZE_TRAIN: (480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800) MIN_SIZE_TEST: 800 DATASETS: TRAIN: ('voc_2007_trainval', 'voc_2012_trainval') TEST: ('voc_2007_test',) SOLVER: STEPS: (12000, 16000) MAX_ITER: 18000 # 17.4 epochs WARMUP_ITERS: 100 ================================================ FILE: detectron2/detectron2/model_zoo/configs/PascalVOC-Detection/faster_rcnn_R_50_FPN.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 20 INPUT: MIN_SIZE_TRAIN: (480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800) MIN_SIZE_TEST: 800 DATASETS: TRAIN: ('voc_2007_trainval', 'voc_2012_trainval') TEST: ('voc_2007_test',) SOLVER: STEPS: (12000, 16000) MAX_ITER: 18000 # 17.4 epochs WARMUP_ITERS: 100 ================================================ FILE: detectron2/detectron2/model_zoo/configs/common/README.md ================================================ This directory provides definitions for a few common models, dataloaders, scheduler, and optimizers that are often used in training. The definition of these objects are provided in the form of lazy instantiation: their arguments can be edited by users before constructing the objects. They can be imported, or loaded by `model_zoo.get_config` API in users' own configs. ================================================ FILE: detectron2/detectron2/model_zoo/configs/common/coco_schedule.py ================================================ from fvcore.common.param_scheduler import MultiStepParamScheduler from detectron2.config import LazyCall as L from detectron2.solver import WarmupParamScheduler def default_X_scheduler(num_X): """ Returns the config for a default multi-step LR scheduler such as "1x", "3x", commonly referred to in papers, where every 1x has the total length of 1440k training images (~12 COCO epochs). LR is decayed twice at the end of training following the strategy defined in "Rethinking ImageNet Pretraining", Sec 4. Args: num_X: a positive real number Returns: DictConfig: configs that define the multiplier for LR during training """ # total number of iterations assuming 16 batch size, using 1440000/16=90000 total_steps_16bs = num_X * 90000 if num_X <= 2: scheduler = L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], # note that scheduler is scale-invariant. This is equivalent to # milestones=[6, 8, 9] milestones=[60000, 80000, 90000], ) else: scheduler = L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], milestones=[total_steps_16bs - 60000, total_steps_16bs - 20000, total_steps_16bs], ) return L(WarmupParamScheduler)( scheduler=scheduler, warmup_length=1000 / total_steps_16bs, warmup_method="linear", warmup_factor=0.001, ) lr_multiplier_1x = default_X_scheduler(1) lr_multiplier_2x = default_X_scheduler(2) lr_multiplier_3x = default_X_scheduler(3) lr_multiplier_6x = default_X_scheduler(6) lr_multiplier_9x = default_X_scheduler(9) ================================================ FILE: detectron2/detectron2/model_zoo/configs/common/data/coco.py ================================================ from omegaconf import OmegaConf import detectron2.data.transforms as T from detectron2.config import LazyCall as L from detectron2.data import ( DatasetMapper, build_detection_test_loader, build_detection_train_loader, get_detection_dataset_dicts, ) from detectron2.evaluation import COCOEvaluator dataloader = OmegaConf.create() dataloader.train = L(build_detection_train_loader)( dataset=L(get_detection_dataset_dicts)(names="coco_2017_train"), mapper=L(DatasetMapper)( is_train=True, augmentations=[ L(T.ResizeShortestEdge)( short_edge_length=(640, 672, 704, 736, 768, 800), sample_style="choice", max_size=1333, ), L(T.RandomFlip)(horizontal=True), ], image_format="BGR", use_instance_mask=True, ), total_batch_size=16, num_workers=4, ) dataloader.test = L(build_detection_test_loader)( dataset=L(get_detection_dataset_dicts)(names="coco_2017_val", filter_empty=False), mapper=L(DatasetMapper)( is_train=False, augmentations=[ L(T.ResizeShortestEdge)(short_edge_length=800, max_size=1333), ], image_format="${...train.mapper.image_format}", ), num_workers=4, ) dataloader.evaluator = L(COCOEvaluator)( dataset_name="${..test.dataset.names}", ) ================================================ FILE: detectron2/detectron2/model_zoo/configs/common/data/coco_keypoint.py ================================================ from detectron2.data.detection_utils import create_keypoint_hflip_indices from .coco import dataloader dataloader.train.dataset.min_keypoints = 1 dataloader.train.dataset.names = "keypoints_coco_2017_train" dataloader.test.dataset.names = "keypoints_coco_2017_val" dataloader.train.mapper.update( use_instance_mask=False, use_keypoint=True, keypoint_hflip_indices=create_keypoint_hflip_indices(dataloader.train.dataset.names), ) ================================================ FILE: detectron2/detectron2/model_zoo/configs/common/data/coco_panoptic_separated.py ================================================ from detectron2.config import LazyCall as L from detectron2.evaluation import ( COCOEvaluator, COCOPanopticEvaluator, DatasetEvaluators, SemSegEvaluator, ) from .coco import dataloader dataloader.train.dataset.names = "coco_2017_train_panoptic_separated" dataloader.train.dataset.filter_empty = False dataloader.test.dataset.names = "coco_2017_val_panoptic_separated" dataloader.evaluator = [ L(COCOEvaluator)( dataset_name="${...test.dataset.names}", ), L(SemSegEvaluator)( dataset_name="${...test.dataset.names}", ), L(COCOPanopticEvaluator)( dataset_name="${...test.dataset.names}", ), ] ================================================ FILE: detectron2/detectron2/model_zoo/configs/common/data/constants.py ================================================ constants = dict( imagenet_rgb256_mean=[123.675, 116.28, 103.53], imagenet_rgb256_std=[58.395, 57.12, 57.375], imagenet_bgr256_mean=[103.530, 116.280, 123.675], # When using pre-trained models in Detectron1 or any MSRA models, # std has been absorbed into its conv1 weights, so the std needs to be set 1. # Otherwise, you can use [57.375, 57.120, 58.395] (ImageNet std) imagenet_bgr256_std=[1.0, 1.0, 1.0], ) ================================================ FILE: detectron2/detectron2/model_zoo/configs/common/optim.py ================================================ import torch from detectron2.config import LazyCall as L from detectron2.solver.build import get_default_optimizer_params SGD = L(torch.optim.SGD)( params=L(get_default_optimizer_params)( # params.model is meant to be set to the model object, before instantiating # the optimizer. weight_decay_norm=0.0 ), lr=0.02, momentum=0.9, weight_decay=1e-4, ) AdamW = L(torch.optim.AdamW)( params=L(get_default_optimizer_params)( # params.model is meant to be set to the model object, before instantiating # the optimizer. base_lr="${..lr}", weight_decay_norm=0.0, ), lr=1e-4, betas=(0.9, 0.999), weight_decay=0.1, ) ================================================ FILE: detectron2/detectron2/model_zoo/configs/common/train.py ================================================ # Common training-related configs that are designed for "tools/lazyconfig_train_net.py" # You can use your own instead, together with your own train_net.py train = dict( output_dir="./output", init_checkpoint="", max_iter=90000, amp=dict(enabled=False), # options for Automatic Mixed Precision ddp=dict( # options for DistributedDataParallel broadcast_buffers=False, find_unused_parameters=False, fp16_compression=False, ), checkpointer=dict(period=5000, max_to_keep=100), # options for PeriodicCheckpointer eval_period=5000, log_period=20, device="cuda" # ... ) ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_R_101_FPN_100ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) model.backbone.bottom_up.stages.depth = 101 ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_R_101_FPN_200ep_LSJ.py ================================================ from .mask_rcnn_R_101_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 2 # 100ep -> 200ep lr_multiplier.scheduler.milestones = [ milestone * 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_R_101_FPN_400ep_LSJ.py ================================================ from .mask_rcnn_R_101_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 4 # 100ep -> 400ep lr_multiplier.scheduler.milestones = [ milestone * 4 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_R_50_FPN_100ep_LSJ.py ================================================ import detectron2.data.transforms as T from detectron2.config.lazy import LazyCall as L from detectron2.layers.batch_norm import NaiveSyncBatchNorm from detectron2.solver import WarmupParamScheduler from fvcore.common.param_scheduler import MultiStepParamScheduler from ..common.data.coco import dataloader from ..common.models.mask_rcnn_fpn import model from ..common.optim import SGD as optimizer from ..common.train import train # train from scratch train.init_checkpoint = "" train.amp.enabled = True train.ddp.fp16_compression = True model.backbone.bottom_up.freeze_at = 0 # SyncBN # fmt: off model.backbone.bottom_up.stem.norm = \ model.backbone.bottom_up.stages.norm = \ model.backbone.norm = "SyncBN" # Using NaiveSyncBatchNorm becase heads may have empty input. That is not supported by # torch.nn.SyncBatchNorm. We can remove this after # https://github.com/pytorch/pytorch/issues/36530 is fixed. model.roi_heads.box_head.conv_norm = \ model.roi_heads.mask_head.conv_norm = lambda c: NaiveSyncBatchNorm(c, stats_mode="N") # fmt: on # 2conv in RPN: # https://github.com/tensorflow/tpu/blob/b24729de804fdb751b06467d3dce0637fa652060/models/official/detection/modeling/architecture/heads.py#L95-L97 # noqa: E501, B950 model.proposal_generator.head.conv_dims = [-1, -1] # 4conv1fc box head model.roi_heads.box_head.conv_dims = [256, 256, 256, 256] model.roi_heads.box_head.fc_dims = [1024] # resize_and_crop_image in: # https://github.com/tensorflow/tpu/blob/b24729de804fdb751b06467d3dce0637fa652060/models/official/detection/utils/input_utils.py#L127 # noqa: E501, B950 image_size = 1024 dataloader.train.mapper.augmentations = [ L(T.ResizeScale)( min_scale=0.1, max_scale=2.0, target_height=image_size, target_width=image_size ), L(T.FixedSizeCrop)(crop_size=(image_size, image_size)), L(T.RandomFlip)(horizontal=True), ] # recompute boxes due to cropping dataloader.train.mapper.recompute_boxes = True # larger batch-size. dataloader.train.total_batch_size = 64 # Equivalent to 100 epochs. # 100 ep = 184375 iters * 64 images/iter / 118000 images/ep train.max_iter = 184375 lr_multiplier = L(WarmupParamScheduler)( scheduler=L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], milestones=[163889, 177546], num_updates=train.max_iter, ), warmup_length=500 / train.max_iter, warmup_factor=0.067, ) optimizer.lr = 0.1 optimizer.weight_decay = 4e-5 ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_R_50_FPN_200ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 2 # 100ep -> 200ep lr_multiplier.scheduler.milestones = [ milestone * 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_R_50_FPN_400ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 4 # 100ep -> 400ep lr_multiplier.scheduler.milestones = [ milestone * 4 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_R_50_FPN_50ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter //= 2 # 100ep -> 50ep lr_multiplier.scheduler.milestones = [ milestone // 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_regnetx_4gf_dds_FPN_100ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) from detectron2.config import LazyCall as L from detectron2.modeling.backbone import RegNet from detectron2.modeling.backbone.regnet import SimpleStem, ResBottleneckBlock # Config source: # https://github.com/facebookresearch/detectron2/blob/main/configs/COCO-InstanceSegmentation/mask_rcnn_regnetx_4gf_dds_fpn_1x.py # noqa model.backbone.bottom_up = L(RegNet)( stem_class=SimpleStem, stem_width=32, block_class=ResBottleneckBlock, depth=23, w_a=38.65, w_0=96, w_m=2.43, group_width=40, norm="SyncBN", out_features=["s1", "s2", "s3", "s4"], ) model.pixel_std = [57.375, 57.120, 58.395] # RegNets benefit from enabling cudnn benchmark mode train.cudnn_benchmark = True ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_regnetx_4gf_dds_FPN_200ep_LSJ.py ================================================ from .mask_rcnn_regnetx_4gf_dds_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 2 # 100ep -> 200ep lr_multiplier.scheduler.milestones = [ milestone * 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_regnetx_4gf_dds_FPN_400ep_LSJ.py ================================================ from .mask_rcnn_regnetx_4gf_dds_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 4 # 100ep -> 400ep lr_multiplier.scheduler.milestones = [ milestone * 4 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_regnety_4gf_dds_FPN_100ep_LSJ.py ================================================ from .mask_rcnn_R_50_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) from detectron2.config import LazyCall as L from detectron2.modeling.backbone import RegNet from detectron2.modeling.backbone.regnet import SimpleStem, ResBottleneckBlock # Config source: # https://github.com/facebookresearch/detectron2/blob/main/configs/COCO-InstanceSegmentation/mask_rcnn_regnety_4gf_dds_fpn_1x.py # noqa model.backbone.bottom_up = L(RegNet)( stem_class=SimpleStem, stem_width=32, block_class=ResBottleneckBlock, depth=22, w_a=31.41, w_0=96, w_m=2.24, group_width=64, se_ratio=0.25, norm="SyncBN", out_features=["s1", "s2", "s3", "s4"], ) model.pixel_std = [57.375, 57.120, 58.395] # RegNets benefit from enabling cudnn benchmark mode train.cudnn_benchmark = True ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_regnety_4gf_dds_FPN_200ep_LSJ.py ================================================ from .mask_rcnn_regnety_4gf_dds_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 2 # 100ep -> 200ep lr_multiplier.scheduler.milestones = [ milestone * 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/detectron2/model_zoo/configs/new_baselines/mask_rcnn_regnety_4gf_dds_FPN_400ep_LSJ.py ================================================ from .mask_rcnn_regnety_4gf_dds_FPN_100ep_LSJ import ( dataloader, lr_multiplier, model, optimizer, train, ) train.max_iter *= 4 # 100ep -> 400ep lr_multiplier.scheduler.milestones = [ milestone * 4 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/README.md ================================================ These are quick configs for performance or accuracy regression tracking purposes. * `*instance_test.yaml`: can train on 2 GPUs. They are used to test whether the training can successfully finish. They are not expected to produce reasonable training results. * `*inference_acc_test.yaml`: They should be run using `--eval-only`. They run inference using pre-trained models and verify the results are as expected. * `*training_acc_test.yaml`: They should be trained on 8 GPUs. They finish in about an hour and verify the training accuracy is within the normal range. ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/cascade_mask_rcnn_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../Misc/cascade_mask_rcnn_R_50_FPN_3x.yaml" MODEL: WEIGHTS: "detectron2://Misc/cascade_mask_rcnn_R_50_FPN_3x/144998488/model_final_480dd8.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 50.18, 0.02], ["segm", "AP", 43.87, 0.02]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/cascade_mask_rcnn_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../Misc/cascade_mask_rcnn_R_50_FPN_3x.yaml" DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/fast_rcnn_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../COCO-Detection/fast_rcnn_R_50_FPN_1x.yaml" MODEL: WEIGHTS: "detectron2://COCO-Detection/fast_rcnn_R_50_FPN_1x/137635226/model_final_e5f7ce.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 45.70, 0.02]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/fast_rcnn_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../COCO-Detection/fast_rcnn_R_50_FPN_1x.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" DATASETS: TRAIN: ("coco_2017_val_100",) PROPOSAL_FILES_TRAIN: ("detectron2://COCO-Detection/rpn_R_50_FPN_1x/137258492/coco_2017_val_box_proposals_ee0dad.pkl", ) TEST: ("coco_2017_val_100",) PROPOSAL_FILES_TEST: ("detectron2://COCO-Detection/rpn_R_50_FPN_1x/137258492/coco_2017_val_box_proposals_ee0dad.pkl", ) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/keypoint_rcnn_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x/137849621/model_final_a6e10b.pkl" DATASETS: TEST: ("keypoints_coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 52.47, 0.02], ["keypoints", "AP", 67.36, 0.02]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/keypoint_rcnn_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" KEYPOINT_ON: True ROI_HEADS: NUM_CLASSES: 1 DATASETS: TRAIN: ("keypoints_coco_2017_val_100",) TEST: ("keypoints_coco_2017_val_100",) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/keypoint_rcnn_R_50_FPN_normalized_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" KEYPOINT_ON: True RESNETS: DEPTH: 50 ROI_HEADS: BATCH_SIZE_PER_IMAGE: 256 NUM_CLASSES: 1 ROI_KEYPOINT_HEAD: POOLER_RESOLUTION: 14 POOLER_SAMPLING_RATIO: 2 NORMALIZE_LOSS_BY_VISIBLE_KEYPOINTS: False LOSS_WEIGHT: 4.0 ROI_BOX_HEAD: SMOOTH_L1_BETA: 1.0 # Keypoint AP degrades when using plain L1 loss RPN: SMOOTH_L1_BETA: 0.2 # Keypoint AP degrades when using plain L1 loss DATASETS: TRAIN: ("keypoints_coco_2017_val",) TEST: ("keypoints_coco_2017_val",) INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) SOLVER: WARMUP_FACTOR: 0.33333333 WARMUP_ITERS: 100 STEPS: (5500, 5800) MAX_ITER: 6000 TEST: EXPECTED_RESULTS: [["bbox", "AP", 55.35, 1.0], ["keypoints", "AP", 76.91, 1.0]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/keypoint_rcnn_R_50_FPN_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" KEYPOINT_ON: True RESNETS: DEPTH: 50 ROI_HEADS: BATCH_SIZE_PER_IMAGE: 256 NUM_CLASSES: 1 ROI_KEYPOINT_HEAD: POOLER_RESOLUTION: 14 POOLER_SAMPLING_RATIO: 2 ROI_BOX_HEAD: SMOOTH_L1_BETA: 1.0 # Keypoint AP degrades when using plain L1 loss RPN: SMOOTH_L1_BETA: 0.2 # Keypoint AP degrades when using plain L1 loss DATASETS: TRAIN: ("keypoints_coco_2017_val",) TEST: ("keypoints_coco_2017_val",) INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) SOLVER: WARMUP_FACTOR: 0.33333333 WARMUP_ITERS: 100 STEPS: (5500, 5800) MAX_ITER: 6000 TEST: EXPECTED_RESULTS: [["bbox", "AP", 53.5, 1.0], ["keypoints", "AP", 72.4, 1.0]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/mask_rcnn_R_50_C4_GCV_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: BASE_LR: 0.001 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 CLIP_GRADIENTS: ENABLED: True CLIP_TYPE: "value" CLIP_VALUE: 1.0 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/mask_rcnn_R_50_C4_inference_acc_test.yaml ================================================ _BASE_: "../COCO-InstanceSegmentation/mask_rcnn_R_50_C4_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_C4_3x/137849525/model_final_4ce675.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 47.37, 0.02], ["segm", "AP", 40.99, 0.02]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/mask_rcnn_R_50_C4_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: BASE_LR: 0.001 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/mask_rcnn_R_50_C4_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ROI_HEADS: BATCH_SIZE_PER_IMAGE: 256 MASK_ON: True DATASETS: TRAIN: ("coco_2017_val",) TEST: ("coco_2017_val",) INPUT: MIN_SIZE_TRAIN: (600,) MAX_SIZE_TRAIN: 1000 MIN_SIZE_TEST: 800 MAX_SIZE_TEST: 1000 SOLVER: IMS_PER_BATCH: 8 # base uses 16 WARMUP_FACTOR: 0.33333 WARMUP_ITERS: 100 STEPS: (11000, 11600) MAX_ITER: 12000 TEST: EXPECTED_RESULTS: [["bbox", "AP", 41.88, 0.7], ["segm", "AP", 33.79, 0.5]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/mask_rcnn_R_50_DC5_inference_acc_test.yaml ================================================ _BASE_: "../COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_3x/137849551/model_final_84107b.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 47.44, 0.02], ["segm", "AP", 42.94, 0.02]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/mask_rcnn_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 47.34, 0.02], ["segm", "AP", 42.67, 0.02], ["bbox_TTA", "AP", 49.11, 0.02], ["segm_TTA", "AP", 45.04, 0.02]] AUG: ENABLED: True MIN_SIZES: (700, 800) # to save some time ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/mask_rcnn_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/mask_rcnn_R_50_FPN_pred_boxes_training_acc_test.yaml ================================================ _BASE_: "./mask_rcnn_R_50_FPN_training_acc_test.yaml" MODEL: ROI_BOX_HEAD: TRAIN_ON_PRED_BOXES: True TEST: EXPECTED_RESULTS: [["bbox", "AP", 42.6, 1.0], ["segm", "AP", 35.8, 0.8]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/mask_rcnn_R_50_FPN_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ROI_HEADS: BATCH_SIZE_PER_IMAGE: 256 MASK_ON: True DATASETS: TRAIN: ("coco_2017_val",) TEST: ("coco_2017_val",) INPUT: MIN_SIZE_TRAIN: (600,) MAX_SIZE_TRAIN: 1000 MIN_SIZE_TEST: 800 MAX_SIZE_TEST: 1000 SOLVER: WARMUP_FACTOR: 0.3333333 WARMUP_ITERS: 100 STEPS: (5500, 5800) MAX_ITER: 6000 TEST: EXPECTED_RESULTS: [["bbox", "AP", 42.5, 1.0], ["segm", "AP", 35.8, 0.8]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/panoptic_fpn_R_50_inference_acc_test.yaml ================================================ _BASE_: "../COCO-PanopticSegmentation/panoptic_fpn_R_50_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-PanopticSegmentation/panoptic_fpn_R_50_3x/139514569/model_final_c10459.pkl" DATASETS: TEST: ("coco_2017_val_100_panoptic_separated",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 46.47, 0.02], ["segm", "AP", 43.39, 0.02], ["sem_seg", "mIoU", 42.55, 0.02], ["panoptic_seg", "PQ", 38.99, 0.02]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/panoptic_fpn_R_50_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "PanopticFPN" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 SEM_SEG_HEAD: LOSS_WEIGHT: 0.5 DATASETS: TRAIN: ("coco_2017_val_100_panoptic_separated",) TEST: ("coco_2017_val_100_panoptic_separated",) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 1 ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/panoptic_fpn_R_50_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "PanopticFPN" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 SEM_SEG_HEAD: LOSS_WEIGHT: 0.5 DATASETS: TRAIN: ("coco_2017_val_panoptic_separated",) TEST: ("coco_2017_val_panoptic_separated",) SOLVER: BASE_LR: 0.01 WARMUP_FACTOR: 0.001 WARMUP_ITERS: 500 STEPS: (5500,) MAX_ITER: 7000 TEST: EXPECTED_RESULTS: [["bbox", "AP", 46.70, 1.1], ["segm", "AP", 39.0, 0.7], ["sem_seg", "mIoU", 64.73, 1.3], ["panoptic_seg", "PQ", 48.13, 0.8]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/retinanet_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../COCO-Detection/retinanet_R_50_FPN_3x.yaml" MODEL: WEIGHTS: "detectron2://COCO-Detection/retinanet_R_50_FPN_3x/190397829/model_final_5bd44e.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 44.45, 0.02]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/retinanet_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../COCO-Detection/retinanet_R_50_FPN_1x.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/rpn_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../COCO-Detection/rpn_R_50_FPN_1x.yaml" MODEL: WEIGHTS: "detectron2://COCO-Detection/rpn_R_50_FPN_1x/137258492/model_final_02ce48.pkl" DATASETS: TEST: ("coco_2017_val_100",) TEST: EXPECTED_RESULTS: [["box_proposals", "AR@1000", 58.16, 0.02]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/rpn_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../COCO-Detection/rpn_R_50_FPN_1x.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" DATASETS: TRAIN: ("coco_2017_val_100",) TEST: ("coco_2017_val_100",) SOLVER: STEPS: (30,) MAX_ITER: 40 BASE_LR: 0.005 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/semantic_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "SemanticSegmentor" WEIGHTS: "detectron2://semantic_R_50_FPN_1x/111802073/model_final_c18079783c55a94968edc28b7101c5f0.pkl" RESNETS: DEPTH: 50 DATASETS: TEST: ("coco_2017_val_100_panoptic_stuffonly",) TEST: EXPECTED_RESULTS: [["sem_seg", "mIoU", 39.53, 0.02], ["sem_seg", "mACC", 51.50, 0.02]] ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/semantic_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "SemanticSegmentor" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 DATASETS: TRAIN: ("coco_2017_val_100_panoptic_stuffonly",) TEST: ("coco_2017_val_100_panoptic_stuffonly",) INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) SOLVER: BASE_LR: 0.005 STEPS: (30,) MAX_ITER: 40 IMS_PER_BATCH: 4 DATALOADER: NUM_WORKERS: 2 ================================================ FILE: detectron2/detectron2/model_zoo/configs/quick_schedules/semantic_R_50_FPN_training_acc_test.yaml ================================================ _BASE_: "../Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "SemanticSegmentor" WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 DATASETS: TRAIN: ("coco_2017_val_panoptic_stuffonly",) TEST: ("coco_2017_val_panoptic_stuffonly",) SOLVER: BASE_LR: 0.01 WARMUP_FACTOR: 0.001 WARMUP_ITERS: 300 STEPS: (5500,) MAX_ITER: 7000 TEST: EXPECTED_RESULTS: [["sem_seg", "mIoU", 76.51, 1.0], ["sem_seg", "mACC", 83.25, 1.0]] INPUT: # no scale augmentation MIN_SIZE_TRAIN: (800, ) ================================================ FILE: detectron2/detectron2/model_zoo/model_zoo.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import os from typing import Optional import pkg_resources import torch from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import CfgNode, LazyConfig, get_cfg, instantiate from detectron2.modeling import build_model class _ModelZooUrls(object): """ Mapping from names to officially released Detectron2 pre-trained models. """ S3_PREFIX = "https://dl.fbaipublicfiles.com/detectron2/" # format: {config_path.yaml} -> model_id/model_final_{commit}.pkl CONFIG_PATH_TO_URL_SUFFIX = { # COCO Detection with Faster R-CNN "COCO-Detection/faster_rcnn_R_50_C4_1x": "137257644/model_final_721ade.pkl", "COCO-Detection/faster_rcnn_R_50_DC5_1x": "137847829/model_final_51d356.pkl", "COCO-Detection/faster_rcnn_R_50_FPN_1x": "137257794/model_final_b275ba.pkl", "COCO-Detection/faster_rcnn_R_50_C4_3x": "137849393/model_final_f97cb7.pkl", "COCO-Detection/faster_rcnn_R_50_DC5_3x": "137849425/model_final_68d202.pkl", "COCO-Detection/faster_rcnn_R_50_FPN_3x": "137849458/model_final_280758.pkl", "COCO-Detection/faster_rcnn_R_101_C4_3x": "138204752/model_final_298dad.pkl", "COCO-Detection/faster_rcnn_R_101_DC5_3x": "138204841/model_final_3e0943.pkl", "COCO-Detection/faster_rcnn_R_101_FPN_3x": "137851257/model_final_f6e8b1.pkl", "COCO-Detection/faster_rcnn_X_101_32x8d_FPN_3x": "139173657/model_final_68b088.pkl", # COCO Detection with RetinaNet "COCO-Detection/retinanet_R_50_FPN_1x": "190397773/model_final_bfca0b.pkl", "COCO-Detection/retinanet_R_50_FPN_3x": "190397829/model_final_5bd44e.pkl", "COCO-Detection/retinanet_R_101_FPN_3x": "190397697/model_final_971ab9.pkl", # COCO Detection with RPN and Fast R-CNN "COCO-Detection/rpn_R_50_C4_1x": "137258005/model_final_450694.pkl", "COCO-Detection/rpn_R_50_FPN_1x": "137258492/model_final_02ce48.pkl", "COCO-Detection/fast_rcnn_R_50_FPN_1x": "137635226/model_final_e5f7ce.pkl", # COCO Instance Segmentation Baselines with Mask R-CNN "COCO-InstanceSegmentation/mask_rcnn_R_50_C4_1x": "137259246/model_final_9243eb.pkl", "COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_1x": "137260150/model_final_4f86c3.pkl", "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x": "137260431/model_final_a54504.pkl", "COCO-InstanceSegmentation/mask_rcnn_R_50_C4_3x": "137849525/model_final_4ce675.pkl", "COCO-InstanceSegmentation/mask_rcnn_R_50_DC5_3x": "137849551/model_final_84107b.pkl", "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x": "137849600/model_final_f10217.pkl", "COCO-InstanceSegmentation/mask_rcnn_R_101_C4_3x": "138363239/model_final_a2914c.pkl", "COCO-InstanceSegmentation/mask_rcnn_R_101_DC5_3x": "138363294/model_final_0464b7.pkl", "COCO-InstanceSegmentation/mask_rcnn_R_101_FPN_3x": "138205316/model_final_a3ec72.pkl", "COCO-InstanceSegmentation/mask_rcnn_X_101_32x8d_FPN_3x": "139653917/model_final_2d9806.pkl", # noqa # New baselines using Large-Scale Jitter and Longer Training Schedule "new_baselines/mask_rcnn_R_50_FPN_100ep_LSJ": "42047764/model_final_bb69de.pkl", "new_baselines/mask_rcnn_R_50_FPN_200ep_LSJ": "42047638/model_final_89a8d3.pkl", "new_baselines/mask_rcnn_R_50_FPN_400ep_LSJ": "42019571/model_final_14d201.pkl", "new_baselines/mask_rcnn_R_101_FPN_100ep_LSJ": "42025812/model_final_4f7b58.pkl", "new_baselines/mask_rcnn_R_101_FPN_200ep_LSJ": "42131867/model_final_0bb7ae.pkl", "new_baselines/mask_rcnn_R_101_FPN_400ep_LSJ": "42073830/model_final_f96b26.pkl", "new_baselines/mask_rcnn_regnetx_4gf_dds_FPN_100ep_LSJ": "42047771/model_final_b7fbab.pkl", # noqa "new_baselines/mask_rcnn_regnetx_4gf_dds_FPN_200ep_LSJ": "42132721/model_final_5d87c1.pkl", # noqa "new_baselines/mask_rcnn_regnetx_4gf_dds_FPN_400ep_LSJ": "42025447/model_final_f1362d.pkl", # noqa "new_baselines/mask_rcnn_regnety_4gf_dds_FPN_100ep_LSJ": "42047784/model_final_6ba57e.pkl", # noqa "new_baselines/mask_rcnn_regnety_4gf_dds_FPN_200ep_LSJ": "42047642/model_final_27b9c1.pkl", # noqa "new_baselines/mask_rcnn_regnety_4gf_dds_FPN_400ep_LSJ": "42045954/model_final_ef3a80.pkl", # noqa # COCO Person Keypoint Detection Baselines with Keypoint R-CNN "COCO-Keypoints/keypoint_rcnn_R_50_FPN_1x": "137261548/model_final_04e291.pkl", "COCO-Keypoints/keypoint_rcnn_R_50_FPN_3x": "137849621/model_final_a6e10b.pkl", "COCO-Keypoints/keypoint_rcnn_R_101_FPN_3x": "138363331/model_final_997cc7.pkl", "COCO-Keypoints/keypoint_rcnn_X_101_32x8d_FPN_3x": "139686956/model_final_5ad38f.pkl", # COCO Panoptic Segmentation Baselines with Panoptic FPN "COCO-PanopticSegmentation/panoptic_fpn_R_50_1x": "139514544/model_final_dbfeb4.pkl", "COCO-PanopticSegmentation/panoptic_fpn_R_50_3x": "139514569/model_final_c10459.pkl", "COCO-PanopticSegmentation/panoptic_fpn_R_101_3x": "139514519/model_final_cafdb1.pkl", # LVIS Instance Segmentation Baselines with Mask R-CNN "LVISv0.5-InstanceSegmentation/mask_rcnn_R_50_FPN_1x": "144219072/model_final_571f7c.pkl", # noqa "LVISv0.5-InstanceSegmentation/mask_rcnn_R_101_FPN_1x": "144219035/model_final_824ab5.pkl", # noqa "LVISv0.5-InstanceSegmentation/mask_rcnn_X_101_32x8d_FPN_1x": "144219108/model_final_5e3439.pkl", # noqa # Cityscapes & Pascal VOC Baselines "Cityscapes/mask_rcnn_R_50_FPN": "142423278/model_final_af9cf5.pkl", "PascalVOC-Detection/faster_rcnn_R_50_C4": "142202221/model_final_b1acc2.pkl", # Other Settings "Misc/mask_rcnn_R_50_FPN_1x_dconv_c3-c5": "138602867/model_final_65c703.pkl", "Misc/mask_rcnn_R_50_FPN_3x_dconv_c3-c5": "144998336/model_final_821d0b.pkl", "Misc/cascade_mask_rcnn_R_50_FPN_1x": "138602847/model_final_e9d89b.pkl", "Misc/cascade_mask_rcnn_R_50_FPN_3x": "144998488/model_final_480dd8.pkl", "Misc/mask_rcnn_R_50_FPN_3x_syncbn": "169527823/model_final_3b3c51.pkl", "Misc/mask_rcnn_R_50_FPN_3x_gn": "138602888/model_final_dc5d9e.pkl", "Misc/scratch_mask_rcnn_R_50_FPN_3x_gn": "138602908/model_final_01ca85.pkl", "Misc/scratch_mask_rcnn_R_50_FPN_9x_gn": "183808979/model_final_da7b4c.pkl", "Misc/scratch_mask_rcnn_R_50_FPN_9x_syncbn": "184226666/model_final_5ce33e.pkl", "Misc/panoptic_fpn_R_101_dconv_cascade_gn_3x": "139797668/model_final_be35db.pkl", "Misc/cascade_mask_rcnn_X_152_32x8d_FPN_IN5k_gn_dconv": "18131413/model_0039999_e76410.pkl", # noqa # D1 Comparisons "Detectron1-Comparisons/faster_rcnn_R_50_FPN_noaug_1x": "137781054/model_final_7ab50c.pkl", # noqa "Detectron1-Comparisons/mask_rcnn_R_50_FPN_noaug_1x": "137781281/model_final_62ca52.pkl", # noqa "Detectron1-Comparisons/keypoint_rcnn_R_50_FPN_1x": "137781195/model_final_cce136.pkl", } @staticmethod def query(config_path: str) -> Optional[str]: """ Args: config_path: relative config filename """ name = config_path.replace(".yaml", "").replace(".py", "") if name in _ModelZooUrls.CONFIG_PATH_TO_URL_SUFFIX: suffix = _ModelZooUrls.CONFIG_PATH_TO_URL_SUFFIX[name] return _ModelZooUrls.S3_PREFIX + name + "/" + suffix return None def get_checkpoint_url(config_path): """ Returns the URL to the model trained using the given config Args: config_path (str): config file name relative to detectron2's "configs/" directory, e.g., "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml" Returns: str: a URL to the model """ url = _ModelZooUrls.query(config_path) if url is None: raise RuntimeError("Pretrained model for {} is not available!".format(config_path)) return url def get_config_file(config_path): """ Returns path to a builtin config file. Args: config_path (str): config file name relative to detectron2's "configs/" directory, e.g., "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml" Returns: str: the real path to the config file. """ cfg_file = pkg_resources.resource_filename( "detectron2.model_zoo", os.path.join("configs", config_path) ) if not os.path.exists(cfg_file): raise RuntimeError("{} not available in Model Zoo!".format(config_path)) return cfg_file def get_config(config_path, trained: bool = False): """ Returns a config object for a model in model zoo. Args: config_path (str): config file name relative to detectron2's "configs/" directory, e.g., "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml" trained (bool): If True, will set ``MODEL.WEIGHTS`` to trained model zoo weights. If False, the checkpoint specified in the config file's ``MODEL.WEIGHTS`` is used instead; this will typically (though not always) initialize a subset of weights using an ImageNet pre-trained model, while randomly initializing the other weights. Returns: CfgNode or omegaconf.DictConfig: a config object """ cfg_file = get_config_file(config_path) if cfg_file.endswith(".yaml"): cfg = get_cfg() cfg.merge_from_file(cfg_file) if trained: cfg.MODEL.WEIGHTS = get_checkpoint_url(config_path) return cfg elif cfg_file.endswith(".py"): cfg = LazyConfig.load(cfg_file) if trained: url = get_checkpoint_url(config_path) if "train" in cfg and "init_checkpoint" in cfg.train: cfg.train.init_checkpoint = url else: raise NotImplementedError return cfg def get(config_path, trained: bool = False, device: Optional[str] = None): """ Get a model specified by relative path under Detectron2's official ``configs/`` directory. Args: config_path (str): config file name relative to detectron2's "configs/" directory, e.g., "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml" trained (bool): see :func:`get_config`. device (str or None): overwrite the device in config, if given. Returns: nn.Module: a detectron2 model. Will be in training mode. Example: :: from detectron2 import model_zoo model = model_zoo.get("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml", trained=True) """ cfg = get_config(config_path, trained) if device is None and not torch.cuda.is_available(): device = "cpu" if device is not None and isinstance(cfg, CfgNode): cfg.MODEL.DEVICE = device if isinstance(cfg, CfgNode): model = build_model(cfg) DetectionCheckpointer(model).load(cfg.MODEL.WEIGHTS) else: model = instantiate(cfg.model) if device is not None: model = model.to(device) if "train" in cfg and "init_checkpoint" in cfg.train: DetectionCheckpointer(model).load(cfg.train.init_checkpoint) return model ================================================ FILE: detectron2/detectron2/modeling/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.layers import ShapeSpec from .anchor_generator import build_anchor_generator, ANCHOR_GENERATOR_REGISTRY from .backbone import ( BACKBONE_REGISTRY, FPN, Backbone, ResNet, ResNetBlockBase, build_backbone, build_resnet_backbone, make_stage, ViT, SimpleFeaturePyramid, get_vit_lr_decay_rate, MViT, SwinTransformer, ) from .meta_arch import ( META_ARCH_REGISTRY, SEM_SEG_HEADS_REGISTRY, GeneralizedRCNN, PanopticFPN, ProposalNetwork, RetinaNet, SemanticSegmentor, build_model, build_sem_seg_head, FCOS, ) from .postprocessing import detector_postprocess from .proposal_generator import ( PROPOSAL_GENERATOR_REGISTRY, build_proposal_generator, RPN_HEAD_REGISTRY, build_rpn_head, ) from .roi_heads import ( ROI_BOX_HEAD_REGISTRY, ROI_HEADS_REGISTRY, ROI_KEYPOINT_HEAD_REGISTRY, ROI_MASK_HEAD_REGISTRY, ROIHeads, StandardROIHeads, BaseMaskRCNNHead, BaseKeypointRCNNHead, FastRCNNOutputLayers, build_box_head, build_keypoint_head, build_mask_head, build_roi_heads, ) from .test_time_augmentation import DatasetMapperTTA, GeneralizedRCNNWithTTA from .mmdet_wrapper import MMDetBackbone, MMDetDetector _EXCLUDE = {"ShapeSpec"} __all__ = [k for k in globals().keys() if k not in _EXCLUDE and not k.startswith("_")] from detectron2.utils.env import fixup_module_metadata fixup_module_metadata(__name__, globals(), __all__) del fixup_module_metadata ================================================ FILE: detectron2/detectron2/modeling/anchor_generator.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import collections import math from typing import List import torch from torch import nn from detectron2.config import configurable from detectron2.layers import ShapeSpec, move_device_like from detectron2.structures import Boxes, RotatedBoxes from detectron2.utils.registry import Registry ANCHOR_GENERATOR_REGISTRY = Registry("ANCHOR_GENERATOR") ANCHOR_GENERATOR_REGISTRY.__doc__ = """ Registry for modules that creates object detection anchors for feature maps. The registered object will be called with `obj(cfg, input_shape)`. """ class BufferList(nn.Module): """ Similar to nn.ParameterList, but for buffers """ def __init__(self, buffers): super().__init__() for i, buffer in enumerate(buffers): # Use non-persistent buffer so the values are not saved in checkpoint self.register_buffer(str(i), buffer, persistent=False) def __len__(self): return len(self._buffers) def __iter__(self): return iter(self._buffers.values()) def _create_grid_offsets( size: List[int], stride: int, offset: float, target_device_tensor: torch.Tensor ): grid_height, grid_width = size shifts_x = move_device_like( torch.arange(offset * stride, grid_width * stride, step=stride, dtype=torch.float32), target_device_tensor, ) shifts_y = move_device_like( torch.arange(offset * stride, grid_height * stride, step=stride, dtype=torch.float32), target_device_tensor, ) shift_y, shift_x = torch.meshgrid(shifts_y, shifts_x) shift_x = shift_x.reshape(-1) shift_y = shift_y.reshape(-1) return shift_x, shift_y def _broadcast_params(params, num_features, name): """ If one size (or aspect ratio) is specified and there are multiple feature maps, we "broadcast" anchors of that single size (or aspect ratio) over all feature maps. If params is list[float], or list[list[float]] with len(params) == 1, repeat it num_features time. Returns: list[list[float]]: param for each feature """ assert isinstance( params, collections.abc.Sequence ), f"{name} in anchor generator has to be a list! Got {params}." assert len(params), f"{name} in anchor generator cannot be empty!" if not isinstance(params[0], collections.abc.Sequence): # params is list[float] return [params] * num_features if len(params) == 1: return list(params) * num_features assert len(params) == num_features, ( f"Got {name} of length {len(params)} in anchor generator, " f"but the number of input features is {num_features}!" ) return params @ANCHOR_GENERATOR_REGISTRY.register() class DefaultAnchorGenerator(nn.Module): """ Compute anchors in the standard ways described in "Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks". """ box_dim: torch.jit.Final[int] = 4 """ the dimension of each anchor box. """ @configurable def __init__(self, *, sizes, aspect_ratios, strides, offset=0.5): """ This interface is experimental. Args: sizes (list[list[float]] or list[float]): If ``sizes`` is list[list[float]], ``sizes[i]`` is the list of anchor sizes (i.e. sqrt of anchor area) to use for the i-th feature map. If ``sizes`` is list[float], ``sizes`` is used for all feature maps. Anchor sizes are given in absolute lengths in units of the input image; they do not dynamically scale if the input image size changes. aspect_ratios (list[list[float]] or list[float]): list of aspect ratios (i.e. height / width) to use for anchors. Same "broadcast" rule for `sizes` applies. strides (list[int]): stride of each input feature. offset (float): Relative offset between the center of the first anchor and the top-left corner of the image. Value has to be in [0, 1). Recommend to use 0.5, which means half stride. """ super().__init__() self.strides = strides self.num_features = len(self.strides) sizes = _broadcast_params(sizes, self.num_features, "sizes") aspect_ratios = _broadcast_params(aspect_ratios, self.num_features, "aspect_ratios") self.cell_anchors = self._calculate_anchors(sizes, aspect_ratios) self.offset = offset assert 0.0 <= self.offset < 1.0, self.offset @classmethod def from_config(cls, cfg, input_shape: List[ShapeSpec]): return { "sizes": cfg.MODEL.ANCHOR_GENERATOR.SIZES, "aspect_ratios": cfg.MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS, "strides": [x.stride for x in input_shape], "offset": cfg.MODEL.ANCHOR_GENERATOR.OFFSET, } def _calculate_anchors(self, sizes, aspect_ratios): cell_anchors = [ self.generate_cell_anchors(s, a).float() for s, a in zip(sizes, aspect_ratios) ] return BufferList(cell_anchors) @property @torch.jit.unused def num_cell_anchors(self): """ Alias of `num_anchors`. """ return self.num_anchors @property @torch.jit.unused def num_anchors(self): """ Returns: list[int]: Each int is the number of anchors at every pixel location, on that feature map. For example, if at every pixel we use anchors of 3 aspect ratios and 5 sizes, the number of anchors is 15. (See also ANCHOR_GENERATOR.SIZES and ANCHOR_GENERATOR.ASPECT_RATIOS in config) In standard RPN models, `num_anchors` on every feature map is the same. """ return [len(cell_anchors) for cell_anchors in self.cell_anchors] def _grid_anchors(self, grid_sizes: List[List[int]]): """ Returns: list[Tensor]: #featuremap tensors, each is (#locations x #cell_anchors) x 4 """ anchors = [] # buffers() not supported by torchscript. use named_buffers() instead buffers: List[torch.Tensor] = [x[1] for x in self.cell_anchors.named_buffers()] for size, stride, base_anchors in zip(grid_sizes, self.strides, buffers): shift_x, shift_y = _create_grid_offsets(size, stride, self.offset, base_anchors) shifts = torch.stack((shift_x, shift_y, shift_x, shift_y), dim=1) anchors.append((shifts.view(-1, 1, 4) + base_anchors.view(1, -1, 4)).reshape(-1, 4)) return anchors def generate_cell_anchors(self, sizes=(32, 64, 128, 256, 512), aspect_ratios=(0.5, 1, 2)): """ Generate a tensor storing canonical anchor boxes, which are all anchor boxes of different sizes and aspect_ratios centered at (0, 0). We can later build the set of anchors for a full feature map by shifting and tiling these tensors (see `meth:_grid_anchors`). Args: sizes (tuple[float]): aspect_ratios (tuple[float]]): Returns: Tensor of shape (len(sizes) * len(aspect_ratios), 4) storing anchor boxes in XYXY format. """ # This is different from the anchor generator defined in the original Faster R-CNN # code or Detectron. They yield the same AP, however the old version defines cell # anchors in a less natural way with a shift relative to the feature grid and # quantization that results in slightly different sizes for different aspect ratios. # See also https://github.com/facebookresearch/Detectron/issues/227 anchors = [] for size in sizes: area = size**2.0 for aspect_ratio in aspect_ratios: # s * s = w * h # a = h / w # ... some algebra ... # w = sqrt(s * s / a) # h = a * w w = math.sqrt(area / aspect_ratio) h = aspect_ratio * w x0, y0, x1, y1 = -w / 2.0, -h / 2.0, w / 2.0, h / 2.0 anchors.append([x0, y0, x1, y1]) return torch.tensor(anchors) def forward(self, features: List[torch.Tensor]): """ Args: features (list[Tensor]): list of backbone feature maps on which to generate anchors. Returns: list[Boxes]: a list of Boxes containing all the anchors for each feature map (i.e. the cell anchors repeated over all locations in the feature map). The number of anchors of each feature map is Hi x Wi x num_cell_anchors, where Hi, Wi are resolution of the feature map divided by anchor stride. """ grid_sizes = [feature_map.shape[-2:] for feature_map in features] anchors_over_all_feature_maps = self._grid_anchors(grid_sizes) return [Boxes(x) for x in anchors_over_all_feature_maps] @ANCHOR_GENERATOR_REGISTRY.register() class RotatedAnchorGenerator(nn.Module): """ Compute rotated anchors used by Rotated RPN (RRPN), described in "Arbitrary-Oriented Scene Text Detection via Rotation Proposals". """ box_dim: int = 5 """ the dimension of each anchor box. """ @configurable def __init__(self, *, sizes, aspect_ratios, strides, angles, offset=0.5): """ This interface is experimental. Args: sizes (list[list[float]] or list[float]): If sizes is list[list[float]], sizes[i] is the list of anchor sizes (i.e. sqrt of anchor area) to use for the i-th feature map. If sizes is list[float], the sizes are used for all feature maps. Anchor sizes are given in absolute lengths in units of the input image; they do not dynamically scale if the input image size changes. aspect_ratios (list[list[float]] or list[float]): list of aspect ratios (i.e. height / width) to use for anchors. Same "broadcast" rule for `sizes` applies. strides (list[int]): stride of each input feature. angles (list[list[float]] or list[float]): list of angles (in degrees CCW) to use for anchors. Same "broadcast" rule for `sizes` applies. offset (float): Relative offset between the center of the first anchor and the top-left corner of the image. Value has to be in [0, 1). Recommend to use 0.5, which means half stride. """ super().__init__() self.strides = strides self.num_features = len(self.strides) sizes = _broadcast_params(sizes, self.num_features, "sizes") aspect_ratios = _broadcast_params(aspect_ratios, self.num_features, "aspect_ratios") angles = _broadcast_params(angles, self.num_features, "angles") self.cell_anchors = self._calculate_anchors(sizes, aspect_ratios, angles) self.offset = offset assert 0.0 <= self.offset < 1.0, self.offset @classmethod def from_config(cls, cfg, input_shape: List[ShapeSpec]): return { "sizes": cfg.MODEL.ANCHOR_GENERATOR.SIZES, "aspect_ratios": cfg.MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS, "strides": [x.stride for x in input_shape], "offset": cfg.MODEL.ANCHOR_GENERATOR.OFFSET, "angles": cfg.MODEL.ANCHOR_GENERATOR.ANGLES, } def _calculate_anchors(self, sizes, aspect_ratios, angles): cell_anchors = [ self.generate_cell_anchors(size, aspect_ratio, angle).float() for size, aspect_ratio, angle in zip(sizes, aspect_ratios, angles) ] return BufferList(cell_anchors) @property def num_cell_anchors(self): """ Alias of `num_anchors`. """ return self.num_anchors @property def num_anchors(self): """ Returns: list[int]: Each int is the number of anchors at every pixel location, on that feature map. For example, if at every pixel we use anchors of 3 aspect ratios, 2 sizes and 5 angles, the number of anchors is 30. (See also ANCHOR_GENERATOR.SIZES, ANCHOR_GENERATOR.ASPECT_RATIOS and ANCHOR_GENERATOR.ANGLES in config) In standard RRPN models, `num_anchors` on every feature map is the same. """ return [len(cell_anchors) for cell_anchors in self.cell_anchors] def _grid_anchors(self, grid_sizes): anchors = [] for size, stride, base_anchors in zip(grid_sizes, self.strides, self.cell_anchors): shift_x, shift_y = _create_grid_offsets(size, stride, self.offset, base_anchors) zeros = torch.zeros_like(shift_x) shifts = torch.stack((shift_x, shift_y, zeros, zeros, zeros), dim=1) anchors.append((shifts.view(-1, 1, 5) + base_anchors.view(1, -1, 5)).reshape(-1, 5)) return anchors def generate_cell_anchors( self, sizes=(32, 64, 128, 256, 512), aspect_ratios=(0.5, 1, 2), angles=(-90, -60, -30, 0, 30, 60, 90), ): """ Generate a tensor storing canonical anchor boxes, which are all anchor boxes of different sizes, aspect_ratios, angles centered at (0, 0). We can later build the set of anchors for a full feature map by shifting and tiling these tensors (see `meth:_grid_anchors`). Args: sizes (tuple[float]): aspect_ratios (tuple[float]]): angles (tuple[float]]): Returns: Tensor of shape (len(sizes) * len(aspect_ratios) * len(angles), 5) storing anchor boxes in (x_ctr, y_ctr, w, h, angle) format. """ anchors = [] for size in sizes: area = size**2.0 for aspect_ratio in aspect_ratios: # s * s = w * h # a = h / w # ... some algebra ... # w = sqrt(s * s / a) # h = a * w w = math.sqrt(area / aspect_ratio) h = aspect_ratio * w anchors.extend([0, 0, w, h, a] for a in angles) return torch.tensor(anchors) def forward(self, features): """ Args: features (list[Tensor]): list of backbone feature maps on which to generate anchors. Returns: list[RotatedBoxes]: a list of Boxes containing all the anchors for each feature map (i.e. the cell anchors repeated over all locations in the feature map). The number of anchors of each feature map is Hi x Wi x num_cell_anchors, where Hi, Wi are resolution of the feature map divided by anchor stride. """ grid_sizes = [feature_map.shape[-2:] for feature_map in features] anchors_over_all_feature_maps = self._grid_anchors(grid_sizes) return [RotatedBoxes(x) for x in anchors_over_all_feature_maps] def build_anchor_generator(cfg, input_shape): """ Built an anchor generator from `cfg.MODEL.ANCHOR_GENERATOR.NAME`. """ anchor_generator = cfg.MODEL.ANCHOR_GENERATOR.NAME return ANCHOR_GENERATOR_REGISTRY.get(anchor_generator)(cfg, input_shape) ================================================ FILE: detectron2/detectron2/modeling/backbone/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .build import build_backbone, BACKBONE_REGISTRY # noqa F401 isort:skip from .backbone import Backbone from .fpn import FPN from .regnet import RegNet from .resnet import ( BasicStem, ResNet, ResNetBlockBase, build_resnet_backbone, make_stage, BottleneckBlock, ) from .vit import ViT, SimpleFeaturePyramid, get_vit_lr_decay_rate from .mvit import MViT from .swin import SwinTransformer __all__ = [k for k in globals().keys() if not k.startswith("_")] # TODO can expose more resnet blocks after careful consideration ================================================ FILE: detectron2/detectron2/modeling/backbone/backbone.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from abc import ABCMeta, abstractmethod from typing import Dict import torch.nn as nn from detectron2.layers import ShapeSpec __all__ = ["Backbone"] class Backbone(nn.Module, metaclass=ABCMeta): """ Abstract base class for network backbones. """ def __init__(self): """ The `__init__` method of any subclass can specify its own set of arguments. """ super().__init__() @abstractmethod def forward(self): """ Subclasses must override this method, but adhere to the same return type. Returns: dict[str->Tensor]: mapping from feature name (e.g., "res2") to tensor """ pass @property def size_divisibility(self) -> int: """ Some backbones require the input height and width to be divisible by a specific integer. This is typically true for encoder / decoder type networks with lateral connection (e.g., FPN) for which feature maps need to match dimension in the "bottom up" and "top down" paths. Set to 0 if no specific input size divisibility is required. """ return 0 @property def padding_constraints(self) -> Dict[str, int]: """ This property is a generalization of size_divisibility. Some backbones and training recipes require specific padding constraints, such as enforcing divisibility by a specific integer (e.g., FPN) or padding to a square (e.g., ViTDet with large-scale jitter in :paper:vitdet). `padding_constraints` contains these optional items like: { "size_divisibility": int, "square_size": int, # Future options are possible } `size_divisibility` will read from here if presented and `square_size` indicates the square padding size if `square_size` > 0. TODO: use type of Dict[str, int] to avoid torchscipt issues. The type of padding_constraints could be generalized as TypedDict (Python 3.8+) to support more types in the future. """ return {} def output_shape(self): """ Returns: dict[str->ShapeSpec] """ # this is a backward-compatible default return { name: ShapeSpec( channels=self._out_feature_channels[name], stride=self._out_feature_strides[name] ) for name in self._out_features } ================================================ FILE: detectron2/detectron2/modeling/backbone/build.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.layers import ShapeSpec from detectron2.utils.registry import Registry from .backbone import Backbone BACKBONE_REGISTRY = Registry("BACKBONE") BACKBONE_REGISTRY.__doc__ = """ Registry for backbones, which extract feature maps from images The registered object must be a callable that accepts two arguments: 1. A :class:`detectron2.config.CfgNode` 2. A :class:`detectron2.layers.ShapeSpec`, which contains the input shape specification. Registered object must return instance of :class:`Backbone`. """ def build_backbone(cfg, input_shape=None): """ Build a backbone from `cfg.MODEL.BACKBONE.NAME`. Returns: an instance of :class:`Backbone` """ if input_shape is None: input_shape = ShapeSpec(channels=len(cfg.MODEL.PIXEL_MEAN)) backbone_name = cfg.MODEL.BACKBONE.NAME backbone = BACKBONE_REGISTRY.get(backbone_name)(cfg, input_shape) assert isinstance(backbone, Backbone) return backbone ================================================ FILE: detectron2/detectron2/modeling/backbone/fpn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import math import fvcore.nn.weight_init as weight_init import torch import torch.nn.functional as F from torch import nn from detectron2.layers import Conv2d, ShapeSpec, get_norm from .backbone import Backbone from .build import BACKBONE_REGISTRY from .resnet import build_resnet_backbone __all__ = ["build_resnet_fpn_backbone", "build_retinanet_resnet_fpn_backbone", "FPN"] class FPN(Backbone): """ This module implements :paper:`FPN`. It creates pyramid features built on top of some input feature maps. """ _fuse_type: torch.jit.Final[str] def __init__( self, bottom_up, in_features, out_channels, norm="", top_block=None, fuse_type="sum", square_pad=0, ): """ Args: bottom_up (Backbone): module representing the bottom up subnetwork. Must be a subclass of :class:`Backbone`. The multi-scale feature maps generated by the bottom up network, and listed in `in_features`, are used to generate FPN levels. in_features (list[str]): names of the input feature maps coming from the backbone to which FPN is attached. For example, if the backbone produces ["res2", "res3", "res4"], any *contiguous* sublist of these may be used; order must be from high to low resolution. out_channels (int): number of channels in the output feature maps. norm (str): the normalization to use. top_block (nn.Module or None): if provided, an extra operation will be performed on the output of the last (smallest resolution) FPN output, and the result will extend the result list. The top_block further downsamples the feature map. It must have an attribute "num_levels", meaning the number of extra FPN levels added by this block, and "in_feature", which is a string representing its input feature (e.g., p5). fuse_type (str): types for fusing the top down features and the lateral ones. It can be "sum" (default), which sums up element-wise; or "avg", which takes the element-wise mean of the two. square_pad (int): If > 0, require input images to be padded to specific square size. """ super(FPN, self).__init__() assert isinstance(bottom_up, Backbone) assert in_features, in_features # Feature map strides and channels from the bottom up network (e.g. ResNet) input_shapes = bottom_up.output_shape() strides = [input_shapes[f].stride for f in in_features] in_channels_per_feature = [input_shapes[f].channels for f in in_features] _assert_strides_are_log2_contiguous(strides) lateral_convs = [] output_convs = [] use_bias = norm == "" for idx, in_channels in enumerate(in_channels_per_feature): lateral_norm = get_norm(norm, out_channels) output_norm = get_norm(norm, out_channels) lateral_conv = Conv2d( in_channels, out_channels, kernel_size=1, bias=use_bias, norm=lateral_norm ) output_conv = Conv2d( out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=use_bias, norm=output_norm, ) weight_init.c2_xavier_fill(lateral_conv) weight_init.c2_xavier_fill(output_conv) stage = int(math.log2(strides[idx])) self.add_module("fpn_lateral{}".format(stage), lateral_conv) self.add_module("fpn_output{}".format(stage), output_conv) lateral_convs.append(lateral_conv) output_convs.append(output_conv) # Place convs into top-down order (from low to high resolution) # to make the top-down computation in forward clearer. self.lateral_convs = lateral_convs[::-1] self.output_convs = output_convs[::-1] self.top_block = top_block self.in_features = tuple(in_features) self.bottom_up = bottom_up # Return feature names are "p", like ["p2", "p3", ..., "p6"] self._out_feature_strides = {"p{}".format(int(math.log2(s))): s for s in strides} # top block output feature maps. if self.top_block is not None: for s in range(stage, stage + self.top_block.num_levels): self._out_feature_strides["p{}".format(s + 1)] = 2 ** (s + 1) self._out_features = list(self._out_feature_strides.keys()) self._out_feature_channels = {k: out_channels for k in self._out_features} self._size_divisibility = strides[-1] self._square_pad = square_pad assert fuse_type in {"avg", "sum"} self._fuse_type = fuse_type @property def size_divisibility(self): return self._size_divisibility @property def padding_constraints(self): return {"square_size": self._square_pad} def forward(self, x): """ Args: input (dict[str->Tensor]): mapping feature map name (e.g., "res5") to feature map tensor for each feature level in high to low resolution order. Returns: dict[str->Tensor]: mapping from feature map name to FPN feature map tensor in high to low resolution order. Returned feature names follow the FPN paper convention: "p", where stage has stride = 2 ** stage e.g., ["p2", "p3", ..., "p6"]. """ bottom_up_features = self.bottom_up(x) results = [] prev_features = self.lateral_convs[0](bottom_up_features[self.in_features[-1]]) results.append(self.output_convs[0](prev_features)) # Reverse feature maps into top-down order (from low to high resolution) for idx, (lateral_conv, output_conv) in enumerate( zip(self.lateral_convs, self.output_convs) ): # Slicing of ModuleList is not supported https://github.com/pytorch/pytorch/issues/47336 # Therefore we loop over all modules but skip the first one if idx > 0: features = self.in_features[-idx - 1] features = bottom_up_features[features] top_down_features = F.interpolate(prev_features, scale_factor=2.0, mode="nearest") lateral_features = lateral_conv(features) prev_features = lateral_features + top_down_features if self._fuse_type == "avg": prev_features /= 2 results.insert(0, output_conv(prev_features)) if self.top_block is not None: if self.top_block.in_feature in bottom_up_features: top_block_in_feature = bottom_up_features[self.top_block.in_feature] else: top_block_in_feature = results[self._out_features.index(self.top_block.in_feature)] results.extend(self.top_block(top_block_in_feature)) assert len(self._out_features) == len(results) return {f: res for f, res in zip(self._out_features, results)} def output_shape(self): return { name: ShapeSpec( channels=self._out_feature_channels[name], stride=self._out_feature_strides[name] ) for name in self._out_features } def _assert_strides_are_log2_contiguous(strides): """ Assert that each stride is 2x times its preceding stride, i.e. "contiguous in log2". """ for i, stride in enumerate(strides[1:], 1): assert stride == 2 * strides[i - 1], "Strides {} {} are not log2 contiguous".format( stride, strides[i - 1] ) class LastLevelMaxPool(nn.Module): """ This module is used in the original FPN to generate a downsampled P6 feature from P5. """ def __init__(self): super().__init__() self.num_levels = 1 self.in_feature = "p5" def forward(self, x): return [F.max_pool2d(x, kernel_size=1, stride=2, padding=0)] class LastLevelP6P7(nn.Module): """ This module is used in RetinaNet to generate extra layers, P6 and P7 from C5 feature. """ def __init__(self, in_channels, out_channels, in_feature="res5"): super().__init__() self.num_levels = 2 self.in_feature = in_feature self.p6 = nn.Conv2d(in_channels, out_channels, 3, 2, 1) self.p7 = nn.Conv2d(out_channels, out_channels, 3, 2, 1) for module in [self.p6, self.p7]: weight_init.c2_xavier_fill(module) def forward(self, c5): p6 = self.p6(c5) p7 = self.p7(F.relu(p6)) return [p6, p7] @BACKBONE_REGISTRY.register() def build_resnet_fpn_backbone(cfg, input_shape: ShapeSpec): """ Args: cfg: a detectron2 CfgNode Returns: backbone (Backbone): backbone module, must be a subclass of :class:`Backbone`. """ bottom_up = build_resnet_backbone(cfg, input_shape) in_features = cfg.MODEL.FPN.IN_FEATURES out_channels = cfg.MODEL.FPN.OUT_CHANNELS backbone = FPN( bottom_up=bottom_up, in_features=in_features, out_channels=out_channels, norm=cfg.MODEL.FPN.NORM, top_block=LastLevelMaxPool(), fuse_type=cfg.MODEL.FPN.FUSE_TYPE, ) return backbone @BACKBONE_REGISTRY.register() def build_retinanet_resnet_fpn_backbone(cfg, input_shape: ShapeSpec): """ Args: cfg: a detectron2 CfgNode Returns: backbone (Backbone): backbone module, must be a subclass of :class:`Backbone`. """ bottom_up = build_resnet_backbone(cfg, input_shape) in_features = cfg.MODEL.FPN.IN_FEATURES out_channels = cfg.MODEL.FPN.OUT_CHANNELS in_channels_p6p7 = bottom_up.output_shape()["res5"].channels backbone = FPN( bottom_up=bottom_up, in_features=in_features, out_channels=out_channels, norm=cfg.MODEL.FPN.NORM, top_block=LastLevelP6P7(in_channels_p6p7, out_channels), fuse_type=cfg.MODEL.FPN.FUSE_TYPE, ) return backbone ================================================ FILE: detectron2/detectron2/modeling/backbone/mvit.py ================================================ import logging import numpy as np import torch import torch.nn as nn from fairscale.nn.checkpoint import checkpoint_wrapper from timm.models.layers import DropPath, Mlp, trunc_normal_ from .backbone import Backbone from .utils import ( PatchEmbed, add_decomposed_rel_pos, get_abs_pos, window_partition, window_unpartition, ) logger = logging.getLogger(__name__) __all__ = ["MViT"] def attention_pool(x, pool, norm=None): # (B, H, W, C) -> (B, C, H, W) x = x.permute(0, 3, 1, 2) x = pool(x) # (B, C, H1, W1) -> (B, H1, W1, C) x = x.permute(0, 2, 3, 1) if norm: x = norm(x) return x class MultiScaleAttention(nn.Module): """Multiscale Multi-head Attention block.""" def __init__( self, dim, dim_out, num_heads, qkv_bias=True, norm_layer=nn.LayerNorm, pool_kernel=(3, 3), stride_q=1, stride_kv=1, residual_pooling=True, window_size=0, use_rel_pos=False, rel_pos_zero_init=True, input_size=None, ): """ Args: dim (int): Number of input channels. dim_out (int): Number of output channels. num_heads (int): Number of attention heads. qkv_bias (bool: If True, add a learnable bias to query, key, value. norm_layer (nn.Module): Normalization layer. pool_kernel (tuple): kernel size for qkv pooling layers. stride_q (int): stride size for q pooling layer. stride_kv (int): stride size for kv pooling layer. residual_pooling (bool): If true, enable residual pooling. use_rel_pos (bool): If True, add relative postional embeddings to the attention map. rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. input_size (int or None): Input resolution. """ super().__init__() self.num_heads = num_heads head_dim = dim_out // num_heads self.scale = head_dim**-0.5 self.qkv = nn.Linear(dim, dim_out * 3, bias=qkv_bias) self.proj = nn.Linear(dim_out, dim_out) # qkv pooling pool_padding = [k // 2 for k in pool_kernel] dim_conv = dim_out // num_heads self.pool_q = nn.Conv2d( dim_conv, dim_conv, pool_kernel, stride=stride_q, padding=pool_padding, groups=dim_conv, bias=False, ) self.norm_q = norm_layer(dim_conv) self.pool_k = nn.Conv2d( dim_conv, dim_conv, pool_kernel, stride=stride_kv, padding=pool_padding, groups=dim_conv, bias=False, ) self.norm_k = norm_layer(dim_conv) self.pool_v = nn.Conv2d( dim_conv, dim_conv, pool_kernel, stride=stride_kv, padding=pool_padding, groups=dim_conv, bias=False, ) self.norm_v = norm_layer(dim_conv) self.window_size = window_size if window_size: self.q_win_size = window_size // stride_q self.kv_win_size = window_size // stride_kv self.residual_pooling = residual_pooling self.use_rel_pos = use_rel_pos if self.use_rel_pos: # initialize relative positional embeddings assert input_size[0] == input_size[1] size = input_size[0] rel_dim = 2 * max(size // stride_q, size // stride_kv) - 1 self.rel_pos_h = nn.Parameter(torch.zeros(rel_dim, head_dim)) self.rel_pos_w = nn.Parameter(torch.zeros(rel_dim, head_dim)) if not rel_pos_zero_init: trunc_normal_(self.rel_pos_h, std=0.02) trunc_normal_(self.rel_pos_w, std=0.02) def forward(self, x): B, H, W, _ = x.shape # qkv with shape (3, B, nHead, H, W, C) qkv = self.qkv(x).reshape(B, H, W, 3, self.num_heads, -1).permute(3, 0, 4, 1, 2, 5) # q, k, v with shape (B * nHead, H, W, C) q, k, v = qkv.reshape(3, B * self.num_heads, H, W, -1).unbind(0) q = attention_pool(q, self.pool_q, self.norm_q) k = attention_pool(k, self.pool_k, self.norm_k) v = attention_pool(v, self.pool_v, self.norm_v) ori_q = q if self.window_size: q, q_hw_pad = window_partition(q, self.q_win_size) k, kv_hw_pad = window_partition(k, self.kv_win_size) v, _ = window_partition(v, self.kv_win_size) q_hw = (self.q_win_size, self.q_win_size) kv_hw = (self.kv_win_size, self.kv_win_size) else: q_hw = q.shape[1:3] kv_hw = k.shape[1:3] q = q.view(q.shape[0], np.prod(q_hw), -1) k = k.view(k.shape[0], np.prod(kv_hw), -1) v = v.view(v.shape[0], np.prod(kv_hw), -1) attn = (q * self.scale) @ k.transpose(-2, -1) if self.use_rel_pos: attn = add_decomposed_rel_pos(attn, q, self.rel_pos_h, self.rel_pos_w, q_hw, kv_hw) attn = attn.softmax(dim=-1) x = attn @ v x = x.view(x.shape[0], q_hw[0], q_hw[1], -1) if self.window_size: x = window_unpartition(x, self.q_win_size, q_hw_pad, ori_q.shape[1:3]) if self.residual_pooling: x += ori_q H, W = x.shape[1], x.shape[2] x = x.view(B, self.num_heads, H, W, -1).permute(0, 2, 3, 1, 4).reshape(B, H, W, -1) x = self.proj(x) return x class MultiScaleBlock(nn.Module): """Multiscale Transformer blocks""" def __init__( self, dim, dim_out, num_heads, mlp_ratio=4.0, qkv_bias=True, drop_path=0.0, norm_layer=nn.LayerNorm, act_layer=nn.GELU, qkv_pool_kernel=(3, 3), stride_q=1, stride_kv=1, residual_pooling=True, window_size=0, use_rel_pos=False, rel_pos_zero_init=True, input_size=None, ): """ Args: dim (int): Number of input channels. dim_out (int): Number of output channels. num_heads (int): Number of attention heads in the MViT block. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. qkv_bias (bool): If True, add a learnable bias to query, key, value. drop_path (float): Stochastic depth rate. norm_layer (nn.Module): Normalization layer. act_layer (nn.Module): Activation layer. qkv_pool_kernel (tuple): kernel size for qkv pooling layers. stride_q (int): stride size for q pooling layer. stride_kv (int): stride size for kv pooling layer. residual_pooling (bool): If true, enable residual pooling. window_size (int): Window size for window attention blocks. If it equals 0, then not use window attention. use_rel_pos (bool): If True, add relative postional embeddings to the attention map. rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. input_size (int or None): Input resolution. """ super().__init__() self.norm1 = norm_layer(dim) self.attn = MultiScaleAttention( dim, dim_out, num_heads=num_heads, qkv_bias=qkv_bias, norm_layer=norm_layer, pool_kernel=qkv_pool_kernel, stride_q=stride_q, stride_kv=stride_kv, residual_pooling=residual_pooling, window_size=window_size, use_rel_pos=use_rel_pos, rel_pos_zero_init=rel_pos_zero_init, input_size=input_size, ) self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity() self.norm2 = norm_layer(dim_out) self.mlp = Mlp( in_features=dim_out, hidden_features=int(dim_out * mlp_ratio), out_features=dim_out, act_layer=act_layer, ) if dim != dim_out: self.proj = nn.Linear(dim, dim_out) if stride_q > 1: kernel_skip = stride_q + 1 padding_skip = int(kernel_skip // 2) self.pool_skip = nn.MaxPool2d(kernel_skip, stride_q, padding_skip, ceil_mode=False) def forward(self, x): x_norm = self.norm1(x) x_block = self.attn(x_norm) if hasattr(self, "proj"): x = self.proj(x_norm) if hasattr(self, "pool_skip"): x = attention_pool(x, self.pool_skip) x = x + self.drop_path(x_block) x = x + self.drop_path(self.mlp(self.norm2(x))) return x class MViT(Backbone): """ This module implements Multiscale Vision Transformer (MViT) backbone in :paper:'mvitv2'. """ def __init__( self, img_size=224, patch_kernel=(7, 7), patch_stride=(4, 4), patch_padding=(3, 3), in_chans=3, embed_dim=96, depth=16, num_heads=1, last_block_indexes=(0, 2, 11, 15), qkv_pool_kernel=(3, 3), adaptive_kv_stride=4, adaptive_window_size=56, residual_pooling=True, mlp_ratio=4.0, qkv_bias=True, drop_path_rate=0.0, norm_layer=nn.LayerNorm, act_layer=nn.GELU, use_abs_pos=False, use_rel_pos=True, rel_pos_zero_init=True, use_act_checkpoint=False, pretrain_img_size=224, pretrain_use_cls_token=True, out_features=("scale2", "scale3", "scale4", "scale5"), ): """ Args: img_size (int): Input image size. patch_kernel (tuple): kernel size for patch embedding. patch_stride (tuple): stride size for patch embedding. patch_padding (tuple): padding size for patch embedding. in_chans (int): Number of input image channels. embed_dim (int): Patch embedding dimension. depth (int): Depth of MViT. num_heads (int): Number of base attention heads in each MViT block. last_block_indexes (tuple): Block indexes for last blocks in each stage. qkv_pool_kernel (tuple): kernel size for qkv pooling layers. adaptive_kv_stride (int): adaptive stride size for kv pooling. adaptive_window_size (int): adaptive window size for window attention blocks. residual_pooling (bool): If true, enable residual pooling. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. qkv_bias (bool): If True, add a learnable bias to query, key, value. drop_path_rate (float): Stochastic depth rate. norm_layer (nn.Module): Normalization layer. act_layer (nn.Module): Activation layer. use_abs_pos (bool): If True, use absolute positional embeddings. use_rel_pos (bool): If True, add relative postional embeddings to the attention map. rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. window_size (int): Window size for window attention blocks. use_act_checkpoint (bool): If True, use activation checkpointing. pretrain_img_size (int): input image size for pretraining models. pretrain_use_cls_token (bool): If True, pretrainig models use class token. out_features (tuple): name of the feature maps from each stage. """ super().__init__() self.pretrain_use_cls_token = pretrain_use_cls_token self.patch_embed = PatchEmbed( kernel_size=patch_kernel, stride=patch_stride, padding=patch_padding, in_chans=in_chans, embed_dim=embed_dim, ) if use_abs_pos: # Initialize absoluate positional embedding with pretrain image size. num_patches = (pretrain_img_size // patch_stride[0]) * ( pretrain_img_size // patch_stride[1] ) num_positions = (num_patches + 1) if pretrain_use_cls_token else num_patches self.pos_embed = nn.Parameter(torch.zeros(1, num_positions, embed_dim)) else: self.pos_embed = None # stochastic depth decay rule dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] dim_out = embed_dim stride_kv = adaptive_kv_stride window_size = adaptive_window_size input_size = (img_size // patch_stride[0], img_size // patch_stride[1]) stage = 2 stride = patch_stride[0] self._out_feature_strides = {} self._out_feature_channels = {} self.blocks = nn.ModuleList() for i in range(depth): # Multiply stride_kv by 2 if it's the last block of stage2 and stage3. if i == last_block_indexes[1] or i == last_block_indexes[2]: stride_kv_ = stride_kv * 2 else: stride_kv_ = stride_kv # hybrid window attention: global attention in last three stages. window_size_ = 0 if i in last_block_indexes[1:] else window_size block = MultiScaleBlock( dim=embed_dim, dim_out=dim_out, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop_path=dpr[i], norm_layer=norm_layer, qkv_pool_kernel=qkv_pool_kernel, stride_q=2 if i - 1 in last_block_indexes else 1, stride_kv=stride_kv_, residual_pooling=residual_pooling, window_size=window_size_, use_rel_pos=use_rel_pos, rel_pos_zero_init=rel_pos_zero_init, input_size=input_size, ) if use_act_checkpoint: block = checkpoint_wrapper(block) self.blocks.append(block) embed_dim = dim_out if i in last_block_indexes: name = f"scale{stage}" if name in out_features: self._out_feature_channels[name] = dim_out self._out_feature_strides[name] = stride self.add_module(f"{name}_norm", norm_layer(dim_out)) dim_out *= 2 num_heads *= 2 stride_kv = max(stride_kv // 2, 1) stride *= 2 stage += 1 if i - 1 in last_block_indexes: window_size = window_size // 2 input_size = [s // 2 for s in input_size] self._out_features = out_features self._last_block_indexes = last_block_indexes if self.pos_embed is not None: trunc_normal_(self.pos_embed, std=0.02) self.apply(self._init_weights) def _init_weights(self, m): if isinstance(m, nn.Linear): trunc_normal_(m.weight, std=0.02) if isinstance(m, nn.Linear) and m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.LayerNorm): nn.init.constant_(m.bias, 0) nn.init.constant_(m.weight, 1.0) def forward(self, x): x = self.patch_embed(x) if self.pos_embed is not None: x = x + get_abs_pos(self.pos_embed, self.pretrain_use_cls_token, x.shape[1:3]) outputs = {} stage = 2 for i, blk in enumerate(self.blocks): x = blk(x) if i in self._last_block_indexes: name = f"scale{stage}" if name in self._out_features: x_out = getattr(self, f"{name}_norm")(x) outputs[name] = x_out.permute(0, 3, 1, 2) stage += 1 return outputs ================================================ FILE: detectron2/detectron2/modeling/backbone/regnet.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved """ Implementation of RegNet models from :paper:`dds` and :paper:`scaling`. This code is adapted from https://github.com/facebookresearch/pycls with minimal modifications. Some code duplication exists between RegNet and ResNets (e.g., ResStem) in order to simplify model loading. """ import numpy as np from torch import nn from detectron2.layers import CNNBlockBase, ShapeSpec, get_norm from .backbone import Backbone __all__ = [ "AnyNet", "RegNet", "ResStem", "SimpleStem", "VanillaBlock", "ResBasicBlock", "ResBottleneckBlock", ] def conv2d(w_in, w_out, k, *, stride=1, groups=1, bias=False): """Helper for building a conv2d layer.""" assert k % 2 == 1, "Only odd size kernels supported to avoid padding issues." s, p, g, b = stride, (k - 1) // 2, groups, bias return nn.Conv2d(w_in, w_out, k, stride=s, padding=p, groups=g, bias=b) def gap2d(): """Helper for building a global average pooling layer.""" return nn.AdaptiveAvgPool2d((1, 1)) def pool2d(k, *, stride=1): """Helper for building a pool2d layer.""" assert k % 2 == 1, "Only odd size kernels supported to avoid padding issues." return nn.MaxPool2d(k, stride=stride, padding=(k - 1) // 2) def init_weights(m): """Performs ResNet-style weight initialization.""" if isinstance(m, nn.Conv2d): # Note that there is no bias due to BN fan_out = m.kernel_size[0] * m.kernel_size[1] * m.out_channels m.weight.data.normal_(mean=0.0, std=np.sqrt(2.0 / fan_out)) elif isinstance(m, nn.BatchNorm2d): m.weight.data.fill_(1.0) m.bias.data.zero_() elif isinstance(m, nn.Linear): m.weight.data.normal_(mean=0.0, std=0.01) m.bias.data.zero_() class ResStem(CNNBlockBase): """ResNet stem for ImageNet: 7x7, BN, AF, MaxPool.""" def __init__(self, w_in, w_out, norm, activation_class): super().__init__(w_in, w_out, 4) self.conv = conv2d(w_in, w_out, 7, stride=2) self.bn = get_norm(norm, w_out) self.af = activation_class() self.pool = pool2d(3, stride=2) def forward(self, x): for layer in self.children(): x = layer(x) return x class SimpleStem(CNNBlockBase): """Simple stem for ImageNet: 3x3, BN, AF.""" def __init__(self, w_in, w_out, norm, activation_class): super().__init__(w_in, w_out, 2) self.conv = conv2d(w_in, w_out, 3, stride=2) self.bn = get_norm(norm, w_out) self.af = activation_class() def forward(self, x): for layer in self.children(): x = layer(x) return x class SE(nn.Module): """Squeeze-and-Excitation (SE) block: AvgPool, FC, Act, FC, Sigmoid.""" def __init__(self, w_in, w_se, activation_class): super().__init__() self.avg_pool = gap2d() self.f_ex = nn.Sequential( conv2d(w_in, w_se, 1, bias=True), activation_class(), conv2d(w_se, w_in, 1, bias=True), nn.Sigmoid(), ) def forward(self, x): return x * self.f_ex(self.avg_pool(x)) class VanillaBlock(CNNBlockBase): """Vanilla block: [3x3 conv, BN, Relu] x2.""" def __init__(self, w_in, w_out, stride, norm, activation_class, _params): super().__init__(w_in, w_out, stride) self.a = conv2d(w_in, w_out, 3, stride=stride) self.a_bn = get_norm(norm, w_out) self.a_af = activation_class() self.b = conv2d(w_out, w_out, 3) self.b_bn = get_norm(norm, w_out) self.b_af = activation_class() def forward(self, x): for layer in self.children(): x = layer(x) return x class BasicTransform(nn.Module): """Basic transformation: [3x3 conv, BN, Relu] x2.""" def __init__(self, w_in, w_out, stride, norm, activation_class, _params): super().__init__() self.a = conv2d(w_in, w_out, 3, stride=stride) self.a_bn = get_norm(norm, w_out) self.a_af = activation_class() self.b = conv2d(w_out, w_out, 3) self.b_bn = get_norm(norm, w_out) self.b_bn.final_bn = True def forward(self, x): for layer in self.children(): x = layer(x) return x class ResBasicBlock(CNNBlockBase): """Residual basic block: x + f(x), f = basic transform.""" def __init__(self, w_in, w_out, stride, norm, activation_class, params): super().__init__(w_in, w_out, stride) self.proj, self.bn = None, None if (w_in != w_out) or (stride != 1): self.proj = conv2d(w_in, w_out, 1, stride=stride) self.bn = get_norm(norm, w_out) self.f = BasicTransform(w_in, w_out, stride, norm, activation_class, params) self.af = activation_class() def forward(self, x): x_p = self.bn(self.proj(x)) if self.proj else x return self.af(x_p + self.f(x)) class BottleneckTransform(nn.Module): """Bottleneck transformation: 1x1, 3x3 [+SE], 1x1.""" def __init__(self, w_in, w_out, stride, norm, activation_class, params): super().__init__() w_b = int(round(w_out * params["bot_mul"])) w_se = int(round(w_in * params["se_r"])) groups = w_b // params["group_w"] self.a = conv2d(w_in, w_b, 1) self.a_bn = get_norm(norm, w_b) self.a_af = activation_class() self.b = conv2d(w_b, w_b, 3, stride=stride, groups=groups) self.b_bn = get_norm(norm, w_b) self.b_af = activation_class() self.se = SE(w_b, w_se, activation_class) if w_se else None self.c = conv2d(w_b, w_out, 1) self.c_bn = get_norm(norm, w_out) self.c_bn.final_bn = True def forward(self, x): for layer in self.children(): x = layer(x) return x class ResBottleneckBlock(CNNBlockBase): """Residual bottleneck block: x + f(x), f = bottleneck transform.""" def __init__(self, w_in, w_out, stride, norm, activation_class, params): super().__init__(w_in, w_out, stride) self.proj, self.bn = None, None if (w_in != w_out) or (stride != 1): self.proj = conv2d(w_in, w_out, 1, stride=stride) self.bn = get_norm(norm, w_out) self.f = BottleneckTransform(w_in, w_out, stride, norm, activation_class, params) self.af = activation_class() def forward(self, x): x_p = self.bn(self.proj(x)) if self.proj else x return self.af(x_p + self.f(x)) class AnyStage(nn.Module): """AnyNet stage (sequence of blocks w/ the same output shape).""" def __init__(self, w_in, w_out, stride, d, block_class, norm, activation_class, params): super().__init__() for i in range(d): block = block_class(w_in, w_out, stride, norm, activation_class, params) self.add_module("b{}".format(i + 1), block) stride, w_in = 1, w_out def forward(self, x): for block in self.children(): x = block(x) return x class AnyNet(Backbone): """AnyNet model. See :paper:`dds`.""" def __init__( self, *, stem_class, stem_width, block_class, depths, widths, group_widths, strides, bottleneck_ratios, se_ratio, activation_class, freeze_at=0, norm="BN", out_features=None, ): """ Args: stem_class (callable): A callable taking 4 arguments (channels in, channels out, normalization, callable returning an activation function) that returns another callable implementing the stem module. stem_width (int): The number of output channels that the stem produces. block_class (callable): A callable taking 6 arguments (channels in, channels out, stride, normalization, callable returning an activation function, a dict of block-specific parameters) that returns another callable implementing the repeated block module. depths (list[int]): Number of blocks in each stage. widths (list[int]): For each stage, the number of output channels of each block. group_widths (list[int]): For each stage, the number of channels per group in group convolution, if the block uses group convolution. strides (list[int]): The stride that each network stage applies to its input. bottleneck_ratios (list[float]): For each stage, the ratio of the number of bottleneck channels to the number of block input channels (or, equivalently, output channels), if the block uses a bottleneck. se_ratio (float): The ratio of the number of channels used inside the squeeze-excitation (SE) module to it number of input channels, if SE the block uses SE. activation_class (callable): A callable taking no arguments that returns another callable implementing an activation function. freeze_at (int): The number of stages at the beginning to freeze. see :meth:`freeze` for detailed explanation. norm (str or callable): normalization for all conv layers. See :func:`layers.get_norm` for supported format. out_features (list[str]): name of the layers whose outputs should be returned in forward. RegNet's use "stem" and "s1", "s2", etc for the stages after the stem. If None, will return the output of the last layer. """ super().__init__() self.stem = stem_class(3, stem_width, norm, activation_class) current_stride = self.stem.stride self._out_feature_strides = {"stem": current_stride} self._out_feature_channels = {"stem": self.stem.out_channels} self.stages_and_names = [] prev_w = stem_width for i, (d, w, s, b, g) in enumerate( zip(depths, widths, strides, bottleneck_ratios, group_widths) ): params = {"bot_mul": b, "group_w": g, "se_r": se_ratio} stage = AnyStage(prev_w, w, s, d, block_class, norm, activation_class, params) name = "s{}".format(i + 1) self.add_module(name, stage) self.stages_and_names.append((stage, name)) self._out_feature_strides[name] = current_stride = int( current_stride * np.prod([k.stride for k in stage.children()]) ) self._out_feature_channels[name] = list(stage.children())[-1].out_channels prev_w = w self.apply(init_weights) if out_features is None: out_features = [name] self._out_features = out_features assert len(self._out_features) children = [x[0] for x in self.named_children()] for out_feature in self._out_features: assert out_feature in children, "Available children: {} does not include {}".format( ", ".join(children), out_feature ) self.freeze(freeze_at) def forward(self, x): """ Args: x: Tensor of shape (N,C,H,W). H, W must be a multiple of ``self.size_divisibility``. Returns: dict[str->Tensor]: names and the corresponding features """ assert x.dim() == 4, f"Model takes an input of shape (N, C, H, W). Got {x.shape} instead!" outputs = {} x = self.stem(x) if "stem" in self._out_features: outputs["stem"] = x for stage, name in self.stages_and_names: x = stage(x) if name in self._out_features: outputs[name] = x return outputs def output_shape(self): return { name: ShapeSpec( channels=self._out_feature_channels[name], stride=self._out_feature_strides[name] ) for name in self._out_features } def freeze(self, freeze_at=0): """ Freeze the first several stages of the model. Commonly used in fine-tuning. Layers that produce the same feature map spatial size are defined as one "stage" by :paper:`FPN`. Args: freeze_at (int): number of stages to freeze. `1` means freezing the stem. `2` means freezing the stem and one residual stage, etc. Returns: nn.Module: this model itself """ if freeze_at >= 1: self.stem.freeze() for idx, (stage, _) in enumerate(self.stages_and_names, start=2): if freeze_at >= idx: for block in stage.children(): block.freeze() return self def adjust_block_compatibility(ws, bs, gs): """Adjusts the compatibility of widths, bottlenecks, and groups.""" assert len(ws) == len(bs) == len(gs) assert all(w > 0 and b > 0 and g > 0 for w, b, g in zip(ws, bs, gs)) vs = [int(max(1, w * b)) for w, b in zip(ws, bs)] gs = [int(min(g, v)) for g, v in zip(gs, vs)] ms = [np.lcm(g, b) if b > 1 else g for g, b in zip(gs, bs)] vs = [max(m, int(round(v / m) * m)) for v, m in zip(vs, ms)] ws = [int(v / b) for v, b in zip(vs, bs)] assert all(w * b % g == 0 for w, b, g in zip(ws, bs, gs)) return ws, bs, gs def generate_regnet_parameters(w_a, w_0, w_m, d, q=8): """Generates per stage widths and depths from RegNet parameters.""" assert w_a >= 0 and w_0 > 0 and w_m > 1 and w_0 % q == 0 # Generate continuous per-block ws ws_cont = np.arange(d) * w_a + w_0 # Generate quantized per-block ws ks = np.round(np.log(ws_cont / w_0) / np.log(w_m)) ws_all = w_0 * np.power(w_m, ks) ws_all = np.round(np.divide(ws_all, q)).astype(int) * q # Generate per stage ws and ds (assumes ws_all are sorted) ws, ds = np.unique(ws_all, return_counts=True) # Compute number of actual stages and total possible stages num_stages, total_stages = len(ws), ks.max() + 1 # Convert numpy arrays to lists and return ws, ds, ws_all, ws_cont = (x.tolist() for x in (ws, ds, ws_all, ws_cont)) return ws, ds, num_stages, total_stages, ws_all, ws_cont class RegNet(AnyNet): """RegNet model. See :paper:`dds`.""" def __init__( self, *, stem_class, stem_width, block_class, depth, w_a, w_0, w_m, group_width, stride=2, bottleneck_ratio=1.0, se_ratio=0.0, activation_class=None, freeze_at=0, norm="BN", out_features=None, ): """ Build a RegNet from the parameterization described in :paper:`dds` Section 3.3. Args: See :class:`AnyNet` for arguments that are not listed here. depth (int): Total number of blocks in the RegNet. w_a (float): Factor by which block width would increase prior to quantizing block widths by stage. See :paper:`dds` Section 3.3. w_0 (int): Initial block width. See :paper:`dds` Section 3.3. w_m (float): Parameter controlling block width quantization. See :paper:`dds` Section 3.3. group_width (int): Number of channels per group in group convolution, if the block uses group convolution. bottleneck_ratio (float): The ratio of the number of bottleneck channels to the number of block input channels (or, equivalently, output channels), if the block uses a bottleneck. stride (int): The stride that each network stage applies to its input. """ ws, ds = generate_regnet_parameters(w_a, w_0, w_m, depth)[0:2] ss = [stride for _ in ws] bs = [bottleneck_ratio for _ in ws] gs = [group_width for _ in ws] ws, bs, gs = adjust_block_compatibility(ws, bs, gs) def default_activation_class(): return nn.ReLU(inplace=True) super().__init__( stem_class=stem_class, stem_width=stem_width, block_class=block_class, depths=ds, widths=ws, strides=ss, group_widths=gs, bottleneck_ratios=bs, se_ratio=se_ratio, activation_class=default_activation_class if activation_class is None else activation_class, freeze_at=freeze_at, norm=norm, out_features=out_features, ) ================================================ FILE: detectron2/detectron2/modeling/backbone/resnet.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import fvcore.nn.weight_init as weight_init import torch import torch.nn.functional as F from torch import nn from detectron2.layers import ( CNNBlockBase, Conv2d, DeformConv, ModulatedDeformConv, ShapeSpec, get_norm, ) from .backbone import Backbone from .build import BACKBONE_REGISTRY __all__ = [ "ResNetBlockBase", "BasicBlock", "BottleneckBlock", "DeformBottleneckBlock", "BasicStem", "ResNet", "make_stage", "build_resnet_backbone", ] class BasicBlock(CNNBlockBase): """ The basic residual block for ResNet-18 and ResNet-34 defined in :paper:`ResNet`, with two 3x3 conv layers and a projection shortcut if needed. """ def __init__(self, in_channels, out_channels, *, stride=1, norm="BN"): """ Args: in_channels (int): Number of input channels. out_channels (int): Number of output channels. stride (int): Stride for the first conv. norm (str or callable): normalization for all conv layers. See :func:`layers.get_norm` for supported format. """ super().__init__(in_channels, out_channels, stride) if in_channels != out_channels: self.shortcut = Conv2d( in_channels, out_channels, kernel_size=1, stride=stride, bias=False, norm=get_norm(norm, out_channels), ) else: self.shortcut = None self.conv1 = Conv2d( in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False, norm=get_norm(norm, out_channels), ) self.conv2 = Conv2d( out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False, norm=get_norm(norm, out_channels), ) for layer in [self.conv1, self.conv2, self.shortcut]: if layer is not None: # shortcut can be None weight_init.c2_msra_fill(layer) def forward(self, x): out = self.conv1(x) out = F.relu_(out) out = self.conv2(out) if self.shortcut is not None: shortcut = self.shortcut(x) else: shortcut = x out += shortcut out = F.relu_(out) return out class BottleneckBlock(CNNBlockBase): """ The standard bottleneck residual block used by ResNet-50, 101 and 152 defined in :paper:`ResNet`. It contains 3 conv layers with kernels 1x1, 3x3, 1x1, and a projection shortcut if needed. """ def __init__( self, in_channels, out_channels, *, bottleneck_channels, stride=1, num_groups=1, norm="BN", stride_in_1x1=False, dilation=1, ): """ Args: bottleneck_channels (int): number of output channels for the 3x3 "bottleneck" conv layers. num_groups (int): number of groups for the 3x3 conv layer. norm (str or callable): normalization for all conv layers. See :func:`layers.get_norm` for supported format. stride_in_1x1 (bool): when stride>1, whether to put stride in the first 1x1 convolution or the bottleneck 3x3 convolution. dilation (int): the dilation rate of the 3x3 conv layer. """ super().__init__(in_channels, out_channels, stride) if in_channels != out_channels: self.shortcut = Conv2d( in_channels, out_channels, kernel_size=1, stride=stride, bias=False, norm=get_norm(norm, out_channels), ) else: self.shortcut = None # The original MSRA ResNet models have stride in the first 1x1 conv # The subsequent fb.torch.resnet and Caffe2 ResNe[X]t implementations have # stride in the 3x3 conv stride_1x1, stride_3x3 = (stride, 1) if stride_in_1x1 else (1, stride) self.conv1 = Conv2d( in_channels, bottleneck_channels, kernel_size=1, stride=stride_1x1, bias=False, norm=get_norm(norm, bottleneck_channels), ) self.conv2 = Conv2d( bottleneck_channels, bottleneck_channels, kernel_size=3, stride=stride_3x3, padding=1 * dilation, bias=False, groups=num_groups, dilation=dilation, norm=get_norm(norm, bottleneck_channels), ) self.conv3 = Conv2d( bottleneck_channels, out_channels, kernel_size=1, bias=False, norm=get_norm(norm, out_channels), ) for layer in [self.conv1, self.conv2, self.conv3, self.shortcut]: if layer is not None: # shortcut can be None weight_init.c2_msra_fill(layer) # Zero-initialize the last normalization in each residual branch, # so that at the beginning, the residual branch starts with zeros, # and each residual block behaves like an identity. # See Sec 5.1 in "Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour": # "For BN layers, the learnable scaling coefficient γ is initialized # to be 1, except for each residual block's last BN # where γ is initialized to be 0." # nn.init.constant_(self.conv3.norm.weight, 0) # TODO this somehow hurts performance when training GN models from scratch. # Add it as an option when we need to use this code to train a backbone. def forward(self, x): out = self.conv1(x) out = F.relu_(out) out = self.conv2(out) out = F.relu_(out) out = self.conv3(out) if self.shortcut is not None: shortcut = self.shortcut(x) else: shortcut = x out += shortcut out = F.relu_(out) return out class DeformBottleneckBlock(CNNBlockBase): """ Similar to :class:`BottleneckBlock`, but with :paper:`deformable conv ` in the 3x3 convolution. """ def __init__( self, in_channels, out_channels, *, bottleneck_channels, stride=1, num_groups=1, norm="BN", stride_in_1x1=False, dilation=1, deform_modulated=False, deform_num_groups=1, ): super().__init__(in_channels, out_channels, stride) self.deform_modulated = deform_modulated if in_channels != out_channels: self.shortcut = Conv2d( in_channels, out_channels, kernel_size=1, stride=stride, bias=False, norm=get_norm(norm, out_channels), ) else: self.shortcut = None stride_1x1, stride_3x3 = (stride, 1) if stride_in_1x1 else (1, stride) self.conv1 = Conv2d( in_channels, bottleneck_channels, kernel_size=1, stride=stride_1x1, bias=False, norm=get_norm(norm, bottleneck_channels), ) if deform_modulated: deform_conv_op = ModulatedDeformConv # offset channels are 2 or 3 (if with modulated) * kernel_size * kernel_size offset_channels = 27 else: deform_conv_op = DeformConv offset_channels = 18 self.conv2_offset = Conv2d( bottleneck_channels, offset_channels * deform_num_groups, kernel_size=3, stride=stride_3x3, padding=1 * dilation, dilation=dilation, ) self.conv2 = deform_conv_op( bottleneck_channels, bottleneck_channels, kernel_size=3, stride=stride_3x3, padding=1 * dilation, bias=False, groups=num_groups, dilation=dilation, deformable_groups=deform_num_groups, norm=get_norm(norm, bottleneck_channels), ) self.conv3 = Conv2d( bottleneck_channels, out_channels, kernel_size=1, bias=False, norm=get_norm(norm, out_channels), ) for layer in [self.conv1, self.conv2, self.conv3, self.shortcut]: if layer is not None: # shortcut can be None weight_init.c2_msra_fill(layer) nn.init.constant_(self.conv2_offset.weight, 0) nn.init.constant_(self.conv2_offset.bias, 0) def forward(self, x): out = self.conv1(x) out = F.relu_(out) if self.deform_modulated: offset_mask = self.conv2_offset(out) offset_x, offset_y, mask = torch.chunk(offset_mask, 3, dim=1) offset = torch.cat((offset_x, offset_y), dim=1) mask = mask.sigmoid() out = self.conv2(out, offset, mask) else: offset = self.conv2_offset(out) out = self.conv2(out, offset) out = F.relu_(out) out = self.conv3(out) if self.shortcut is not None: shortcut = self.shortcut(x) else: shortcut = x out += shortcut out = F.relu_(out) return out class BasicStem(CNNBlockBase): """ The standard ResNet stem (layers before the first residual block), with a conv, relu and max_pool. """ def __init__(self, in_channels=3, out_channels=64, norm="BN"): """ Args: norm (str or callable): norm after the first conv layer. See :func:`layers.get_norm` for supported format. """ super().__init__(in_channels, out_channels, 4) self.in_channels = in_channels self.conv1 = Conv2d( in_channels, out_channels, kernel_size=7, stride=2, padding=3, bias=False, norm=get_norm(norm, out_channels), ) weight_init.c2_msra_fill(self.conv1) def forward(self, x): x = self.conv1(x) x = F.relu_(x) x = F.max_pool2d(x, kernel_size=3, stride=2, padding=1) return x class ResNet(Backbone): """ Implement :paper:`ResNet`. """ def __init__(self, stem, stages, num_classes=None, out_features=None, freeze_at=0): """ Args: stem (nn.Module): a stem module stages (list[list[CNNBlockBase]]): several (typically 4) stages, each contains multiple :class:`CNNBlockBase`. num_classes (None or int): if None, will not perform classification. Otherwise, will create a linear layer. out_features (list[str]): name of the layers whose outputs should be returned in forward. Can be anything in "stem", "linear", or "res2" ... If None, will return the output of the last layer. freeze_at (int): The number of stages at the beginning to freeze. see :meth:`freeze` for detailed explanation. """ super().__init__() self.stem = stem self.num_classes = num_classes current_stride = self.stem.stride self._out_feature_strides = {"stem": current_stride} self._out_feature_channels = {"stem": self.stem.out_channels} self.stage_names, self.stages = [], [] if out_features is not None: # Avoid keeping unused layers in this module. They consume extra memory # and may cause allreduce to fail num_stages = max( [{"res2": 1, "res3": 2, "res4": 3, "res5": 4}.get(f, 0) for f in out_features] ) stages = stages[:num_stages] for i, blocks in enumerate(stages): assert len(blocks) > 0, len(blocks) for block in blocks: assert isinstance(block, CNNBlockBase), block name = "res" + str(i + 2) stage = nn.Sequential(*blocks) self.add_module(name, stage) self.stage_names.append(name) self.stages.append(stage) self._out_feature_strides[name] = current_stride = int( current_stride * np.prod([k.stride for k in blocks]) ) self._out_feature_channels[name] = curr_channels = blocks[-1].out_channels self.stage_names = tuple(self.stage_names) # Make it static for scripting if num_classes is not None: self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) self.linear = nn.Linear(curr_channels, num_classes) # Sec 5.1 in "Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour": # "The 1000-way fully-connected layer is initialized by # drawing weights from a zero-mean Gaussian with standard deviation of 0.01." nn.init.normal_(self.linear.weight, std=0.01) name = "linear" if out_features is None: out_features = [name] self._out_features = out_features assert len(self._out_features) children = [x[0] for x in self.named_children()] for out_feature in self._out_features: assert out_feature in children, "Available children: {}".format(", ".join(children)) self.freeze(freeze_at) def forward(self, x): """ Args: x: Tensor of shape (N,C,H,W). H, W must be a multiple of ``self.size_divisibility``. Returns: dict[str->Tensor]: names and the corresponding features """ assert x.dim() == 4, f"ResNet takes an input of shape (N, C, H, W). Got {x.shape} instead!" outputs = {} x = self.stem(x) if "stem" in self._out_features: outputs["stem"] = x for name, stage in zip(self.stage_names, self.stages): x = stage(x) if name in self._out_features: outputs[name] = x if self.num_classes is not None: x = self.avgpool(x) x = torch.flatten(x, 1) x = self.linear(x) if "linear" in self._out_features: outputs["linear"] = x return outputs def output_shape(self): return { name: ShapeSpec( channels=self._out_feature_channels[name], stride=self._out_feature_strides[name] ) for name in self._out_features } def freeze(self, freeze_at=0): """ Freeze the first several stages of the ResNet. Commonly used in fine-tuning. Layers that produce the same feature map spatial size are defined as one "stage" by :paper:`FPN`. Args: freeze_at (int): number of stages to freeze. `1` means freezing the stem. `2` means freezing the stem and one residual stage, etc. Returns: nn.Module: this ResNet itself """ if freeze_at >= 1: self.stem.freeze() for idx, stage in enumerate(self.stages, start=2): if freeze_at >= idx: for block in stage.children(): block.freeze() return self @staticmethod def make_stage(block_class, num_blocks, *, in_channels, out_channels, **kwargs): """ Create a list of blocks of the same type that forms one ResNet stage. Args: block_class (type): a subclass of CNNBlockBase that's used to create all blocks in this stage. A module of this type must not change spatial resolution of inputs unless its stride != 1. num_blocks (int): number of blocks in this stage in_channels (int): input channels of the entire stage. out_channels (int): output channels of **every block** in the stage. kwargs: other arguments passed to the constructor of `block_class`. If the argument name is "xx_per_block", the argument is a list of values to be passed to each block in the stage. Otherwise, the same argument is passed to every block in the stage. Returns: list[CNNBlockBase]: a list of block module. Examples: :: stage = ResNet.make_stage( BottleneckBlock, 3, in_channels=16, out_channels=64, bottleneck_channels=16, num_groups=1, stride_per_block=[2, 1, 1], dilations_per_block=[1, 1, 2] ) Usually, layers that produce the same feature map spatial size are defined as one "stage" (in :paper:`FPN`). Under such definition, ``stride_per_block[1:]`` should all be 1. """ blocks = [] for i in range(num_blocks): curr_kwargs = {} for k, v in kwargs.items(): if k.endswith("_per_block"): assert len(v) == num_blocks, ( f"Argument '{k}' of make_stage should have the " f"same length as num_blocks={num_blocks}." ) newk = k[: -len("_per_block")] assert newk not in kwargs, f"Cannot call make_stage with both {k} and {newk}!" curr_kwargs[newk] = v[i] else: curr_kwargs[k] = v blocks.append( block_class(in_channels=in_channels, out_channels=out_channels, **curr_kwargs) ) in_channels = out_channels return blocks @staticmethod def make_default_stages(depth, block_class=None, **kwargs): """ Created list of ResNet stages from pre-defined depth (one of 18, 34, 50, 101, 152). If it doesn't create the ResNet variant you need, please use :meth:`make_stage` instead for fine-grained customization. Args: depth (int): depth of ResNet block_class (type): the CNN block class. Has to accept `bottleneck_channels` argument for depth > 50. By default it is BasicBlock or BottleneckBlock, based on the depth. kwargs: other arguments to pass to `make_stage`. Should not contain stride and channels, as they are predefined for each depth. Returns: list[list[CNNBlockBase]]: modules in all stages; see arguments of :class:`ResNet.__init__`. """ num_blocks_per_stage = { 18: [2, 2, 2, 2], 34: [3, 4, 6, 3], 50: [3, 4, 6, 3], 101: [3, 4, 23, 3], 152: [3, 8, 36, 3], }[depth] if block_class is None: block_class = BasicBlock if depth < 50 else BottleneckBlock if depth < 50: in_channels = [64, 64, 128, 256] out_channels = [64, 128, 256, 512] else: in_channels = [64, 256, 512, 1024] out_channels = [256, 512, 1024, 2048] ret = [] for (n, s, i, o) in zip(num_blocks_per_stage, [1, 2, 2, 2], in_channels, out_channels): if depth >= 50: kwargs["bottleneck_channels"] = o // 4 ret.append( ResNet.make_stage( block_class=block_class, num_blocks=n, stride_per_block=[s] + [1] * (n - 1), in_channels=i, out_channels=o, **kwargs, ) ) return ret ResNetBlockBase = CNNBlockBase """ Alias for backward compatibiltiy. """ def make_stage(*args, **kwargs): """ Deprecated alias for backward compatibiltiy. """ return ResNet.make_stage(*args, **kwargs) @BACKBONE_REGISTRY.register() def build_resnet_backbone(cfg, input_shape): """ Create a ResNet instance from config. Returns: ResNet: a :class:`ResNet` instance. """ # need registration of new blocks/stems? norm = cfg.MODEL.RESNETS.NORM stem = BasicStem( in_channels=input_shape.channels, out_channels=cfg.MODEL.RESNETS.STEM_OUT_CHANNELS, norm=norm, ) # fmt: off freeze_at = cfg.MODEL.BACKBONE.FREEZE_AT out_features = cfg.MODEL.RESNETS.OUT_FEATURES depth = cfg.MODEL.RESNETS.DEPTH num_groups = cfg.MODEL.RESNETS.NUM_GROUPS width_per_group = cfg.MODEL.RESNETS.WIDTH_PER_GROUP bottleneck_channels = num_groups * width_per_group in_channels = cfg.MODEL.RESNETS.STEM_OUT_CHANNELS out_channels = cfg.MODEL.RESNETS.RES2_OUT_CHANNELS stride_in_1x1 = cfg.MODEL.RESNETS.STRIDE_IN_1X1 res5_dilation = cfg.MODEL.RESNETS.RES5_DILATION deform_on_per_stage = cfg.MODEL.RESNETS.DEFORM_ON_PER_STAGE deform_modulated = cfg.MODEL.RESNETS.DEFORM_MODULATED deform_num_groups = cfg.MODEL.RESNETS.DEFORM_NUM_GROUPS # fmt: on assert res5_dilation in {1, 2}, "res5_dilation cannot be {}.".format(res5_dilation) num_blocks_per_stage = { 18: [2, 2, 2, 2], 34: [3, 4, 6, 3], 50: [3, 4, 6, 3], 101: [3, 4, 23, 3], 152: [3, 8, 36, 3], }[depth] if depth in [18, 34]: assert out_channels == 64, "Must set MODEL.RESNETS.RES2_OUT_CHANNELS = 64 for R18/R34" assert not any( deform_on_per_stage ), "MODEL.RESNETS.DEFORM_ON_PER_STAGE unsupported for R18/R34" assert res5_dilation == 1, "Must set MODEL.RESNETS.RES5_DILATION = 1 for R18/R34" assert num_groups == 1, "Must set MODEL.RESNETS.NUM_GROUPS = 1 for R18/R34" stages = [] for idx, stage_idx in enumerate(range(2, 6)): # res5_dilation is used this way as a convention in R-FCN & Deformable Conv paper dilation = res5_dilation if stage_idx == 5 else 1 first_stride = 1 if idx == 0 or (stage_idx == 5 and dilation == 2) else 2 stage_kargs = { "num_blocks": num_blocks_per_stage[idx], "stride_per_block": [first_stride] + [1] * (num_blocks_per_stage[idx] - 1), "in_channels": in_channels, "out_channels": out_channels, "norm": norm, } # Use BasicBlock for R18 and R34. if depth in [18, 34]: stage_kargs["block_class"] = BasicBlock else: stage_kargs["bottleneck_channels"] = bottleneck_channels stage_kargs["stride_in_1x1"] = stride_in_1x1 stage_kargs["dilation"] = dilation stage_kargs["num_groups"] = num_groups if deform_on_per_stage[idx]: stage_kargs["block_class"] = DeformBottleneckBlock stage_kargs["deform_modulated"] = deform_modulated stage_kargs["deform_num_groups"] = deform_num_groups else: stage_kargs["block_class"] = BottleneckBlock blocks = ResNet.make_stage(**stage_kargs) in_channels = out_channels out_channels *= 2 bottleneck_channels *= 2 stages.append(blocks) return ResNet(stem, stages, out_features=out_features, freeze_at=freeze_at) ================================================ FILE: detectron2/detectron2/modeling/backbone/swin.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved """ Implementation of Swin models from :paper:`swin`. This code is adapted from https://github.com/SwinTransformer/Swin-Transformer-Object-Detection/blob/master/mmdet/models/backbones/swin_transformer.py with minimal modifications. # noqa -------------------------------------------------------- Swin Transformer Copyright (c) 2021 Microsoft Licensed under The MIT License [see LICENSE for details] Written by Ze Liu, Yutong Lin, Yixuan Wei -------------------------------------------------------- LICENSE: https://github.com/SwinTransformer/Swin-Transformer-Object-Detection/blob/461e003166a8083d0b620beacd4662a2df306bd6/LICENSE """ import numpy as np import torch import torch.nn as nn import torch.nn.functional as F import torch.utils.checkpoint as checkpoint from detectron2.modeling.backbone.backbone import Backbone from timm.models.layers import DropPath, to_2tuple, trunc_normal_ class Mlp(nn.Module): """Multilayer perceptron.""" def __init__( self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.0 ): super().__init__() out_features = out_features or in_features hidden_features = hidden_features or in_features self.fc1 = nn.Linear(in_features, hidden_features) self.act = act_layer() self.fc2 = nn.Linear(hidden_features, out_features) self.drop = nn.Dropout(drop) def forward(self, x): x = self.fc1(x) x = self.act(x) x = self.drop(x) x = self.fc2(x) x = self.drop(x) return x def window_partition(x, window_size): """ Args: x: (B, H, W, C) window_size (int): window size Returns: windows: (num_windows*B, window_size, window_size, C) """ B, H, W, C = x.shape x = x.view(B, H // window_size, window_size, W // window_size, window_size, C) windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C) return windows def window_reverse(windows, window_size, H, W): """ Args: windows: (num_windows*B, window_size, window_size, C) window_size (int): Window size H (int): Height of image W (int): Width of image Returns: x: (B, H, W, C) """ B = int(windows.shape[0] / (H * W / window_size / window_size)) x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1) x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1) return x class WindowAttention(nn.Module): """Window based multi-head self attention (W-MSA) module with relative position bias. It supports both of shifted and non-shifted window. Args: dim (int): Number of input channels. window_size (tuple[int]): The height and width of the window. num_heads (int): Number of attention heads. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set attn_drop (float, optional): Dropout ratio of attention weight. Default: 0.0 proj_drop (float, optional): Dropout ratio of output. Default: 0.0 """ def __init__( self, dim, window_size, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0.0, proj_drop=0.0, ): super().__init__() self.dim = dim self.window_size = window_size # Wh, Ww self.num_heads = num_heads head_dim = dim // num_heads self.scale = qk_scale or head_dim**-0.5 # define a parameter table of relative position bias self.relative_position_bias_table = nn.Parameter( torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads) ) # 2*Wh-1 * 2*Ww-1, nH # get pair-wise relative position index for each token inside the window coords_h = torch.arange(self.window_size[0]) coords_w = torch.arange(self.window_size[1]) coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2 relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0 relative_coords[:, :, 1] += self.window_size[1] - 1 relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1 relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww self.register_buffer("relative_position_index", relative_position_index) self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) self.attn_drop = nn.Dropout(attn_drop) self.proj = nn.Linear(dim, dim) self.proj_drop = nn.Dropout(proj_drop) trunc_normal_(self.relative_position_bias_table, std=0.02) self.softmax = nn.Softmax(dim=-1) def forward(self, x, mask=None): """Forward function. Args: x: input features with shape of (num_windows*B, N, C) mask: (0/-inf) mask with shape of (num_windows, Wh*Ww, Wh*Ww) or None """ B_, N, C = x.shape qkv = ( self.qkv(x) .reshape(B_, N, 3, self.num_heads, C // self.num_heads) .permute(2, 0, 3, 1, 4) ) q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) q = q * self.scale attn = q @ k.transpose(-2, -1) relative_position_bias = self.relative_position_bias_table[ self.relative_position_index.view(-1) ].view( self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1 ) # Wh*Ww,Wh*Ww,nH relative_position_bias = relative_position_bias.permute( 2, 0, 1 ).contiguous() # nH, Wh*Ww, Wh*Ww attn = attn + relative_position_bias.unsqueeze(0) if mask is not None: nW = mask.shape[0] attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0) attn = attn.view(-1, self.num_heads, N, N) attn = self.softmax(attn) else: attn = self.softmax(attn) attn = self.attn_drop(attn) x = (attn @ v).transpose(1, 2).reshape(B_, N, C) x = self.proj(x) x = self.proj_drop(x) return x class SwinTransformerBlock(nn.Module): """Swin Transformer Block. Args: dim (int): Number of input channels. num_heads (int): Number of attention heads. window_size (int): Window size. shift_size (int): Shift size for SW-MSA. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set. drop (float, optional): Dropout rate. Default: 0.0 attn_drop (float, optional): Attention dropout rate. Default: 0.0 drop_path (float, optional): Stochastic depth rate. Default: 0.0 act_layer (nn.Module, optional): Activation layer. Default: nn.GELU norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm """ def __init__( self, dim, num_heads, window_size=7, shift_size=0, mlp_ratio=4.0, qkv_bias=True, qk_scale=None, drop=0.0, attn_drop=0.0, drop_path=0.0, act_layer=nn.GELU, norm_layer=nn.LayerNorm, ): super().__init__() self.dim = dim self.num_heads = num_heads self.window_size = window_size self.shift_size = shift_size self.mlp_ratio = mlp_ratio assert 0 <= self.shift_size < self.window_size, "shift_size must in 0-window_size" self.norm1 = norm_layer(dim) self.attn = WindowAttention( dim, window_size=to_2tuple(self.window_size), num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop, ) self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity() self.norm2 = norm_layer(dim) mlp_hidden_dim = int(dim * mlp_ratio) self.mlp = Mlp( in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop ) self.H = None self.W = None def forward(self, x, mask_matrix): """Forward function. Args: x: Input feature, tensor size (B, H*W, C). H, W: Spatial resolution of the input feature. mask_matrix: Attention mask for cyclic shift. """ B, L, C = x.shape H, W = self.H, self.W assert L == H * W, "input feature has wrong size" shortcut = x x = self.norm1(x) x = x.view(B, H, W, C) # pad feature maps to multiples of window size pad_l = pad_t = 0 pad_r = (self.window_size - W % self.window_size) % self.window_size pad_b = (self.window_size - H % self.window_size) % self.window_size x = F.pad(x, (0, 0, pad_l, pad_r, pad_t, pad_b)) _, Hp, Wp, _ = x.shape # cyclic shift if self.shift_size > 0: shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2)) attn_mask = mask_matrix else: shifted_x = x attn_mask = None # partition windows x_windows = window_partition( shifted_x, self.window_size ) # nW*B, window_size, window_size, C x_windows = x_windows.view( -1, self.window_size * self.window_size, C ) # nW*B, window_size*window_size, C # W-MSA/SW-MSA attn_windows = self.attn(x_windows, mask=attn_mask) # nW*B, window_size*window_size, C # merge windows attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C) shifted_x = window_reverse(attn_windows, self.window_size, Hp, Wp) # B H' W' C # reverse cyclic shift if self.shift_size > 0: x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2)) else: x = shifted_x if pad_r > 0 or pad_b > 0: x = x[:, :H, :W, :].contiguous() x = x.view(B, H * W, C) # FFN x = shortcut + self.drop_path(x) x = x + self.drop_path(self.mlp(self.norm2(x))) return x class PatchMerging(nn.Module): """Patch Merging Layer Args: dim (int): Number of input channels. norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm """ def __init__(self, dim, norm_layer=nn.LayerNorm): super().__init__() self.dim = dim self.reduction = nn.Linear(4 * dim, 2 * dim, bias=False) self.norm = norm_layer(4 * dim) def forward(self, x, H, W): """Forward function. Args: x: Input feature, tensor size (B, H*W, C). H, W: Spatial resolution of the input feature. """ B, L, C = x.shape assert L == H * W, "input feature has wrong size" x = x.view(B, H, W, C) # padding pad_input = (H % 2 == 1) or (W % 2 == 1) if pad_input: x = F.pad(x, (0, 0, 0, W % 2, 0, H % 2)) x0 = x[:, 0::2, 0::2, :] # B H/2 W/2 C x1 = x[:, 1::2, 0::2, :] # B H/2 W/2 C x2 = x[:, 0::2, 1::2, :] # B H/2 W/2 C x3 = x[:, 1::2, 1::2, :] # B H/2 W/2 C x = torch.cat([x0, x1, x2, x3], -1) # B H/2 W/2 4*C x = x.view(B, -1, 4 * C) # B H/2*W/2 4*C x = self.norm(x) x = self.reduction(x) return x class BasicLayer(nn.Module): """A basic Swin Transformer layer for one stage. Args: dim (int): Number of feature channels depth (int): Depths of this stage. num_heads (int): Number of attention head. window_size (int): Local window size. Default: 7. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set. drop (float, optional): Dropout rate. Default: 0.0 attn_drop (float, optional): Attention dropout rate. Default: 0.0 drop_path (float | tuple[float], optional): Stochastic depth rate. Default: 0.0 norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm downsample (nn.Module | None, optional): Downsample layer at the end of the layer. Default: None use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False. """ def __init__( self, dim, depth, num_heads, window_size=7, mlp_ratio=4.0, qkv_bias=True, qk_scale=None, drop=0.0, attn_drop=0.0, drop_path=0.0, norm_layer=nn.LayerNorm, downsample=None, use_checkpoint=False, ): super().__init__() self.window_size = window_size self.shift_size = window_size // 2 self.depth = depth self.use_checkpoint = use_checkpoint # build blocks self.blocks = nn.ModuleList( [ SwinTransformerBlock( dim=dim, num_heads=num_heads, window_size=window_size, shift_size=0 if (i % 2 == 0) else window_size // 2, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, drop=drop, attn_drop=attn_drop, drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path, norm_layer=norm_layer, ) for i in range(depth) ] ) # patch merging layer if downsample is not None: self.downsample = downsample(dim=dim, norm_layer=norm_layer) else: self.downsample = None def forward(self, x, H, W): """Forward function. Args: x: Input feature, tensor size (B, H*W, C). H, W: Spatial resolution of the input feature. """ # calculate attention mask for SW-MSA Hp = int(np.ceil(H / self.window_size)) * self.window_size Wp = int(np.ceil(W / self.window_size)) * self.window_size img_mask = torch.zeros((1, Hp, Wp, 1), device=x.device) # 1 Hp Wp 1 h_slices = ( slice(0, -self.window_size), slice(-self.window_size, -self.shift_size), slice(-self.shift_size, None), ) w_slices = ( slice(0, -self.window_size), slice(-self.window_size, -self.shift_size), slice(-self.shift_size, None), ) cnt = 0 for h in h_slices: for w in w_slices: img_mask[:, h, w, :] = cnt cnt += 1 mask_windows = window_partition( img_mask, self.window_size ) # nW, window_size, window_size, 1 mask_windows = mask_windows.view(-1, self.window_size * self.window_size) attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2) attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill( attn_mask == 0, float(0.0) ) for blk in self.blocks: blk.H, blk.W = H, W if self.use_checkpoint: x = checkpoint.checkpoint(blk, x, attn_mask) else: x = blk(x, attn_mask) if self.downsample is not None: x_down = self.downsample(x, H, W) Wh, Ww = (H + 1) // 2, (W + 1) // 2 return x, H, W, x_down, Wh, Ww else: return x, H, W, x, H, W class PatchEmbed(nn.Module): """Image to Patch Embedding Args: patch_size (int): Patch token size. Default: 4. in_chans (int): Number of input image channels. Default: 3. embed_dim (int): Number of linear projection output channels. Default: 96. norm_layer (nn.Module, optional): Normalization layer. Default: None """ def __init__(self, patch_size=4, in_chans=3, embed_dim=96, norm_layer=None): super().__init__() patch_size = to_2tuple(patch_size) self.patch_size = patch_size self.in_chans = in_chans self.embed_dim = embed_dim self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size) if norm_layer is not None: self.norm = norm_layer(embed_dim) else: self.norm = None def forward(self, x): """Forward function.""" # padding _, _, H, W = x.size() if W % self.patch_size[1] != 0: x = F.pad(x, (0, self.patch_size[1] - W % self.patch_size[1])) if H % self.patch_size[0] != 0: x = F.pad(x, (0, 0, 0, self.patch_size[0] - H % self.patch_size[0])) x = self.proj(x) # B C Wh Ww if self.norm is not None: Wh, Ww = x.size(2), x.size(3) x = x.flatten(2).transpose(1, 2) x = self.norm(x) x = x.transpose(1, 2).view(-1, self.embed_dim, Wh, Ww) return x class SwinTransformer(Backbone): """Swin Transformer backbone. A PyTorch impl of : `Swin Transformer: Hierarchical Vision Transformer using Shifted Windows` - https://arxiv.org/pdf/2103.14030 Args: pretrain_img_size (int): Input image size for training the pretrained model, used in absolute postion embedding. Default 224. patch_size (int | tuple(int)): Patch size. Default: 4. in_chans (int): Number of input image channels. Default: 3. embed_dim (int): Number of linear projection output channels. Default: 96. depths (tuple[int]): Depths of each Swin Transformer stage. num_heads (tuple[int]): Number of attention head of each stage. window_size (int): Window size. Default: 7. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4. qkv_bias (bool): If True, add a learnable bias to query, key, value. Default: True qk_scale (float): Override default qk scale of head_dim ** -0.5 if set. drop_rate (float): Dropout rate. attn_drop_rate (float): Attention dropout rate. Default: 0. drop_path_rate (float): Stochastic depth rate. Default: 0.2. norm_layer (nn.Module): Normalization layer. Default: nn.LayerNorm. ape (bool): If True, add absolute position embedding to the patch embedding. Default: False. patch_norm (bool): If True, add normalization after patch embedding. Default: True. out_indices (Sequence[int]): Output from which stages. frozen_stages (int): Stages to be frozen (stop grad and set eval mode). -1 means not freezing any parameters. use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False. """ def __init__( self, pretrain_img_size=224, patch_size=4, in_chans=3, embed_dim=96, depths=(2, 2, 6, 2), num_heads=(3, 6, 12, 24), window_size=7, mlp_ratio=4.0, qkv_bias=True, qk_scale=None, drop_rate=0.0, attn_drop_rate=0.0, drop_path_rate=0.2, norm_layer=nn.LayerNorm, ape=False, patch_norm=True, out_indices=(0, 1, 2, 3), frozen_stages=-1, use_checkpoint=False, ): super().__init__() self.pretrain_img_size = pretrain_img_size self.num_layers = len(depths) self.embed_dim = embed_dim self.ape = ape self.patch_norm = patch_norm self.out_indices = out_indices self.frozen_stages = frozen_stages # split image into non-overlapping patches self.patch_embed = PatchEmbed( patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim, norm_layer=norm_layer if self.patch_norm else None, ) # absolute position embedding if self.ape: pretrain_img_size = to_2tuple(pretrain_img_size) patch_size = to_2tuple(patch_size) patches_resolution = [ pretrain_img_size[0] // patch_size[0], pretrain_img_size[1] // patch_size[1], ] self.absolute_pos_embed = nn.Parameter( torch.zeros(1, embed_dim, patches_resolution[0], patches_resolution[1]) ) trunc_normal_(self.absolute_pos_embed, std=0.02) self.pos_drop = nn.Dropout(p=drop_rate) # stochastic depth dpr = [ x.item() for x in torch.linspace(0, drop_path_rate, sum(depths)) ] # stochastic depth decay rule # build layers self.layers = nn.ModuleList() for i_layer in range(self.num_layers): layer = BasicLayer( dim=int(embed_dim * 2**i_layer), depth=depths[i_layer], num_heads=num_heads[i_layer], window_size=window_size, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[sum(depths[:i_layer]) : sum(depths[: i_layer + 1])], norm_layer=norm_layer, downsample=PatchMerging if (i_layer < self.num_layers - 1) else None, use_checkpoint=use_checkpoint, ) self.layers.append(layer) num_features = [int(embed_dim * 2**i) for i in range(self.num_layers)] self.num_features = num_features # add a norm layer for each output for i_layer in out_indices: layer = norm_layer(num_features[i_layer]) layer_name = f"norm{i_layer}" self.add_module(layer_name, layer) self._freeze_stages() self._out_features = ["p{}".format(i) for i in self.out_indices] self._out_feature_channels = { "p{}".format(i): self.embed_dim * 2**i for i in self.out_indices } self._out_feature_strides = {"p{}".format(i): 2 ** (i + 2) for i in self.out_indices} self._size_devisibility = 32 self.apply(self._init_weights) def _freeze_stages(self): if self.frozen_stages >= 0: self.patch_embed.eval() for param in self.patch_embed.parameters(): param.requires_grad = False if self.frozen_stages >= 1 and self.ape: self.absolute_pos_embed.requires_grad = False if self.frozen_stages >= 2: self.pos_drop.eval() for i in range(0, self.frozen_stages - 1): m = self.layers[i] m.eval() for param in m.parameters(): param.requires_grad = False def _init_weights(self, m): if isinstance(m, nn.Linear): trunc_normal_(m.weight, std=0.02) if isinstance(m, nn.Linear) and m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.LayerNorm): nn.init.constant_(m.bias, 0) nn.init.constant_(m.weight, 1.0) @property def size_divisibility(self): return self._size_divisibility def forward(self, x): """Forward function.""" x = self.patch_embed(x) Wh, Ww = x.size(2), x.size(3) if self.ape: # interpolate the position embedding to the corresponding size absolute_pos_embed = F.interpolate( self.absolute_pos_embed, size=(Wh, Ww), mode="bicubic" ) x = (x + absolute_pos_embed).flatten(2).transpose(1, 2) # B Wh*Ww C else: x = x.flatten(2).transpose(1, 2) x = self.pos_drop(x) outs = {} for i in range(self.num_layers): layer = self.layers[i] x_out, H, W, x, Wh, Ww = layer(x, Wh, Ww) if i in self.out_indices: norm_layer = getattr(self, f"norm{i}") x_out = norm_layer(x_out) out = x_out.view(-1, H, W, self.num_features[i]).permute(0, 3, 1, 2).contiguous() outs["p{}".format(i)] = out return outs ================================================ FILE: detectron2/detectron2/modeling/backbone/utils.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import math import torch import torch.nn as nn import torch.nn.functional as F __all__ = [ "window_partition", "window_unpartition", "add_decomposed_rel_pos", "get_abs_pos", "PatchEmbed", ] def window_partition(x, window_size): """ Partition into non-overlapping windows with padding if needed. Args: x (tensor): input tokens with [B, H, W, C]. window_size (int): window size. Returns: windows: windows after partition with [B * num_windows, window_size, window_size, C]. (Hp, Wp): padded height and width before partition """ B, H, W, C = x.shape pad_h = (window_size - H % window_size) % window_size pad_w = (window_size - W % window_size) % window_size if pad_h > 0 or pad_w > 0: x = F.pad(x, (0, 0, 0, pad_w, 0, pad_h)) Hp, Wp = H + pad_h, W + pad_w x = x.view(B, Hp // window_size, window_size, Wp // window_size, window_size, C) windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C) return windows, (Hp, Wp) def window_unpartition(windows, window_size, pad_hw, hw): """ Window unpartition into original sequences and removing padding. Args: x (tensor): input tokens with [B * num_windows, window_size, window_size, C]. window_size (int): window size. pad_hw (Tuple): padded height and width (Hp, Wp). hw (Tuple): original height and width (H, W) before padding. Returns: x: unpartitioned sequences with [B, H, W, C]. """ Hp, Wp = pad_hw H, W = hw B = windows.shape[0] // (Hp * Wp // window_size // window_size) x = windows.view(B, Hp // window_size, Wp // window_size, window_size, window_size, -1) x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, Hp, Wp, -1) if Hp > H or Wp > W: x = x[:, :H, :W, :].contiguous() return x def get_rel_pos(q_size, k_size, rel_pos): """ Get relative positional embeddings according to the relative positions of query and key sizes. Args: q_size (int): size of query q. k_size (int): size of key k. rel_pos (Tensor): relative position embeddings (L, C). Returns: Extracted positional embeddings according to relative positions. """ max_rel_dist = int(2 * max(q_size, k_size) - 1) # Interpolate rel pos if needed. if rel_pos.shape[0] != max_rel_dist: # Interpolate rel pos. rel_pos_resized = F.interpolate( rel_pos.reshape(1, rel_pos.shape[0], -1).permute(0, 2, 1), size=max_rel_dist, mode="linear", ) rel_pos_resized = rel_pos_resized.reshape(-1, max_rel_dist).permute(1, 0) else: rel_pos_resized = rel_pos # Scale the coords with short length if shapes for q and k are different. q_coords = torch.arange(q_size)[:, None] * max(k_size / q_size, 1.0) k_coords = torch.arange(k_size)[None, :] * max(q_size / k_size, 1.0) relative_coords = (q_coords - k_coords) + (k_size - 1) * max(q_size / k_size, 1.0) return rel_pos_resized[relative_coords.long()] def add_decomposed_rel_pos(attn, q, rel_pos_h, rel_pos_w, q_size, k_size): """ Calculate decomposed Relative Positional Embeddings from :paper:`mvitv2`. https://github.com/facebookresearch/mvit/blob/19786631e330df9f3622e5402b4a419a263a2c80/mvit/models/attention.py # noqa B950 Args: attn (Tensor): attention map. q (Tensor): query q in the attention layer with shape (B, q_h * q_w, C). rel_pos_h (Tensor): relative position embeddings (Lh, C) for height axis. rel_pos_w (Tensor): relative position embeddings (Lw, C) for width axis. q_size (Tuple): spatial sequence size of query q with (q_h, q_w). k_size (Tuple): spatial sequence size of key k with (k_h, k_w). Returns: attn (Tensor): attention map with added relative positional embeddings. """ q_h, q_w = q_size k_h, k_w = k_size Rh = get_rel_pos(q_h, k_h, rel_pos_h) Rw = get_rel_pos(q_w, k_w, rel_pos_w) B, _, dim = q.shape r_q = q.reshape(B, q_h, q_w, dim) rel_h = torch.einsum("bhwc,hkc->bhwk", r_q, Rh) rel_w = torch.einsum("bhwc,wkc->bhwk", r_q, Rw) attn = ( attn.view(B, q_h, q_w, k_h, k_w) + rel_h[:, :, :, :, None] + rel_w[:, :, :, None, :] ).view(B, q_h * q_w, k_h * k_w) return attn def get_abs_pos(abs_pos, has_cls_token, hw): """ Calculate absolute positional embeddings. If needed, resize embeddings and remove cls_token dimension for the original embeddings. Args: abs_pos (Tensor): absolute positional embeddings with (1, num_position, C). has_cls_token (bool): If true, has 1 embedding in abs_pos for cls token. hw (Tuple): size of input image tokens. Returns: Absolute positional embeddings after processing with shape (1, H, W, C) """ h, w = hw if has_cls_token: abs_pos = abs_pos[:, 1:] xy_num = abs_pos.shape[1] size = int(math.sqrt(xy_num)) assert size * size == xy_num if size != h or size != w: new_abs_pos = F.interpolate( abs_pos.reshape(1, size, size, -1).permute(0, 3, 1, 2), size=(h, w), mode="bicubic", align_corners=False, ) return new_abs_pos.permute(0, 2, 3, 1) else: return abs_pos.reshape(1, h, w, -1) class PatchEmbed(nn.Module): """ Image to Patch Embedding. """ def __init__( self, kernel_size=(16, 16), stride=(16, 16), padding=(0, 0), in_chans=3, embed_dim=768 ): """ Args: kernel_size (Tuple): kernel size of the projection layer. stride (Tuple): stride of the projection layer. padding (Tuple): padding size of the projection layer. in_chans (int): Number of input image channels. embed_dim (int): embed_dim (int): Patch embedding dimension. """ super().__init__() self.proj = nn.Conv2d( in_chans, embed_dim, kernel_size=kernel_size, stride=stride, padding=padding ) def forward(self, x): x = self.proj(x) # B C H W -> B H W C x = x.permute(0, 2, 3, 1) return x ================================================ FILE: detectron2/detectron2/modeling/backbone/vit.py ================================================ import logging import math import fvcore.nn.weight_init as weight_init import torch import torch.nn as nn from detectron2.layers import CNNBlockBase, Conv2d, get_norm from detectron2.modeling.backbone.fpn import _assert_strides_are_log2_contiguous from fairscale.nn.checkpoint import checkpoint_wrapper from timm.models.layers import DropPath, Mlp, trunc_normal_ from .backbone import Backbone from .utils import ( PatchEmbed, add_decomposed_rel_pos, get_abs_pos, window_partition, window_unpartition, ) logger = logging.getLogger(__name__) __all__ = ["ViT", "SimpleFeaturePyramid", "get_vit_lr_decay_rate"] class Attention(nn.Module): """Multi-head Attention block with relative position embeddings.""" def __init__( self, dim, num_heads=8, qkv_bias=True, use_rel_pos=False, rel_pos_zero_init=True, input_size=None, ): """ Args: dim (int): Number of input channels. num_heads (int): Number of attention heads. qkv_bias (bool: If True, add a learnable bias to query, key, value. rel_pos (bool): If True, add relative positional embeddings to the attention map. rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. input_size (int or None): Input resolution for calculating the relative positional parameter size. """ super().__init__() self.num_heads = num_heads head_dim = dim // num_heads self.scale = head_dim**-0.5 self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) self.proj = nn.Linear(dim, dim) self.use_rel_pos = use_rel_pos if self.use_rel_pos: # initialize relative positional embeddings self.rel_pos_h = nn.Parameter(torch.zeros(2 * input_size[0] - 1, head_dim)) self.rel_pos_w = nn.Parameter(torch.zeros(2 * input_size[1] - 1, head_dim)) if not rel_pos_zero_init: trunc_normal_(self.rel_pos_h, std=0.02) trunc_normal_(self.rel_pos_w, std=0.02) def forward(self, x): B, H, W, _ = x.shape # qkv with shape (3, B, nHead, H * W, C) qkv = self.qkv(x).reshape(B, H * W, 3, self.num_heads, -1).permute(2, 0, 3, 1, 4) # q, k, v with shape (B * nHead, H * W, C) q, k, v = qkv.reshape(3, B * self.num_heads, H * W, -1).unbind(0) attn = (q * self.scale) @ k.transpose(-2, -1) if self.use_rel_pos: attn = add_decomposed_rel_pos(attn, q, self.rel_pos_h, self.rel_pos_w, (H, W), (H, W)) attn = attn.softmax(dim=-1) x = (attn @ v).view(B, self.num_heads, H, W, -1).permute(0, 2, 3, 1, 4).reshape(B, H, W, -1) x = self.proj(x) return x class ResBottleneckBlock(CNNBlockBase): """ The standard bottleneck residual block without the last activation layer. It contains 3 conv layers with kernels 1x1, 3x3, 1x1. """ def __init__( self, in_channels, out_channels, bottleneck_channels, norm="LN", act_layer=nn.GELU, ): """ Args: in_channels (int): Number of input channels. out_channels (int): Number of output channels. bottleneck_channels (int): number of output channels for the 3x3 "bottleneck" conv layers. norm (str or callable): normalization for all conv layers. See :func:`layers.get_norm` for supported format. act_layer (callable): activation for all conv layers. """ super().__init__(in_channels, out_channels, 1) self.conv1 = Conv2d(in_channels, bottleneck_channels, 1, bias=False) self.norm1 = get_norm(norm, bottleneck_channels) self.act1 = act_layer() self.conv2 = Conv2d( bottleneck_channels, bottleneck_channels, 3, padding=1, bias=False, ) self.norm2 = get_norm(norm, bottleneck_channels) self.act2 = act_layer() self.conv3 = Conv2d(bottleneck_channels, out_channels, 1, bias=False) self.norm3 = get_norm(norm, out_channels) for layer in [self.conv1, self.conv2, self.conv3]: weight_init.c2_msra_fill(layer) for layer in [self.norm1, self.norm2]: layer.weight.data.fill_(1.0) layer.bias.data.zero_() # zero init last norm layer. self.norm3.weight.data.zero_() self.norm3.bias.data.zero_() def forward(self, x): out = x for layer in self.children(): out = layer(out) out = x + out return out class Block(nn.Module): """Transformer blocks with support of window attention and residual propagation blocks""" def __init__( self, dim, num_heads, mlp_ratio=4.0, qkv_bias=True, drop_path=0.0, norm_layer=nn.LayerNorm, act_layer=nn.GELU, use_rel_pos=False, rel_pos_zero_init=True, window_size=0, use_residual_block=False, input_size=None, ): """ Args: dim (int): Number of input channels. num_heads (int): Number of attention heads in each ViT block. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. qkv_bias (bool): If True, add a learnable bias to query, key, value. drop_path (float): Stochastic depth rate. norm_layer (nn.Module): Normalization layer. act_layer (nn.Module): Activation layer. use_rel_pos (bool): If True, add relative positional embeddings to the attention map. rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. window_size (int): Window size for window attention blocks. If it equals 0, then not use window attention. use_residual_block (bool): If True, use a residual block after the MLP block. input_size (int or None): Input resolution for calculating the relative positional parameter size. """ super().__init__() self.norm1 = norm_layer(dim) self.attn = Attention( dim, num_heads=num_heads, qkv_bias=qkv_bias, use_rel_pos=use_rel_pos, rel_pos_zero_init=rel_pos_zero_init, input_size=input_size if window_size == 0 else (window_size, window_size), ) self.drop_path = DropPath(drop_path) if drop_path > 0.0 else nn.Identity() self.norm2 = norm_layer(dim) self.mlp = Mlp(in_features=dim, hidden_features=int(dim * mlp_ratio), act_layer=act_layer) self.window_size = window_size self.use_residual_block = use_residual_block if use_residual_block: # Use a residual block with bottleneck channel as dim // 2 self.residual = ResBottleneckBlock( in_channels=dim, out_channels=dim, bottleneck_channels=dim // 2, norm="LN", act_layer=act_layer, ) def forward(self, x): shortcut = x x = self.norm1(x) # Window partition if self.window_size > 0: H, W = x.shape[1], x.shape[2] x, pad_hw = window_partition(x, self.window_size) x = self.attn(x) # Reverse window partition if self.window_size > 0: x = window_unpartition(x, self.window_size, pad_hw, (H, W)) x = shortcut + self.drop_path(x) x = x + self.drop_path(self.mlp(self.norm2(x))) if self.use_residual_block: x = self.residual(x.permute(0, 3, 1, 2)).permute(0, 2, 3, 1) return x class ViT(Backbone): """ This module implements Vision Transformer (ViT) backbone in :paper:`vitdet`. "Exploring Plain Vision Transformer Backbones for Object Detection", https://arxiv.org/abs/2203.16527 """ def __init__( self, img_size=1024, patch_size=16, in_chans=3, embed_dim=768, depth=12, num_heads=12, mlp_ratio=4.0, qkv_bias=True, drop_path_rate=0.0, norm_layer=nn.LayerNorm, act_layer=nn.GELU, use_abs_pos=True, use_rel_pos=False, rel_pos_zero_init=True, window_size=0, window_block_indexes=(), residual_block_indexes=(), use_act_checkpoint=False, pretrain_img_size=224, pretrain_use_cls_token=True, out_feature="last_feat", ): """ Args: img_size (int): Input image size. patch_size (int): Patch size. in_chans (int): Number of input image channels. embed_dim (int): Patch embedding dimension. depth (int): Depth of ViT. num_heads (int): Number of attention heads in each ViT block. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. qkv_bias (bool): If True, add a learnable bias to query, key, value. drop_path_rate (float): Stochastic depth rate. norm_layer (nn.Module): Normalization layer. act_layer (nn.Module): Activation layer. use_abs_pos (bool): If True, use absolute positional embeddings. use_rel_pos (bool): If True, add relative positional embeddings to the attention map. rel_pos_zero_init (bool): If True, zero initialize relative positional parameters. window_size (int): Window size for window attention blocks. window_block_indexes (list): Indexes for blocks using window attention. residual_block_indexes (list): Indexes for blocks using conv propagation. use_act_checkpoint (bool): If True, use activation checkpointing. pretrain_img_size (int): input image size for pretraining models. pretrain_use_cls_token (bool): If True, pretrainig models use class token. out_feature (str): name of the feature from the last block. """ super().__init__() self.pretrain_use_cls_token = pretrain_use_cls_token self.patch_embed = PatchEmbed( kernel_size=(patch_size, patch_size), stride=(patch_size, patch_size), in_chans=in_chans, embed_dim=embed_dim, ) if use_abs_pos: # Initialize absolute positional embedding with pretrain image size. num_patches = (pretrain_img_size // patch_size) * (pretrain_img_size // patch_size) num_positions = (num_patches + 1) if pretrain_use_cls_token else num_patches self.pos_embed = nn.Parameter(torch.zeros(1, num_positions, embed_dim)) else: self.pos_embed = None # stochastic depth decay rule dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] self.blocks = nn.ModuleList() for i in range(depth): block = Block( dim=embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, drop_path=dpr[i], norm_layer=norm_layer, act_layer=act_layer, use_rel_pos=use_rel_pos, rel_pos_zero_init=rel_pos_zero_init, window_size=window_size if i in window_block_indexes else 0, use_residual_block=i in residual_block_indexes, input_size=(img_size // patch_size, img_size // patch_size), ) if use_act_checkpoint: block = checkpoint_wrapper(block) self.blocks.append(block) self._out_feature_channels = {out_feature: embed_dim} self._out_feature_strides = {out_feature: patch_size} self._out_features = [out_feature] if self.pos_embed is not None: trunc_normal_(self.pos_embed, std=0.02) self.apply(self._init_weights) def _init_weights(self, m): if isinstance(m, nn.Linear): trunc_normal_(m.weight, std=0.02) if isinstance(m, nn.Linear) and m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.LayerNorm): nn.init.constant_(m.bias, 0) nn.init.constant_(m.weight, 1.0) def forward(self, x): x = self.patch_embed(x) if self.pos_embed is not None: x = x + get_abs_pos( self.pos_embed, self.pretrain_use_cls_token, (x.shape[1], x.shape[2]) ) for blk in self.blocks: x = blk(x) outputs = {self._out_features[0]: x.permute(0, 3, 1, 2)} return outputs class SimpleFeaturePyramid(Backbone): """ This module implements SimpleFeaturePyramid in :paper:`vitdet`. It creates pyramid features built on top of the input feature map. """ def __init__( self, net, in_feature, out_channels, scale_factors, top_block=None, norm="LN", square_pad=0, ): """ Args: net (Backbone): module representing the subnetwork backbone. Must be a subclass of :class:`Backbone`. in_feature (str): names of the input feature maps coming from the net. out_channels (int): number of channels in the output feature maps. scale_factors (list[float]): list of scaling factors to upsample or downsample the input features for creating pyramid features. top_block (nn.Module or None): if provided, an extra operation will be performed on the output of the last (smallest resolution) pyramid output, and the result will extend the result list. The top_block further downsamples the feature map. It must have an attribute "num_levels", meaning the number of extra pyramid levels added by this block, and "in_feature", which is a string representing its input feature (e.g., p5). norm (str): the normalization to use. square_pad (int): If > 0, require input images to be padded to specific square size. """ super(SimpleFeaturePyramid, self).__init__() assert isinstance(net, Backbone) self.scale_factors = scale_factors input_shapes = net.output_shape() strides = [int(input_shapes[in_feature].stride / scale) for scale in scale_factors] _assert_strides_are_log2_contiguous(strides) dim = input_shapes[in_feature].channels self.stages = [] use_bias = norm == "" for idx, scale in enumerate(scale_factors): out_dim = dim if scale == 4.0: layers = [ nn.ConvTranspose2d(dim, dim // 2, kernel_size=2, stride=2), get_norm(norm, dim // 2), nn.GELU(), nn.ConvTranspose2d(dim // 2, dim // 4, kernel_size=2, stride=2), ] out_dim = dim // 4 elif scale == 2.0: layers = [nn.ConvTranspose2d(dim, dim // 2, kernel_size=2, stride=2)] out_dim = dim // 2 elif scale == 1.0: layers = [] elif scale == 0.5: layers = [nn.MaxPool2d(kernel_size=2, stride=2)] else: raise NotImplementedError(f"scale_factor={scale} is not supported yet.") layers.extend( [ Conv2d( out_dim, out_channels, kernel_size=1, bias=use_bias, norm=get_norm(norm, out_channels), ), Conv2d( out_channels, out_channels, kernel_size=3, padding=1, bias=use_bias, norm=get_norm(norm, out_channels), ), ] ) layers = nn.Sequential(*layers) stage = int(math.log2(strides[idx])) self.add_module(f"simfp_{stage}", layers) self.stages.append(layers) self.net = net self.in_feature = in_feature self.top_block = top_block # Return feature names are "p", like ["p2", "p3", ..., "p6"] self._out_feature_strides = {"p{}".format(int(math.log2(s))): s for s in strides} # top block output feature maps. if self.top_block is not None: for s in range(stage, stage + self.top_block.num_levels): self._out_feature_strides["p{}".format(s + 1)] = 2 ** (s + 1) self._out_features = list(self._out_feature_strides.keys()) self._out_feature_channels = {k: out_channels for k in self._out_features} self._size_divisibility = strides[-1] self._square_pad = square_pad @property def padding_constraints(self): return { "size_divisiblity": self._size_divisibility, "square_size": self._square_pad, } def forward(self, x): """ Args: x: Tensor of shape (N,C,H,W). H, W must be a multiple of ``self.size_divisibility``. Returns: dict[str->Tensor]: mapping from feature map name to pyramid feature map tensor in high to low resolution order. Returned feature names follow the FPN convention: "p", where stage has stride = 2 ** stage e.g., ["p2", "p3", ..., "p6"]. """ bottom_up_features = self.net(x) features = bottom_up_features[self.in_feature] results = [] for stage in self.stages: results.append(stage(features)) if self.top_block is not None: if self.top_block.in_feature in bottom_up_features: top_block_in_feature = bottom_up_features[self.top_block.in_feature] else: top_block_in_feature = results[self._out_features.index(self.top_block.in_feature)] results.extend(self.top_block(top_block_in_feature)) assert len(self._out_features) == len(results) return {f: res for f, res in zip(self._out_features, results)} def get_vit_lr_decay_rate(name, lr_decay_rate=1.0, num_layers=12): """ Calculate lr decay rate for different ViT blocks. Args: name (string): parameter name. lr_decay_rate (float): base lr decay rate. num_layers (int): number of ViT blocks. Returns: lr decay rate for the given parameter. """ layer_id = num_layers + 1 if name.startswith("backbone"): if ".pos_embed" in name or ".patch_embed" in name: layer_id = 0 elif ".blocks." in name and ".residual." not in name: layer_id = int(name[name.find(".blocks.") :].split(".")[2]) + 1 return lr_decay_rate ** (num_layers + 1 - layer_id) ================================================ FILE: detectron2/detectron2/modeling/box_regression.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import math from typing import List, Tuple, Union import torch from fvcore.nn import giou_loss, smooth_l1_loss from torch.nn import functional as F from detectron2.layers import cat, ciou_loss, diou_loss from detectron2.structures import Boxes # Value for clamping large dw and dh predictions. The heuristic is that we clamp # such that dw and dh are no larger than what would transform a 16px box into a # 1000px box (based on a small anchor, 16px, and a typical image size, 1000px). _DEFAULT_SCALE_CLAMP = math.log(1000.0 / 16) __all__ = ["Box2BoxTransform", "Box2BoxTransformRotated", "Box2BoxTransformLinear"] @torch.jit.script class Box2BoxTransform(object): """ The box-to-box transform defined in R-CNN. The transformation is parameterized by 4 deltas: (dx, dy, dw, dh). The transformation scales the box's width and height by exp(dw), exp(dh) and shifts a box's center by the offset (dx * width, dy * height). """ def __init__( self, weights: Tuple[float, float, float, float], scale_clamp: float = _DEFAULT_SCALE_CLAMP ): """ Args: weights (4-element tuple): Scaling factors that are applied to the (dx, dy, dw, dh) deltas. In Fast R-CNN, these were originally set such that the deltas have unit variance; now they are treated as hyperparameters of the system. scale_clamp (float): When predicting deltas, the predicted box scaling factors (dw and dh) are clamped such that they are <= scale_clamp. """ self.weights = weights self.scale_clamp = scale_clamp def get_deltas(self, src_boxes, target_boxes): """ Get box regression transformation deltas (dx, dy, dw, dh) that can be used to transform the `src_boxes` into the `target_boxes`. That is, the relation ``target_boxes == self.apply_deltas(deltas, src_boxes)`` is true (unless any delta is too large and is clamped). Args: src_boxes (Tensor): source boxes, e.g., object proposals target_boxes (Tensor): target of the transformation, e.g., ground-truth boxes. """ assert isinstance(src_boxes, torch.Tensor), type(src_boxes) assert isinstance(target_boxes, torch.Tensor), type(target_boxes) src_widths = src_boxes[:, 2] - src_boxes[:, 0] src_heights = src_boxes[:, 3] - src_boxes[:, 1] src_ctr_x = src_boxes[:, 0] + 0.5 * src_widths src_ctr_y = src_boxes[:, 1] + 0.5 * src_heights target_widths = target_boxes[:, 2] - target_boxes[:, 0] target_heights = target_boxes[:, 3] - target_boxes[:, 1] target_ctr_x = target_boxes[:, 0] + 0.5 * target_widths target_ctr_y = target_boxes[:, 1] + 0.5 * target_heights wx, wy, ww, wh = self.weights dx = wx * (target_ctr_x - src_ctr_x) / src_widths dy = wy * (target_ctr_y - src_ctr_y) / src_heights dw = ww * torch.log(target_widths / src_widths) dh = wh * torch.log(target_heights / src_heights) deltas = torch.stack((dx, dy, dw, dh), dim=1) assert (src_widths > 0).all().item(), "Input boxes to Box2BoxTransform are not valid!" return deltas def apply_deltas(self, deltas, boxes): """ Apply transformation `deltas` (dx, dy, dw, dh) to `boxes`. Args: deltas (Tensor): transformation deltas of shape (N, k*4), where k >= 1. deltas[i] represents k potentially different class-specific box transformations for the single box boxes[i]. boxes (Tensor): boxes to transform, of shape (N, 4) """ deltas = deltas.float() # ensure fp32 for decoding precision boxes = boxes.to(deltas.dtype) widths = boxes[:, 2] - boxes[:, 0] heights = boxes[:, 3] - boxes[:, 1] ctr_x = boxes[:, 0] + 0.5 * widths ctr_y = boxes[:, 1] + 0.5 * heights wx, wy, ww, wh = self.weights dx = deltas[:, 0::4] / wx dy = deltas[:, 1::4] / wy dw = deltas[:, 2::4] / ww dh = deltas[:, 3::4] / wh # Prevent sending too large values into torch.exp() dw = torch.clamp(dw, max=self.scale_clamp) dh = torch.clamp(dh, max=self.scale_clamp) pred_ctr_x = dx * widths[:, None] + ctr_x[:, None] pred_ctr_y = dy * heights[:, None] + ctr_y[:, None] pred_w = torch.exp(dw) * widths[:, None] pred_h = torch.exp(dh) * heights[:, None] x1 = pred_ctr_x - 0.5 * pred_w y1 = pred_ctr_y - 0.5 * pred_h x2 = pred_ctr_x + 0.5 * pred_w y2 = pred_ctr_y + 0.5 * pred_h pred_boxes = torch.stack((x1, y1, x2, y2), dim=-1) return pred_boxes.reshape(deltas.shape) @torch.jit.script class Box2BoxTransformRotated(object): """ The box-to-box transform defined in Rotated R-CNN. The transformation is parameterized by 5 deltas: (dx, dy, dw, dh, da). The transformation scales the box's width and height by exp(dw), exp(dh), shifts a box's center by the offset (dx * width, dy * height), and rotate a box's angle by da (radians). Note: angles of deltas are in radians while angles of boxes are in degrees. """ def __init__( self, weights: Tuple[float, float, float, float, float], scale_clamp: float = _DEFAULT_SCALE_CLAMP, ): """ Args: weights (5-element tuple): Scaling factors that are applied to the (dx, dy, dw, dh, da) deltas. These are treated as hyperparameters of the system. scale_clamp (float): When predicting deltas, the predicted box scaling factors (dw and dh) are clamped such that they are <= scale_clamp. """ self.weights = weights self.scale_clamp = scale_clamp def get_deltas(self, src_boxes, target_boxes): """ Get box regression transformation deltas (dx, dy, dw, dh, da) that can be used to transform the `src_boxes` into the `target_boxes`. That is, the relation ``target_boxes == self.apply_deltas(deltas, src_boxes)`` is true (unless any delta is too large and is clamped). Args: src_boxes (Tensor): Nx5 source boxes, e.g., object proposals target_boxes (Tensor): Nx5 target of the transformation, e.g., ground-truth boxes. """ assert isinstance(src_boxes, torch.Tensor), type(src_boxes) assert isinstance(target_boxes, torch.Tensor), type(target_boxes) src_ctr_x, src_ctr_y, src_widths, src_heights, src_angles = torch.unbind(src_boxes, dim=1) target_ctr_x, target_ctr_y, target_widths, target_heights, target_angles = torch.unbind( target_boxes, dim=1 ) wx, wy, ww, wh, wa = self.weights dx = wx * (target_ctr_x - src_ctr_x) / src_widths dy = wy * (target_ctr_y - src_ctr_y) / src_heights dw = ww * torch.log(target_widths / src_widths) dh = wh * torch.log(target_heights / src_heights) # Angles of deltas are in radians while angles of boxes are in degrees. # the conversion to radians serve as a way to normalize the values da = target_angles - src_angles da = (da + 180.0) % 360.0 - 180.0 # make it in [-180, 180) da *= wa * math.pi / 180.0 deltas = torch.stack((dx, dy, dw, dh, da), dim=1) assert ( (src_widths > 0).all().item() ), "Input boxes to Box2BoxTransformRotated are not valid!" return deltas def apply_deltas(self, deltas, boxes): """ Apply transformation `deltas` (dx, dy, dw, dh, da) to `boxes`. Args: deltas (Tensor): transformation deltas of shape (N, k*5). deltas[i] represents box transformation for the single box boxes[i]. boxes (Tensor): boxes to transform, of shape (N, 5) """ assert deltas.shape[1] % 5 == 0 and boxes.shape[1] == 5 boxes = boxes.to(deltas.dtype).unsqueeze(2) ctr_x = boxes[:, 0] ctr_y = boxes[:, 1] widths = boxes[:, 2] heights = boxes[:, 3] angles = boxes[:, 4] wx, wy, ww, wh, wa = self.weights dx = deltas[:, 0::5] / wx dy = deltas[:, 1::5] / wy dw = deltas[:, 2::5] / ww dh = deltas[:, 3::5] / wh da = deltas[:, 4::5] / wa # Prevent sending too large values into torch.exp() dw = torch.clamp(dw, max=self.scale_clamp) dh = torch.clamp(dh, max=self.scale_clamp) pred_boxes = torch.zeros_like(deltas) pred_boxes[:, 0::5] = dx * widths + ctr_x # x_ctr pred_boxes[:, 1::5] = dy * heights + ctr_y # y_ctr pred_boxes[:, 2::5] = torch.exp(dw) * widths # width pred_boxes[:, 3::5] = torch.exp(dh) * heights # height # Following original RRPN implementation, # angles of deltas are in radians while angles of boxes are in degrees. pred_angle = da * 180.0 / math.pi + angles pred_angle = (pred_angle + 180.0) % 360.0 - 180.0 # make it in [-180, 180) pred_boxes[:, 4::5] = pred_angle return pred_boxes class Box2BoxTransformLinear(object): """ The linear box-to-box transform defined in FCOS. The transformation is parameterized by the distance from the center of (square) src box to 4 edges of the target box. """ def __init__(self, normalize_by_size=True): """ Args: normalize_by_size: normalize deltas by the size of src (anchor) boxes. """ self.normalize_by_size = normalize_by_size def get_deltas(self, src_boxes, target_boxes): """ Get box regression transformation deltas (dx1, dy1, dx2, dy2) that can be used to transform the `src_boxes` into the `target_boxes`. That is, the relation ``target_boxes == self.apply_deltas(deltas, src_boxes)`` is true. The center of src must be inside target boxes. Args: src_boxes (Tensor): square source boxes, e.g., anchors target_boxes (Tensor): target of the transformation, e.g., ground-truth boxes. """ assert isinstance(src_boxes, torch.Tensor), type(src_boxes) assert isinstance(target_boxes, torch.Tensor), type(target_boxes) src_ctr_x = 0.5 * (src_boxes[:, 0] + src_boxes[:, 2]) src_ctr_y = 0.5 * (src_boxes[:, 1] + src_boxes[:, 3]) target_l = src_ctr_x - target_boxes[:, 0] target_t = src_ctr_y - target_boxes[:, 1] target_r = target_boxes[:, 2] - src_ctr_x target_b = target_boxes[:, 3] - src_ctr_y deltas = torch.stack((target_l, target_t, target_r, target_b), dim=1) if self.normalize_by_size: stride_w = src_boxes[:, 2] - src_boxes[:, 0] stride_h = src_boxes[:, 3] - src_boxes[:, 1] strides = torch.stack([stride_w, stride_h, stride_w, stride_h], axis=1) deltas = deltas / strides return deltas def apply_deltas(self, deltas, boxes): """ Apply transformation `deltas` (dx1, dy1, dx2, dy2) to `boxes`. Args: deltas (Tensor): transformation deltas of shape (N, k*4), where k >= 1. deltas[i] represents k potentially different class-specific box transformations for the single box boxes[i]. boxes (Tensor): boxes to transform, of shape (N, 4) """ # Ensure the output is a valid box. See Sec 2.1 of https://arxiv.org/abs/2006.09214 deltas = F.relu(deltas) boxes = boxes.to(deltas.dtype) ctr_x = 0.5 * (boxes[:, 0] + boxes[:, 2]) ctr_y = 0.5 * (boxes[:, 1] + boxes[:, 3]) if self.normalize_by_size: stride_w = boxes[:, 2] - boxes[:, 0] stride_h = boxes[:, 3] - boxes[:, 1] strides = torch.stack([stride_w, stride_h, stride_w, stride_h], axis=1) deltas = deltas * strides l = deltas[:, 0::4] t = deltas[:, 1::4] r = deltas[:, 2::4] b = deltas[:, 3::4] pred_boxes = torch.zeros_like(deltas) pred_boxes[:, 0::4] = ctr_x[:, None] - l # x1 pred_boxes[:, 1::4] = ctr_y[:, None] - t # y1 pred_boxes[:, 2::4] = ctr_x[:, None] + r # x2 pred_boxes[:, 3::4] = ctr_y[:, None] + b # y2 return pred_boxes def _dense_box_regression_loss( anchors: List[Union[Boxes, torch.Tensor]], box2box_transform: Box2BoxTransform, pred_anchor_deltas: List[torch.Tensor], gt_boxes: List[torch.Tensor], fg_mask: torch.Tensor, box_reg_loss_type="smooth_l1", smooth_l1_beta=0.0, ): """ Compute loss for dense multi-level box regression. Loss is accumulated over ``fg_mask``. Args: anchors: #lvl anchor boxes, each is (HixWixA, 4) pred_anchor_deltas: #lvl predictions, each is (N, HixWixA, 4) gt_boxes: N ground truth boxes, each has shape (R, 4) (R = sum(Hi * Wi * A)) fg_mask: the foreground boolean mask of shape (N, R) to compute loss on box_reg_loss_type (str): Loss type to use. Supported losses: "smooth_l1", "giou", "diou", "ciou". smooth_l1_beta (float): beta parameter for the smooth L1 regression loss. Default to use L1 loss. Only used when `box_reg_loss_type` is "smooth_l1" """ if isinstance(anchors[0], Boxes): anchors = type(anchors[0]).cat(anchors).tensor # (R, 4) else: anchors = cat(anchors) if box_reg_loss_type == "smooth_l1": gt_anchor_deltas = [box2box_transform.get_deltas(anchors, k) for k in gt_boxes] gt_anchor_deltas = torch.stack(gt_anchor_deltas) # (N, R, 4) loss_box_reg = smooth_l1_loss( cat(pred_anchor_deltas, dim=1)[fg_mask], gt_anchor_deltas[fg_mask], beta=smooth_l1_beta, reduction="sum", ) elif box_reg_loss_type == "giou": pred_boxes = [ box2box_transform.apply_deltas(k, anchors) for k in cat(pred_anchor_deltas, dim=1) ] loss_box_reg = giou_loss( torch.stack(pred_boxes)[fg_mask], torch.stack(gt_boxes)[fg_mask], reduction="sum" ) elif box_reg_loss_type == "diou": pred_boxes = [ box2box_transform.apply_deltas(k, anchors) for k in cat(pred_anchor_deltas, dim=1) ] loss_box_reg = diou_loss( torch.stack(pred_boxes)[fg_mask], torch.stack(gt_boxes)[fg_mask], reduction="sum" ) elif box_reg_loss_type == "ciou": pred_boxes = [ box2box_transform.apply_deltas(k, anchors) for k in cat(pred_anchor_deltas, dim=1) ] loss_box_reg = ciou_loss( torch.stack(pred_boxes)[fg_mask], torch.stack(gt_boxes)[fg_mask], reduction="sum" ) else: raise ValueError(f"Invalid dense box regression loss type '{box_reg_loss_type}'") return loss_box_reg ================================================ FILE: detectron2/detectron2/modeling/matcher.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import List import torch from detectron2.layers import nonzero_tuple # TODO: the name is too general class Matcher(object): """ This class assigns to each predicted "element" (e.g., a box) a ground-truth element. Each predicted element will have exactly zero or one matches; each ground-truth element may be matched to zero or more predicted elements. The matching is determined by the MxN match_quality_matrix, that characterizes how well each (ground-truth, prediction)-pair match each other. For example, if the elements are boxes, this matrix may contain box intersection-over-union overlap values. The matcher returns (a) a vector of length N containing the index of the ground-truth element m in [0, M) that matches to prediction n in [0, N). (b) a vector of length N containing the labels for each prediction. """ def __init__( self, thresholds: List[float], labels: List[int], allow_low_quality_matches: bool = False ): """ Args: thresholds (list): a list of thresholds used to stratify predictions into levels. labels (list): a list of values to label predictions belonging at each level. A label can be one of {-1, 0, 1} signifying {ignore, negative class, positive class}, respectively. allow_low_quality_matches (bool): if True, produce additional matches for predictions with maximum match quality lower than high_threshold. See set_low_quality_matches_ for more details. For example, thresholds = [0.3, 0.5] labels = [0, -1, 1] All predictions with iou < 0.3 will be marked with 0 and thus will be considered as false positives while training. All predictions with 0.3 <= iou < 0.5 will be marked with -1 and thus will be ignored. All predictions with 0.5 <= iou will be marked with 1 and thus will be considered as true positives. """ # Add -inf and +inf to first and last position in thresholds thresholds = thresholds[:] assert thresholds[0] > 0 thresholds.insert(0, -float("inf")) thresholds.append(float("inf")) # Currently torchscript does not support all + generator assert all([low <= high for (low, high) in zip(thresholds[:-1], thresholds[1:])]) assert all([l in [-1, 0, 1] for l in labels]) assert len(labels) == len(thresholds) - 1 self.thresholds = thresholds self.labels = labels self.allow_low_quality_matches = allow_low_quality_matches def __call__(self, match_quality_matrix): """ Args: match_quality_matrix (Tensor[float]): an MxN tensor, containing the pairwise quality between M ground-truth elements and N predicted elements. All elements must be >= 0 (due to the us of `torch.nonzero` for selecting indices in :meth:`set_low_quality_matches_`). Returns: matches (Tensor[int64]): a vector of length N, where matches[i] is a matched ground-truth index in [0, M) match_labels (Tensor[int8]): a vector of length N, where pred_labels[i] indicates whether a prediction is a true or false positive or ignored """ assert match_quality_matrix.dim() == 2 if match_quality_matrix.numel() == 0: default_matches = match_quality_matrix.new_full( (match_quality_matrix.size(1),), 0, dtype=torch.int64 ) # When no gt boxes exist, we define IOU = 0 and therefore set labels # to `self.labels[0]`, which usually defaults to background class 0 # To choose to ignore instead, can make labels=[-1,0,-1,1] + set appropriate thresholds default_match_labels = match_quality_matrix.new_full( (match_quality_matrix.size(1),), self.labels[0], dtype=torch.int8 ) return default_matches, default_match_labels assert torch.all(match_quality_matrix >= 0) # match_quality_matrix is M (gt) x N (predicted) # Max over gt elements (dim 0) to find best gt candidate for each prediction matched_vals, matches = match_quality_matrix.max(dim=0) match_labels = matches.new_full(matches.size(), 1, dtype=torch.int8) for (l, low, high) in zip(self.labels, self.thresholds[:-1], self.thresholds[1:]): low_high = (matched_vals >= low) & (matched_vals < high) match_labels[low_high] = l if self.allow_low_quality_matches: self.set_low_quality_matches_(match_labels, match_quality_matrix) return matches, match_labels def set_low_quality_matches_(self, match_labels, match_quality_matrix): """ Produce additional matches for predictions that have only low-quality matches. Specifically, for each ground-truth G find the set of predictions that have maximum overlap with it (including ties); for each prediction in that set, if it is unmatched, then match it to the ground-truth G. This function implements the RPN assignment case (i) in Sec. 3.1.2 of :paper:`Faster R-CNN`. """ # For each gt, find the prediction with which it has highest quality highest_quality_foreach_gt, _ = match_quality_matrix.max(dim=1) # Find the highest quality match available, even if it is low, including ties. # Note that the matches qualities must be positive due to the use of # `torch.nonzero`. _, pred_inds_with_highest_quality = nonzero_tuple( match_quality_matrix == highest_quality_foreach_gt[:, None] ) # If an anchor was labeled positive only due to a low-quality match # with gt_A, but it has larger overlap with gt_B, it's matched index will still be gt_B. # This follows the implementation in Detectron, and is found to have no significant impact. match_labels[pred_inds_with_highest_quality] = 1 ================================================ FILE: detectron2/detectron2/modeling/meta_arch/__init__.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. from .build import META_ARCH_REGISTRY, build_model # isort:skip from .panoptic_fpn import PanopticFPN # import all the meta_arch, so they will be registered from .rcnn import GeneralizedRCNN, ProposalNetwork from .dense_detector import DenseDetector from .retinanet import RetinaNet from .fcos import FCOS from .semantic_seg import SEM_SEG_HEADS_REGISTRY, SemanticSegmentor, build_sem_seg_head __all__ = list(globals().keys()) ================================================ FILE: detectron2/detectron2/modeling/meta_arch/build.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch from detectron2.utils.logger import _log_api_usage from detectron2.utils.registry import Registry META_ARCH_REGISTRY = Registry("META_ARCH") # noqa F401 isort:skip META_ARCH_REGISTRY.__doc__ = """ Registry for meta-architectures, i.e. the whole model. The registered object will be called with `obj(cfg)` and expected to return a `nn.Module` object. """ def build_model(cfg): """ Build the whole model architecture, defined by ``cfg.MODEL.META_ARCHITECTURE``. Note that it does not load any weights from ``cfg``. """ meta_arch = cfg.MODEL.META_ARCHITECTURE model = META_ARCH_REGISTRY.get(meta_arch)(cfg) model.to(torch.device(cfg.MODEL.DEVICE)) _log_api_usage("modeling.meta_arch." + meta_arch) return model ================================================ FILE: detectron2/detectron2/modeling/meta_arch/dense_detector.py ================================================ import numpy as np from typing import Dict, List, Optional, Tuple import torch from torch import Tensor, nn from detectron2.data.detection_utils import convert_image_to_rgb from detectron2.layers import move_device_like from detectron2.modeling import Backbone from detectron2.structures import Boxes, ImageList, Instances from detectron2.utils.events import get_event_storage from ..postprocessing import detector_postprocess def permute_to_N_HWA_K(tensor, K: int): """ Transpose/reshape a tensor from (N, (Ai x K), H, W) to (N, (HxWxAi), K) """ assert tensor.dim() == 4, tensor.shape N, _, H, W = tensor.shape tensor = tensor.view(N, -1, K, H, W) tensor = tensor.permute(0, 3, 4, 1, 2) tensor = tensor.reshape(N, -1, K) # Size=(N,HWA,K) return tensor class DenseDetector(nn.Module): """ Base class for dense detector. We define a dense detector as a fully-convolutional model that makes per-pixel (i.e. dense) predictions. """ def __init__( self, backbone: Backbone, head: nn.Module, head_in_features: Optional[List[str]] = None, *, pixel_mean, pixel_std, ): """ Args: backbone: backbone module head: head module head_in_features: backbone features to use in head. Default to all backbone features. pixel_mean (Tuple[float]): Values to be used for image normalization (BGR order). To train on images of different number of channels, set different mean & std. Default values are the mean pixel value from ImageNet: [103.53, 116.28, 123.675] pixel_std (Tuple[float]): When using pre-trained models in Detectron1 or any MSRA models, std has been absorbed into its conv1 weights, so the std needs to be set 1. Otherwise, you can use [57.375, 57.120, 58.395] (ImageNet std) """ super().__init__() self.backbone = backbone self.head = head if head_in_features is None: shapes = self.backbone.output_shape() self.head_in_features = sorted(shapes.keys(), key=lambda x: shapes[x].stride) else: self.head_in_features = head_in_features self.register_buffer("pixel_mean", torch.tensor(pixel_mean).view(-1, 1, 1), False) self.register_buffer("pixel_std", torch.tensor(pixel_std).view(-1, 1, 1), False) @property def device(self): return self.pixel_mean.device def _move_to_current_device(self, x): return move_device_like(x, self.pixel_mean) def forward(self, batched_inputs: List[Dict[str, Tensor]]): """ Args: batched_inputs: a list, batched outputs of :class:`DatasetMapper` . Each item in the list contains the inputs for one image. For now, each item in the list is a dict that contains: * image: Tensor, image in (C, H, W) format. * instances: Instances Other information that's included in the original dicts, such as: * "height", "width" (int): the output resolution of the model, used in inference. See :meth:`postprocess` for details. Returns: In training, dict[str, Tensor]: mapping from a named loss to a tensor storing the loss. Used during training only. In inference, the standard output format, described in :doc:`/tutorials/models`. """ images = self.preprocess_image(batched_inputs) features = self.backbone(images.tensor) features = [features[f] for f in self.head_in_features] predictions = self.head(features) if self.training: assert not torch.jit.is_scripting(), "Not supported" assert "instances" in batched_inputs[0], "Instance annotations are missing in training!" gt_instances = [x["instances"].to(self.device) for x in batched_inputs] return self.forward_training(images, features, predictions, gt_instances) else: results = self.forward_inference(images, features, predictions) if torch.jit.is_scripting(): return results processed_results = [] for results_per_image, input_per_image, image_size in zip( results, batched_inputs, images.image_sizes ): height = input_per_image.get("height", image_size[0]) width = input_per_image.get("width", image_size[1]) r = detector_postprocess(results_per_image, height, width) processed_results.append({"instances": r}) return processed_results def forward_training(self, images, features, predictions, gt_instances): raise NotImplementedError() def preprocess_image(self, batched_inputs: List[Dict[str, Tensor]]): """ Normalize, pad and batch the input images. """ images = [self._move_to_current_device(x["image"]) for x in batched_inputs] images = [(x - self.pixel_mean) / self.pixel_std for x in images] images = ImageList.from_tensors( images, self.backbone.size_divisibility, padding_constraints=self.backbone.padding_constraints, ) return images def _transpose_dense_predictions( self, predictions: List[List[Tensor]], dims_per_anchor: List[int] ) -> List[List[Tensor]]: """ Transpose the dense per-level predictions. Args: predictions: a list of outputs, each is a list of per-level predictions with shape (N, Ai x K, Hi, Wi), where N is the number of images, Ai is the number of anchors per location on level i, K is the dimension of predictions per anchor. dims_per_anchor: the value of K for each predictions. e.g. 4 for box prediction, #classes for classification prediction. Returns: List[List[Tensor]]: each prediction is transposed to (N, Hi x Wi x Ai, K). """ assert len(predictions) == len(dims_per_anchor) res: List[List[Tensor]] = [] for pred, dim_per_anchor in zip(predictions, dims_per_anchor): pred = [permute_to_N_HWA_K(x, dim_per_anchor) for x in pred] res.append(pred) return res def _ema_update(self, name: str, value: float, initial_value: float, momentum: float = 0.9): """ Apply EMA update to `self.name` using `value`. This is mainly used for loss normalizer. In Detectron1, loss is normalized by number of foreground samples in the batch. When batch size is 1 per GPU, #foreground has a large variance and using it lead to lower performance. Therefore we maintain an EMA of #foreground to stabilize the normalizer. Args: name: name of the normalizer value: the new value to update initial_value: the initial value to start with momentum: momentum of EMA Returns: float: the updated EMA value """ if hasattr(self, name): old = getattr(self, name) else: old = initial_value new = old * momentum + value * (1 - momentum) setattr(self, name, new) return new def _decode_per_level_predictions( self, anchors: Boxes, pred_scores: Tensor, pred_deltas: Tensor, score_thresh: float, topk_candidates: int, image_size: Tuple[int, int], ) -> Instances: """ Decode boxes and classification predictions of one featuer level, by the following steps: 1. filter the predictions based on score threshold and top K scores. 2. transform the box regression outputs 3. return the predicted scores, classes and boxes Args: anchors: Boxes, anchor for this feature level pred_scores: HxWxA,K pred_deltas: HxWxA,4 Returns: Instances: with field "scores", "pred_boxes", "pred_classes". """ # Apply two filtering to make NMS faster. # 1. Keep boxes with confidence score higher than threshold keep_idxs = pred_scores > score_thresh pred_scores = pred_scores[keep_idxs] topk_idxs = torch.nonzero(keep_idxs) # Kx2 # 2. Keep top k top scoring boxes only num_topk = min(topk_candidates, topk_idxs.size(0)) pred_scores, idxs = pred_scores.topk(num_topk) topk_idxs = topk_idxs[idxs] anchor_idxs, classes_idxs = topk_idxs.unbind(dim=1) pred_boxes = self.box2box_transform.apply_deltas( pred_deltas[anchor_idxs], anchors.tensor[anchor_idxs] ) return Instances( image_size, pred_boxes=Boxes(pred_boxes), scores=pred_scores, pred_classes=classes_idxs ) def _decode_multi_level_predictions( self, anchors: List[Boxes], pred_scores: List[Tensor], pred_deltas: List[Tensor], score_thresh: float, topk_candidates: int, image_size: Tuple[int, int], ) -> Instances: """ Run `_decode_per_level_predictions` for all feature levels and concat the results. """ predictions = [ self._decode_per_level_predictions( anchors_i, box_cls_i, box_reg_i, self.test_score_thresh, self.test_topk_candidates, image_size, ) # Iterate over every feature level for box_cls_i, box_reg_i, anchors_i in zip(pred_scores, pred_deltas, anchors) ] return predictions[0].cat(predictions) # 'Instances.cat' is not scriptale but this is def visualize_training(self, batched_inputs, results): """ A function used to visualize ground truth images and final network predictions. It shows ground truth bounding boxes on the original image and up to 20 predicted object bounding boxes on the original image. Args: batched_inputs (list): a list that contains input to the model. results (List[Instances]): a list of #images elements returned by forward_inference(). """ from detectron2.utils.visualizer import Visualizer assert len(batched_inputs) == len( results ), "Cannot visualize inputs and results of different sizes" storage = get_event_storage() max_boxes = 20 image_index = 0 # only visualize a single image img = batched_inputs[image_index]["image"] img = convert_image_to_rgb(img.permute(1, 2, 0), self.input_format) v_gt = Visualizer(img, None) v_gt = v_gt.overlay_instances(boxes=batched_inputs[image_index]["instances"].gt_boxes) anno_img = v_gt.get_image() processed_results = detector_postprocess(results[image_index], img.shape[0], img.shape[1]) predicted_boxes = processed_results.pred_boxes.tensor.detach().cpu().numpy() v_pred = Visualizer(img, None) v_pred = v_pred.overlay_instances(boxes=predicted_boxes[0:max_boxes]) prop_img = v_pred.get_image() vis_img = np.vstack((anno_img, prop_img)) vis_img = vis_img.transpose(2, 0, 1) vis_name = f"Top: GT bounding boxes; Bottom: {max_boxes} Highest Scoring Results" storage.put_image(vis_name, vis_img) ================================================ FILE: detectron2/detectron2/modeling/meta_arch/fcos.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging from typing import List, Optional, Tuple import torch from fvcore.nn import sigmoid_focal_loss_jit from torch import nn from torch.nn import functional as F from detectron2.layers import ShapeSpec, batched_nms from detectron2.structures import Boxes, ImageList, Instances, pairwise_point_box_distance from detectron2.utils.events import get_event_storage from ..anchor_generator import DefaultAnchorGenerator from ..backbone import Backbone from ..box_regression import Box2BoxTransformLinear, _dense_box_regression_loss from .dense_detector import DenseDetector from .retinanet import RetinaNetHead __all__ = ["FCOS"] logger = logging.getLogger(__name__) class FCOS(DenseDetector): """ Implement FCOS in :paper:`fcos`. """ def __init__( self, *, backbone: Backbone, head: nn.Module, head_in_features: Optional[List[str]] = None, box2box_transform=None, num_classes, center_sampling_radius: float = 1.5, focal_loss_alpha=0.25, focal_loss_gamma=2.0, test_score_thresh=0.2, test_topk_candidates=1000, test_nms_thresh=0.6, max_detections_per_image=100, pixel_mean, pixel_std, ): """ Args: center_sampling_radius: radius of the "center" of a groundtruth box, within which all anchor points are labeled positive. Other arguments mean the same as in :class:`RetinaNet`. """ super().__init__( backbone, head, head_in_features, pixel_mean=pixel_mean, pixel_std=pixel_std ) self.num_classes = num_classes # FCOS uses one anchor point per location. # We represent the anchor point by a box whose size equals the anchor stride. feature_shapes = backbone.output_shape() fpn_strides = [feature_shapes[k].stride for k in self.head_in_features] self.anchor_generator = DefaultAnchorGenerator( sizes=[[k] for k in fpn_strides], aspect_ratios=[1.0], strides=fpn_strides ) # FCOS parameterizes box regression by a linear transform, # where predictions are normalized by anchor stride (equal to anchor size). if box2box_transform is None: box2box_transform = Box2BoxTransformLinear(normalize_by_size=True) self.box2box_transform = box2box_transform self.center_sampling_radius = float(center_sampling_radius) # Loss parameters: self.focal_loss_alpha = focal_loss_alpha self.focal_loss_gamma = focal_loss_gamma # Inference parameters: self.test_score_thresh = test_score_thresh self.test_topk_candidates = test_topk_candidates self.test_nms_thresh = test_nms_thresh self.max_detections_per_image = max_detections_per_image def forward_training(self, images, features, predictions, gt_instances): # Transpose the Hi*Wi*A dimension to the middle: pred_logits, pred_anchor_deltas, pred_centerness = self._transpose_dense_predictions( predictions, [self.num_classes, 4, 1] ) anchors = self.anchor_generator(features) gt_labels, gt_boxes = self.label_anchors(anchors, gt_instances) return self.losses( anchors, pred_logits, gt_labels, pred_anchor_deltas, gt_boxes, pred_centerness ) @torch.no_grad() def _match_anchors(self, gt_boxes: Boxes, anchors: List[Boxes]): """ Match ground-truth boxes to a set of multi-level anchors. Args: gt_boxes: Ground-truth boxes from instances of an image. anchors: List of anchors for each feature map (of different scales). Returns: torch.Tensor A tensor of shape `(M, R)`, given `M` ground-truth boxes and total `R` anchor points from all feature levels, indicating the quality of match between m-th box and r-th anchor. Higher value indicates better match. """ # Naming convention: (M = ground-truth boxes, R = anchor points) # Anchor points are represented as square boxes of size = stride. num_anchors_per_level = [len(x) for x in anchors] anchors = Boxes.cat(anchors) # (R, 4) anchor_centers = anchors.get_centers() # (R, 2) anchor_sizes = anchors.tensor[:, 2] - anchors.tensor[:, 0] # (R, ) lower_bound = anchor_sizes * 4 lower_bound[: num_anchors_per_level[0]] = 0 upper_bound = anchor_sizes * 8 upper_bound[-num_anchors_per_level[-1] :] = float("inf") gt_centers = gt_boxes.get_centers() # FCOS with center sampling: anchor point must be close enough to # ground-truth box center. center_dists = (anchor_centers[None, :, :] - gt_centers[:, None, :]).abs_() sampling_regions = self.center_sampling_radius * anchor_sizes[None, :] match_quality_matrix = center_dists.max(dim=2).values < sampling_regions pairwise_dist = pairwise_point_box_distance(anchor_centers, gt_boxes) pairwise_dist = pairwise_dist.permute(1, 0, 2) # (M, R, 4) # The original FCOS anchor matching rule: anchor point must be inside GT. match_quality_matrix &= pairwise_dist.min(dim=2).values > 0 # Multilevel anchor matching in FCOS: each anchor is only responsible # for certain scale range. pairwise_dist = pairwise_dist.max(dim=2).values match_quality_matrix &= (pairwise_dist > lower_bound[None, :]) & ( pairwise_dist < upper_bound[None, :] ) # Match the GT box with minimum area, if there are multiple GT matches. gt_areas = gt_boxes.area() # (M, ) match_quality_matrix = match_quality_matrix.to(torch.float32) match_quality_matrix *= 1e8 - gt_areas[:, None] return match_quality_matrix # (M, R) @torch.no_grad() def label_anchors(self, anchors: List[Boxes], gt_instances: List[Instances]): """ Same interface as :meth:`RetinaNet.label_anchors`, but implemented with FCOS anchor matching rule. Unlike RetinaNet, there are no ignored anchors. """ gt_labels, matched_gt_boxes = [], [] for inst in gt_instances: if len(inst) > 0: match_quality_matrix = self._match_anchors(inst.gt_boxes, anchors) # Find matched ground-truth box per anchor. Un-matched anchors are # assigned -1. This is equivalent to using an anchor matcher as used # in R-CNN/RetinaNet: `Matcher(thresholds=[1e-5], labels=[0, 1])` match_quality, matched_idxs = match_quality_matrix.max(dim=0) matched_idxs[match_quality < 1e-5] = -1 matched_gt_boxes_i = inst.gt_boxes.tensor[matched_idxs.clip(min=0)] gt_labels_i = inst.gt_classes[matched_idxs.clip(min=0)] # Anchors with matched_idxs = -1 are labeled background. gt_labels_i[matched_idxs < 0] = self.num_classes else: matched_gt_boxes_i = torch.zeros_like(Boxes.cat(anchors).tensor) gt_labels_i = torch.full( (len(matched_gt_boxes_i),), fill_value=self.num_classes, dtype=torch.long, device=matched_gt_boxes_i.device, ) gt_labels.append(gt_labels_i) matched_gt_boxes.append(matched_gt_boxes_i) return gt_labels, matched_gt_boxes def losses( self, anchors, pred_logits, gt_labels, pred_anchor_deltas, gt_boxes, pred_centerness ): """ This method is almost identical to :meth:`RetinaNet.losses`, with an extra "loss_centerness" in the returned dict. """ num_images = len(gt_labels) gt_labels = torch.stack(gt_labels) # (M, R) pos_mask = (gt_labels >= 0) & (gt_labels != self.num_classes) num_pos_anchors = pos_mask.sum().item() get_event_storage().put_scalar("num_pos_anchors", num_pos_anchors / num_images) normalizer = self._ema_update("loss_normalizer", max(num_pos_anchors, 1), 300) # classification and regression loss gt_labels_target = F.one_hot(gt_labels, num_classes=self.num_classes + 1)[ :, :, :-1 ] # no loss for the last (background) class loss_cls = sigmoid_focal_loss_jit( torch.cat(pred_logits, dim=1), gt_labels_target.to(pred_logits[0].dtype), alpha=self.focal_loss_alpha, gamma=self.focal_loss_gamma, reduction="sum", ) loss_box_reg = _dense_box_regression_loss( anchors, self.box2box_transform, pred_anchor_deltas, gt_boxes, pos_mask, box_reg_loss_type="giou", ) ctrness_targets = self.compute_ctrness_targets(anchors, gt_boxes) # (M, R) pred_centerness = torch.cat(pred_centerness, dim=1).squeeze(dim=2) # (M, R) ctrness_loss = F.binary_cross_entropy_with_logits( pred_centerness[pos_mask], ctrness_targets[pos_mask], reduction="sum" ) return { "loss_fcos_cls": loss_cls / normalizer, "loss_fcos_loc": loss_box_reg / normalizer, "loss_fcos_ctr": ctrness_loss / normalizer, } def compute_ctrness_targets(self, anchors: List[Boxes], gt_boxes: List[torch.Tensor]): anchors = Boxes.cat(anchors).tensor # Rx4 reg_targets = [self.box2box_transform.get_deltas(anchors, m) for m in gt_boxes] reg_targets = torch.stack(reg_targets, dim=0) # NxRx4 if len(reg_targets) == 0: return reg_targets.new_zeros(len(reg_targets)) left_right = reg_targets[:, :, [0, 2]] top_bottom = reg_targets[:, :, [1, 3]] ctrness = (left_right.min(dim=-1)[0] / left_right.max(dim=-1)[0]) * ( top_bottom.min(dim=-1)[0] / top_bottom.max(dim=-1)[0] ) return torch.sqrt(ctrness) def forward_inference( self, images: ImageList, features: List[torch.Tensor], predictions: List[List[torch.Tensor]], ): pred_logits, pred_anchor_deltas, pred_centerness = self._transpose_dense_predictions( predictions, [self.num_classes, 4, 1] ) anchors = self.anchor_generator(features) results: List[Instances] = [] for img_idx, image_size in enumerate(images.image_sizes): scores_per_image = [ # Multiply and sqrt centerness & classification scores # (See eqn. 4 in https://arxiv.org/abs/2006.09214) torch.sqrt(x[img_idx].sigmoid_() * y[img_idx].sigmoid_()) for x, y in zip(pred_logits, pred_centerness) ] deltas_per_image = [x[img_idx] for x in pred_anchor_deltas] results_per_image = self.inference_single_image( anchors, scores_per_image, deltas_per_image, image_size ) results.append(results_per_image) return results def inference_single_image( self, anchors: List[Boxes], box_cls: List[torch.Tensor], box_delta: List[torch.Tensor], image_size: Tuple[int, int], ): """ Identical to :meth:`RetinaNet.inference_single_image. """ pred = self._decode_multi_level_predictions( anchors, box_cls, box_delta, self.test_score_thresh, self.test_topk_candidates, image_size, ) keep = batched_nms( pred.pred_boxes.tensor, pred.scores, pred.pred_classes, self.test_nms_thresh ) return pred[keep[: self.max_detections_per_image]] class FCOSHead(RetinaNetHead): """ The head used in :paper:`fcos`. It adds an additional centerness prediction branch on top of :class:`RetinaNetHead`. """ def __init__(self, *, input_shape: List[ShapeSpec], conv_dims: List[int], **kwargs): super().__init__(input_shape=input_shape, conv_dims=conv_dims, num_anchors=1, **kwargs) # Unlike original FCOS, we do not add an additional learnable scale layer # because it's found to have no benefits after normalizing regression targets by stride. self._num_features = len(input_shape) self.ctrness = nn.Conv2d(conv_dims[-1], 1, kernel_size=3, stride=1, padding=1) torch.nn.init.normal_(self.ctrness.weight, std=0.01) torch.nn.init.constant_(self.ctrness.bias, 0) def forward(self, features): assert len(features) == self._num_features logits = [] bbox_reg = [] ctrness = [] for feature in features: logits.append(self.cls_score(self.cls_subnet(feature))) bbox_feature = self.bbox_subnet(feature) bbox_reg.append(self.bbox_pred(bbox_feature)) ctrness.append(self.ctrness(bbox_feature)) return logits, bbox_reg, ctrness ================================================ FILE: detectron2/detectron2/modeling/meta_arch/panoptic_fpn.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import logging from typing import Dict, List import torch from torch import nn from detectron2.config import configurable from detectron2.structures import ImageList from ..postprocessing import detector_postprocess, sem_seg_postprocess from .build import META_ARCH_REGISTRY from .rcnn import GeneralizedRCNN from .semantic_seg import build_sem_seg_head __all__ = ["PanopticFPN"] @META_ARCH_REGISTRY.register() class PanopticFPN(GeneralizedRCNN): """ Implement the paper :paper:`PanopticFPN`. """ @configurable def __init__( self, *, sem_seg_head: nn.Module, combine_overlap_thresh: float = 0.5, combine_stuff_area_thresh: float = 4096, combine_instances_score_thresh: float = 0.5, **kwargs, ): """ NOTE: this interface is experimental. Args: sem_seg_head: a module for the semantic segmentation head. combine_overlap_thresh: combine masks into one instances if they have enough overlap combine_stuff_area_thresh: ignore stuff areas smaller than this threshold combine_instances_score_thresh: ignore instances whose score is smaller than this threshold Other arguments are the same as :class:`GeneralizedRCNN`. """ super().__init__(**kwargs) self.sem_seg_head = sem_seg_head # options when combining instance & semantic outputs self.combine_overlap_thresh = combine_overlap_thresh self.combine_stuff_area_thresh = combine_stuff_area_thresh self.combine_instances_score_thresh = combine_instances_score_thresh @classmethod def from_config(cls, cfg): ret = super().from_config(cfg) ret.update( { "combine_overlap_thresh": cfg.MODEL.PANOPTIC_FPN.COMBINE.OVERLAP_THRESH, "combine_stuff_area_thresh": cfg.MODEL.PANOPTIC_FPN.COMBINE.STUFF_AREA_LIMIT, "combine_instances_score_thresh": cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH, # noqa } ) ret["sem_seg_head"] = build_sem_seg_head(cfg, ret["backbone"].output_shape()) logger = logging.getLogger(__name__) if not cfg.MODEL.PANOPTIC_FPN.COMBINE.ENABLED: logger.warning( "PANOPTIC_FPN.COMBINED.ENABLED is no longer used. " " model.inference(do_postprocess=) should be used to toggle postprocessing." ) if cfg.MODEL.PANOPTIC_FPN.INSTANCE_LOSS_WEIGHT != 1.0: w = cfg.MODEL.PANOPTIC_FPN.INSTANCE_LOSS_WEIGHT logger.warning( "PANOPTIC_FPN.INSTANCE_LOSS_WEIGHT should be replaced by weights on each ROI head." ) def update_weight(x): if isinstance(x, dict): return {k: v * w for k, v in x.items()} else: return x * w roi_heads = ret["roi_heads"] roi_heads.box_predictor.loss_weight = update_weight(roi_heads.box_predictor.loss_weight) roi_heads.mask_head.loss_weight = update_weight(roi_heads.mask_head.loss_weight) return ret def forward(self, batched_inputs): """ Args: batched_inputs: a list, batched outputs of :class:`DatasetMapper`. Each item in the list contains the inputs for one image. For now, each item in the list is a dict that contains: * "image": Tensor, image in (C, H, W) format. * "instances": Instances * "sem_seg": semantic segmentation ground truth. * Other information that's included in the original dicts, such as: "height", "width" (int): the output resolution of the model, used in inference. See :meth:`postprocess` for details. Returns: list[dict]: each dict has the results for one image. The dict contains the following keys: * "instances": see :meth:`GeneralizedRCNN.forward` for its format. * "sem_seg": see :meth:`SemanticSegmentor.forward` for its format. * "panoptic_seg": See the return value of :func:`combine_semantic_and_instance_outputs` for its format. """ if not self.training: return self.inference(batched_inputs) images = self.preprocess_image(batched_inputs) features = self.backbone(images.tensor) assert "sem_seg" in batched_inputs[0] gt_sem_seg = [x["sem_seg"].to(self.device) for x in batched_inputs] gt_sem_seg = ImageList.from_tensors( gt_sem_seg, self.backbone.size_divisibility, self.sem_seg_head.ignore_value, self.backbone.padding_constraints, ).tensor sem_seg_results, sem_seg_losses = self.sem_seg_head(features, gt_sem_seg) gt_instances = [x["instances"].to(self.device) for x in batched_inputs] proposals, proposal_losses = self.proposal_generator(images, features, gt_instances) detector_results, detector_losses = self.roi_heads( images, features, proposals, gt_instances ) losses = sem_seg_losses losses.update(proposal_losses) losses.update(detector_losses) return losses def inference(self, batched_inputs: List[Dict[str, torch.Tensor]], do_postprocess: bool = True): """ Run inference on the given inputs. Args: batched_inputs (list[dict]): same as in :meth:`forward` do_postprocess (bool): whether to apply post-processing on the outputs. Returns: When do_postprocess=True, see docs in :meth:`forward`. Otherwise, returns a (list[Instances], list[Tensor]) that contains the raw detector outputs, and raw semantic segmentation outputs. """ images = self.preprocess_image(batched_inputs) features = self.backbone(images.tensor) sem_seg_results, sem_seg_losses = self.sem_seg_head(features, None) proposals, _ = self.proposal_generator(images, features, None) detector_results, _ = self.roi_heads(images, features, proposals, None) if do_postprocess: processed_results = [] for sem_seg_result, detector_result, input_per_image, image_size in zip( sem_seg_results, detector_results, batched_inputs, images.image_sizes ): height = input_per_image.get("height", image_size[0]) width = input_per_image.get("width", image_size[1]) sem_seg_r = sem_seg_postprocess(sem_seg_result, image_size, height, width) detector_r = detector_postprocess(detector_result, height, width) processed_results.append({"sem_seg": sem_seg_r, "instances": detector_r}) panoptic_r = combine_semantic_and_instance_outputs( detector_r, sem_seg_r.argmax(dim=0), self.combine_overlap_thresh, self.combine_stuff_area_thresh, self.combine_instances_score_thresh, ) processed_results[-1]["panoptic_seg"] = panoptic_r return processed_results else: return detector_results, sem_seg_results def combine_semantic_and_instance_outputs( instance_results, semantic_results, overlap_threshold, stuff_area_thresh, instances_score_thresh, ): """ Implement a simple combining logic following "combine_semantic_and_instance_predictions.py" in panopticapi to produce panoptic segmentation outputs. Args: instance_results: output of :func:`detector_postprocess`. semantic_results: an (H, W) tensor, each element is the contiguous semantic category id Returns: panoptic_seg (Tensor): of shape (height, width) where the values are ids for each segment. segments_info (list[dict]): Describe each segment in `panoptic_seg`. Each dict contains keys "id", "category_id", "isthing". """ panoptic_seg = torch.zeros_like(semantic_results, dtype=torch.int32) # sort instance outputs by scores sorted_inds = torch.argsort(-instance_results.scores) current_segment_id = 0 segments_info = [] instance_masks = instance_results.pred_masks.to(dtype=torch.bool, device=panoptic_seg.device) # Add instances one-by-one, check for overlaps with existing ones for inst_id in sorted_inds: score = instance_results.scores[inst_id].item() if score < instances_score_thresh: break mask = instance_masks[inst_id] # H,W mask_area = mask.sum().item() if mask_area == 0: continue intersect = (mask > 0) & (panoptic_seg > 0) intersect_area = intersect.sum().item() if intersect_area * 1.0 / mask_area > overlap_threshold: continue if intersect_area > 0: mask = mask & (panoptic_seg == 0) current_segment_id += 1 panoptic_seg[mask] = current_segment_id segments_info.append( { "id": current_segment_id, "isthing": True, "score": score, "category_id": instance_results.pred_classes[inst_id].item(), "instance_id": inst_id.item(), } ) # Add semantic results to remaining empty areas semantic_labels = torch.unique(semantic_results).cpu().tolist() for semantic_label in semantic_labels: if semantic_label == 0: # 0 is a special "thing" class continue mask = (semantic_results == semantic_label) & (panoptic_seg == 0) mask_area = mask.sum().item() if mask_area < stuff_area_thresh: continue current_segment_id += 1 panoptic_seg[mask] = current_segment_id segments_info.append( { "id": current_segment_id, "isthing": False, "category_id": semantic_label, "area": mask_area, } ) return panoptic_seg, segments_info ================================================ FILE: detectron2/detectron2/modeling/meta_arch/rcnn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np from typing import Dict, List, Optional, Tuple import torch from torch import nn from detectron2.config import configurable from detectron2.data.detection_utils import convert_image_to_rgb from detectron2.layers import move_device_like from detectron2.structures import ImageList, Instances from detectron2.utils.events import get_event_storage from detectron2.utils.logger import log_first_n from ..backbone import Backbone, build_backbone from ..postprocessing import detector_postprocess from ..proposal_generator import build_proposal_generator from ..roi_heads import build_roi_heads from .build import META_ARCH_REGISTRY __all__ = ["GeneralizedRCNN", "ProposalNetwork"] @META_ARCH_REGISTRY.register() class GeneralizedRCNN(nn.Module): """ Generalized R-CNN. Any models that contains the following three components: 1. Per-image feature extraction (aka backbone) 2. Region proposal generation 3. Per-region feature extraction and prediction """ @configurable def __init__( self, *, backbone: Backbone, proposal_generator: nn.Module, roi_heads: nn.Module, pixel_mean: Tuple[float], pixel_std: Tuple[float], input_format: Optional[str] = None, vis_period: int = 0, ): """ Args: backbone: a backbone module, must follow detectron2's backbone interface proposal_generator: a module that generates proposals using backbone features roi_heads: a ROI head that performs per-region computation pixel_mean, pixel_std: list or tuple with #channels element, representing the per-channel mean and std to be used to normalize the input image input_format: describe the meaning of channels of input. Needed by visualization vis_period: the period to run visualization. Set to 0 to disable. """ super().__init__() self.backbone = backbone self.proposal_generator = proposal_generator self.roi_heads = roi_heads self.input_format = input_format self.vis_period = vis_period if vis_period > 0: assert input_format is not None, "input_format is required for visualization!" self.register_buffer("pixel_mean", torch.tensor(pixel_mean).view(-1, 1, 1), False) self.register_buffer("pixel_std", torch.tensor(pixel_std).view(-1, 1, 1), False) assert ( self.pixel_mean.shape == self.pixel_std.shape ), f"{self.pixel_mean} and {self.pixel_std} have different shapes!" @classmethod def from_config(cls, cfg): backbone = build_backbone(cfg) return { "backbone": backbone, "proposal_generator": build_proposal_generator(cfg, backbone.output_shape()), "roi_heads": build_roi_heads(cfg, backbone.output_shape()), "input_format": cfg.INPUT.FORMAT, "vis_period": cfg.VIS_PERIOD, "pixel_mean": cfg.MODEL.PIXEL_MEAN, "pixel_std": cfg.MODEL.PIXEL_STD, } @property def device(self): return self.pixel_mean.device def _move_to_current_device(self, x): return move_device_like(x, self.pixel_mean) def visualize_training(self, batched_inputs, proposals): """ A function used to visualize images and proposals. It shows ground truth bounding boxes on the original image and up to 20 top-scoring predicted object proposals on the original image. Users can implement different visualization functions for different models. Args: batched_inputs (list): a list that contains input to the model. proposals (list): a list that contains predicted proposals. Both batched_inputs and proposals should have the same length. """ from detectron2.utils.visualizer import Visualizer storage = get_event_storage() max_vis_prop = 20 for input, prop in zip(batched_inputs, proposals): img = input["image"] img = convert_image_to_rgb(img.permute(1, 2, 0), self.input_format) v_gt = Visualizer(img, None) v_gt = v_gt.overlay_instances(boxes=input["instances"].gt_boxes) anno_img = v_gt.get_image() box_size = min(len(prop.proposal_boxes), max_vis_prop) v_pred = Visualizer(img, None) v_pred = v_pred.overlay_instances( boxes=prop.proposal_boxes[0:box_size].tensor.cpu().numpy() ) prop_img = v_pred.get_image() vis_img = np.concatenate((anno_img, prop_img), axis=1) vis_img = vis_img.transpose(2, 0, 1) vis_name = "Left: GT bounding boxes; Right: Predicted proposals" storage.put_image(vis_name, vis_img) break # only visualize one image in a batch def forward(self, batched_inputs: List[Dict[str, torch.Tensor]]): """ Args: batched_inputs: a list, batched outputs of :class:`DatasetMapper` . Each item in the list contains the inputs for one image. For now, each item in the list is a dict that contains: * image: Tensor, image in (C, H, W) format. * instances (optional): groundtruth :class:`Instances` * proposals (optional): :class:`Instances`, precomputed proposals. Other information that's included in the original dicts, such as: * "height", "width" (int): the output resolution of the model, used in inference. See :meth:`postprocess` for details. Returns: list[dict]: Each dict is the output for one input image. The dict contains one key "instances" whose value is a :class:`Instances`. The :class:`Instances` object has the following keys: "pred_boxes", "pred_classes", "scores", "pred_masks", "pred_keypoints" """ if not self.training: return self.inference(batched_inputs) images = self.preprocess_image(batched_inputs) if "instances" in batched_inputs[0]: gt_instances = [x["instances"].to(self.device) for x in batched_inputs] else: gt_instances = None features = self.backbone(images.tensor) if self.proposal_generator is not None: proposals, proposal_losses = self.proposal_generator(images, features, gt_instances) else: assert "proposals" in batched_inputs[0] proposals = [x["proposals"].to(self.device) for x in batched_inputs] proposal_losses = {} _, detector_losses = self.roi_heads(images, features, proposals, gt_instances) if self.vis_period > 0: storage = get_event_storage() if storage.iter % self.vis_period == 0: self.visualize_training(batched_inputs, proposals) losses = {} losses.update(detector_losses) losses.update(proposal_losses) return losses def inference( self, batched_inputs: List[Dict[str, torch.Tensor]], detected_instances: Optional[List[Instances]] = None, do_postprocess: bool = True, ): """ Run inference on the given inputs. Args: batched_inputs (list[dict]): same as in :meth:`forward` detected_instances (None or list[Instances]): if not None, it contains an `Instances` object per image. The `Instances` object contains "pred_boxes" and "pred_classes" which are known boxes in the image. The inference will then skip the detection of bounding boxes, and only predict other per-ROI outputs. do_postprocess (bool): whether to apply post-processing on the outputs. Returns: When do_postprocess=True, same as in :meth:`forward`. Otherwise, a list[Instances] containing raw network outputs. """ assert not self.training images = self.preprocess_image(batched_inputs) features = self.backbone(images.tensor) if detected_instances is None: if self.proposal_generator is not None: proposals, _ = self.proposal_generator(images, features, None) else: assert "proposals" in batched_inputs[0] proposals = [x["proposals"].to(self.device) for x in batched_inputs] results, _ = self.roi_heads(images, features, proposals, None) else: detected_instances = [x.to(self.device) for x in detected_instances] results = self.roi_heads.forward_with_given_boxes(features, detected_instances) if do_postprocess: assert not torch.jit.is_scripting(), "Scripting is not supported for postprocess." return GeneralizedRCNN._postprocess(results, batched_inputs, images.image_sizes) return results def preprocess_image(self, batched_inputs: List[Dict[str, torch.Tensor]]): """ Normalize, pad and batch the input images. """ images = [self._move_to_current_device(x["image"]) for x in batched_inputs] images = [(x - self.pixel_mean) / self.pixel_std for x in images] images = ImageList.from_tensors( images, self.backbone.size_divisibility, padding_constraints=self.backbone.padding_constraints, ) return images @staticmethod def _postprocess(instances, batched_inputs: List[Dict[str, torch.Tensor]], image_sizes): """ Rescale the output instances to the target size. """ # note: private function; subject to changes processed_results = [] for results_per_image, input_per_image, image_size in zip( instances, batched_inputs, image_sizes ): height = input_per_image.get("height", image_size[0]) width = input_per_image.get("width", image_size[1]) r = detector_postprocess(results_per_image, height, width) processed_results.append({"instances": r}) return processed_results @META_ARCH_REGISTRY.register() class ProposalNetwork(nn.Module): """ A meta architecture that only predicts object proposals. """ @configurable def __init__( self, *, backbone: Backbone, proposal_generator: nn.Module, pixel_mean: Tuple[float], pixel_std: Tuple[float], ): """ Args: backbone: a backbone module, must follow detectron2's backbone interface proposal_generator: a module that generates proposals using backbone features pixel_mean, pixel_std: list or tuple with #channels element, representing the per-channel mean and std to be used to normalize the input image """ super().__init__() self.backbone = backbone self.proposal_generator = proposal_generator self.register_buffer("pixel_mean", torch.tensor(pixel_mean).view(-1, 1, 1), False) self.register_buffer("pixel_std", torch.tensor(pixel_std).view(-1, 1, 1), False) @classmethod def from_config(cls, cfg): backbone = build_backbone(cfg) return { "backbone": backbone, "proposal_generator": build_proposal_generator(cfg, backbone.output_shape()), "pixel_mean": cfg.MODEL.PIXEL_MEAN, "pixel_std": cfg.MODEL.PIXEL_STD, } @property def device(self): return self.pixel_mean.device def _move_to_current_device(self, x): return move_device_like(x, self.pixel_mean) def forward(self, batched_inputs): """ Args: Same as in :class:`GeneralizedRCNN.forward` Returns: list[dict]: Each dict is the output for one input image. The dict contains one key "proposals" whose value is a :class:`Instances` with keys "proposal_boxes" and "objectness_logits". """ images = [self._move_to_current_device(x["image"]) for x in batched_inputs] images = [(x - self.pixel_mean) / self.pixel_std for x in images] images = ImageList.from_tensors( images, self.backbone.size_divisibility, padding_constraints=self.backbone.padding_constraints, ) features = self.backbone(images.tensor) if "instances" in batched_inputs[0]: gt_instances = [x["instances"].to(self.device) for x in batched_inputs] elif "targets" in batched_inputs[0]: log_first_n( logging.WARN, "'targets' in the model inputs is now renamed to 'instances'!", n=10 ) gt_instances = [x["targets"].to(self.device) for x in batched_inputs] else: gt_instances = None proposals, proposal_losses = self.proposal_generator(images, features, gt_instances) # In training, the proposals are not useful at all but we generate them anyway. # This makes RPN-only models about 5% slower. if self.training: return proposal_losses processed_results = [] for results_per_image, input_per_image, image_size in zip( proposals, batched_inputs, images.image_sizes ): height = input_per_image.get("height", image_size[0]) width = input_per_image.get("width", image_size[1]) r = detector_postprocess(results_per_image, height, width) processed_results.append({"proposals": r}) return processed_results ================================================ FILE: detectron2/detectron2/modeling/meta_arch/retinanet.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import math from typing import List, Tuple import torch from fvcore.nn import sigmoid_focal_loss_jit from torch import Tensor, nn from torch.nn import functional as F from detectron2.config import configurable from detectron2.layers import CycleBatchNormList, ShapeSpec, batched_nms, cat, get_norm from detectron2.structures import Boxes, ImageList, Instances, pairwise_iou from detectron2.utils.events import get_event_storage from ..anchor_generator import build_anchor_generator from ..backbone import Backbone, build_backbone from ..box_regression import Box2BoxTransform, _dense_box_regression_loss from ..matcher import Matcher from .build import META_ARCH_REGISTRY from .dense_detector import DenseDetector, permute_to_N_HWA_K # noqa __all__ = ["RetinaNet"] logger = logging.getLogger(__name__) @META_ARCH_REGISTRY.register() class RetinaNet(DenseDetector): """ Implement RetinaNet in :paper:`RetinaNet`. """ @configurable def __init__( self, *, backbone: Backbone, head: nn.Module, head_in_features, anchor_generator, box2box_transform, anchor_matcher, num_classes, focal_loss_alpha=0.25, focal_loss_gamma=2.0, smooth_l1_beta=0.0, box_reg_loss_type="smooth_l1", test_score_thresh=0.05, test_topk_candidates=1000, test_nms_thresh=0.5, max_detections_per_image=100, pixel_mean, pixel_std, vis_period=0, input_format="BGR", ): """ NOTE: this interface is experimental. Args: backbone: a backbone module, must follow detectron2's backbone interface head (nn.Module): a module that predicts logits and regression deltas for each level from a list of per-level features head_in_features (Tuple[str]): Names of the input feature maps to be used in head anchor_generator (nn.Module): a module that creates anchors from a list of features. Usually an instance of :class:`AnchorGenerator` box2box_transform (Box2BoxTransform): defines the transform from anchors boxes to instance boxes anchor_matcher (Matcher): label the anchors by matching them with ground truth. num_classes (int): number of classes. Used to label background proposals. # Loss parameters: focal_loss_alpha (float): focal_loss_alpha focal_loss_gamma (float): focal_loss_gamma smooth_l1_beta (float): smooth_l1_beta box_reg_loss_type (str): Options are "smooth_l1", "giou", "diou", "ciou" # Inference parameters: test_score_thresh (float): Inference cls score threshold, only anchors with score > INFERENCE_TH are considered for inference (to improve speed) test_topk_candidates (int): Select topk candidates before NMS test_nms_thresh (float): Overlap threshold used for non-maximum suppression (suppress boxes with IoU >= this threshold) max_detections_per_image (int): Maximum number of detections to return per image during inference (100 is based on the limit established for the COCO dataset). pixel_mean, pixel_std: see :class:`DenseDetector`. """ super().__init__( backbone, head, head_in_features, pixel_mean=pixel_mean, pixel_std=pixel_std ) self.num_classes = num_classes # Anchors self.anchor_generator = anchor_generator self.box2box_transform = box2box_transform self.anchor_matcher = anchor_matcher # Loss parameters: self.focal_loss_alpha = focal_loss_alpha self.focal_loss_gamma = focal_loss_gamma self.smooth_l1_beta = smooth_l1_beta self.box_reg_loss_type = box_reg_loss_type # Inference parameters: self.test_score_thresh = test_score_thresh self.test_topk_candidates = test_topk_candidates self.test_nms_thresh = test_nms_thresh self.max_detections_per_image = max_detections_per_image # Vis parameters self.vis_period = vis_period self.input_format = input_format @classmethod def from_config(cls, cfg): backbone = build_backbone(cfg) backbone_shape = backbone.output_shape() feature_shapes = [backbone_shape[f] for f in cfg.MODEL.RETINANET.IN_FEATURES] head = RetinaNetHead(cfg, feature_shapes) anchor_generator = build_anchor_generator(cfg, feature_shapes) return { "backbone": backbone, "head": head, "anchor_generator": anchor_generator, "box2box_transform": Box2BoxTransform(weights=cfg.MODEL.RETINANET.BBOX_REG_WEIGHTS), "anchor_matcher": Matcher( cfg.MODEL.RETINANET.IOU_THRESHOLDS, cfg.MODEL.RETINANET.IOU_LABELS, allow_low_quality_matches=True, ), "pixel_mean": cfg.MODEL.PIXEL_MEAN, "pixel_std": cfg.MODEL.PIXEL_STD, "num_classes": cfg.MODEL.RETINANET.NUM_CLASSES, "head_in_features": cfg.MODEL.RETINANET.IN_FEATURES, # Loss parameters: "focal_loss_alpha": cfg.MODEL.RETINANET.FOCAL_LOSS_ALPHA, "focal_loss_gamma": cfg.MODEL.RETINANET.FOCAL_LOSS_GAMMA, "smooth_l1_beta": cfg.MODEL.RETINANET.SMOOTH_L1_LOSS_BETA, "box_reg_loss_type": cfg.MODEL.RETINANET.BBOX_REG_LOSS_TYPE, # Inference parameters: "test_score_thresh": cfg.MODEL.RETINANET.SCORE_THRESH_TEST, "test_topk_candidates": cfg.MODEL.RETINANET.TOPK_CANDIDATES_TEST, "test_nms_thresh": cfg.MODEL.RETINANET.NMS_THRESH_TEST, "max_detections_per_image": cfg.TEST.DETECTIONS_PER_IMAGE, # Vis parameters "vis_period": cfg.VIS_PERIOD, "input_format": cfg.INPUT.FORMAT, } def forward_training(self, images, features, predictions, gt_instances): # Transpose the Hi*Wi*A dimension to the middle: pred_logits, pred_anchor_deltas = self._transpose_dense_predictions( predictions, [self.num_classes, 4] ) anchors = self.anchor_generator(features) gt_labels, gt_boxes = self.label_anchors(anchors, gt_instances) return self.losses(anchors, pred_logits, gt_labels, pred_anchor_deltas, gt_boxes) def losses(self, anchors, pred_logits, gt_labels, pred_anchor_deltas, gt_boxes): """ Args: anchors (list[Boxes]): a list of #feature level Boxes gt_labels, gt_boxes: see output of :meth:`RetinaNet.label_anchors`. Their shapes are (N, R) and (N, R, 4), respectively, where R is the total number of anchors across levels, i.e. sum(Hi x Wi x Ai) pred_logits, pred_anchor_deltas: both are list[Tensor]. Each element in the list corresponds to one level and has shape (N, Hi * Wi * Ai, K or 4). Where K is the number of classes used in `pred_logits`. Returns: dict[str, Tensor]: mapping from a named loss to a scalar tensor storing the loss. Used during training only. The dict keys are: "loss_cls" and "loss_box_reg" """ num_images = len(gt_labels) gt_labels = torch.stack(gt_labels) # (N, R) valid_mask = gt_labels >= 0 pos_mask = (gt_labels >= 0) & (gt_labels != self.num_classes) num_pos_anchors = pos_mask.sum().item() get_event_storage().put_scalar("num_pos_anchors", num_pos_anchors / num_images) normalizer = self._ema_update("loss_normalizer", max(num_pos_anchors, 1), 100) # classification and regression loss gt_labels_target = F.one_hot(gt_labels[valid_mask], num_classes=self.num_classes + 1)[ :, :-1 ] # no loss for the last (background) class loss_cls = sigmoid_focal_loss_jit( cat(pred_logits, dim=1)[valid_mask], gt_labels_target.to(pred_logits[0].dtype), alpha=self.focal_loss_alpha, gamma=self.focal_loss_gamma, reduction="sum", ) loss_box_reg = _dense_box_regression_loss( anchors, self.box2box_transform, pred_anchor_deltas, gt_boxes, pos_mask, box_reg_loss_type=self.box_reg_loss_type, smooth_l1_beta=self.smooth_l1_beta, ) return { "loss_cls": loss_cls / normalizer, "loss_box_reg": loss_box_reg / normalizer, } @torch.no_grad() def label_anchors(self, anchors, gt_instances): """ Args: anchors (list[Boxes]): A list of #feature level Boxes. The Boxes contains anchors of this image on the specific feature level. gt_instances (list[Instances]): a list of N `Instances`s. The i-th `Instances` contains the ground-truth per-instance annotations for the i-th input image. Returns: list[Tensor]: List of #img tensors. i-th element is a vector of labels whose length is the total number of anchors across all feature maps (sum(Hi * Wi * A)). Label values are in {-1, 0, ..., K}, with -1 means ignore, and K means background. list[Tensor]: i-th element is a Rx4 tensor, where R is the total number of anchors across feature maps. The values are the matched gt boxes for each anchor. Values are undefined for those anchors not labeled as foreground. """ anchors = Boxes.cat(anchors) # Rx4 gt_labels = [] matched_gt_boxes = [] for gt_per_image in gt_instances: match_quality_matrix = pairwise_iou(gt_per_image.gt_boxes, anchors) matched_idxs, anchor_labels = self.anchor_matcher(match_quality_matrix) del match_quality_matrix if len(gt_per_image) > 0: matched_gt_boxes_i = gt_per_image.gt_boxes.tensor[matched_idxs] gt_labels_i = gt_per_image.gt_classes[matched_idxs] # Anchors with label 0 are treated as background. gt_labels_i[anchor_labels == 0] = self.num_classes # Anchors with label -1 are ignored. gt_labels_i[anchor_labels == -1] = -1 else: matched_gt_boxes_i = torch.zeros_like(anchors.tensor) gt_labels_i = torch.zeros_like(matched_idxs) + self.num_classes gt_labels.append(gt_labels_i) matched_gt_boxes.append(matched_gt_boxes_i) return gt_labels, matched_gt_boxes def forward_inference( self, images: ImageList, features: List[Tensor], predictions: List[List[Tensor]] ): pred_logits, pred_anchor_deltas = self._transpose_dense_predictions( predictions, [self.num_classes, 4] ) anchors = self.anchor_generator(features) results: List[Instances] = [] for img_idx, image_size in enumerate(images.image_sizes): scores_per_image = [x[img_idx].sigmoid_() for x in pred_logits] deltas_per_image = [x[img_idx] for x in pred_anchor_deltas] results_per_image = self.inference_single_image( anchors, scores_per_image, deltas_per_image, image_size ) results.append(results_per_image) return results def inference_single_image( self, anchors: List[Boxes], box_cls: List[Tensor], box_delta: List[Tensor], image_size: Tuple[int, int], ): """ Single-image inference. Return bounding-box detection results by thresholding on scores and applying non-maximum suppression (NMS). Arguments: anchors (list[Boxes]): list of #feature levels. Each entry contains a Boxes object, which contains all the anchors in that feature level. box_cls (list[Tensor]): list of #feature levels. Each entry contains tensor of size (H x W x A, K) box_delta (list[Tensor]): Same shape as 'box_cls' except that K becomes 4. image_size (tuple(H, W)): a tuple of the image height and width. Returns: Same as `inference`, but for only one image. """ pred = self._decode_multi_level_predictions( anchors, box_cls, box_delta, self.test_score_thresh, self.test_topk_candidates, image_size, ) keep = batched_nms( # per-class NMS pred.pred_boxes.tensor, pred.scores, pred.pred_classes, self.test_nms_thresh ) return pred[keep[: self.max_detections_per_image]] class RetinaNetHead(nn.Module): """ The head used in RetinaNet for object classification and box regression. It has two subnets for the two tasks, with a common structure but separate parameters. """ @configurable def __init__( self, *, input_shape: List[ShapeSpec], num_classes, num_anchors, conv_dims: List[int], norm="", prior_prob=0.01, ): """ NOTE: this interface is experimental. Args: input_shape (List[ShapeSpec]): input shape num_classes (int): number of classes. Used to label background proposals. num_anchors (int): number of generated anchors conv_dims (List[int]): dimensions for each convolution layer norm (str or callable): Normalization for conv layers except for the two output layers. See :func:`detectron2.layers.get_norm` for supported types. prior_prob (float): Prior weight for computing bias """ super().__init__() self._num_features = len(input_shape) if norm == "BN" or norm == "SyncBN": logger.info( f"Using domain-specific {norm} in RetinaNetHead with len={self._num_features}." ) bn_class = nn.BatchNorm2d if norm == "BN" else nn.SyncBatchNorm def norm(c): return CycleBatchNormList( length=self._num_features, bn_class=bn_class, num_features=c ) else: norm_name = str(type(get_norm(norm, 1))) if "BN" in norm_name: logger.warning( f"Shared BatchNorm (type={norm_name}) may not work well in RetinaNetHead." ) cls_subnet = [] bbox_subnet = [] for in_channels, out_channels in zip( [input_shape[0].channels] + list(conv_dims), conv_dims ): cls_subnet.append( nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1) ) if norm: cls_subnet.append(get_norm(norm, out_channels)) cls_subnet.append(nn.ReLU()) bbox_subnet.append( nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1) ) if norm: bbox_subnet.append(get_norm(norm, out_channels)) bbox_subnet.append(nn.ReLU()) self.cls_subnet = nn.Sequential(*cls_subnet) self.bbox_subnet = nn.Sequential(*bbox_subnet) self.cls_score = nn.Conv2d( conv_dims[-1], num_anchors * num_classes, kernel_size=3, stride=1, padding=1 ) self.bbox_pred = nn.Conv2d( conv_dims[-1], num_anchors * 4, kernel_size=3, stride=1, padding=1 ) # Initialization for modules in [self.cls_subnet, self.bbox_subnet, self.cls_score, self.bbox_pred]: for layer in modules.modules(): if isinstance(layer, nn.Conv2d): torch.nn.init.normal_(layer.weight, mean=0, std=0.01) torch.nn.init.constant_(layer.bias, 0) # Use prior in model initialization to improve stability bias_value = -(math.log((1 - prior_prob) / prior_prob)) torch.nn.init.constant_(self.cls_score.bias, bias_value) @classmethod def from_config(cls, cfg, input_shape: List[ShapeSpec]): num_anchors = build_anchor_generator(cfg, input_shape).num_cell_anchors assert ( len(set(num_anchors)) == 1 ), "Using different number of anchors between levels is not currently supported!" num_anchors = num_anchors[0] return { "input_shape": input_shape, "num_classes": cfg.MODEL.RETINANET.NUM_CLASSES, "conv_dims": [input_shape[0].channels] * cfg.MODEL.RETINANET.NUM_CONVS, "prior_prob": cfg.MODEL.RETINANET.PRIOR_PROB, "norm": cfg.MODEL.RETINANET.NORM, "num_anchors": num_anchors, } def forward(self, features: List[Tensor]): """ Arguments: features (list[Tensor]): FPN feature map tensors in high to low resolution. Each tensor in the list correspond to different feature levels. Returns: logits (list[Tensor]): #lvl tensors, each has shape (N, AxK, Hi, Wi). The tensor predicts the classification probability at each spatial position for each of the A anchors and K object classes. bbox_reg (list[Tensor]): #lvl tensors, each has shape (N, Ax4, Hi, Wi). The tensor predicts 4-vector (dx,dy,dw,dh) box regression values for every anchor. These values are the relative offset between the anchor and the ground truth box. """ assert len(features) == self._num_features logits = [] bbox_reg = [] for feature in features: logits.append(self.cls_score(self.cls_subnet(feature))) bbox_reg.append(self.bbox_pred(self.bbox_subnet(feature))) return logits, bbox_reg ================================================ FILE: detectron2/detectron2/modeling/meta_arch/semantic_seg.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from typing import Callable, Dict, Optional, Tuple, Union import fvcore.nn.weight_init as weight_init import torch from torch import nn from torch.nn import functional as F from detectron2.config import configurable from detectron2.layers import Conv2d, ShapeSpec, get_norm from detectron2.structures import ImageList from detectron2.utils.registry import Registry from ..backbone import Backbone, build_backbone from ..postprocessing import sem_seg_postprocess from .build import META_ARCH_REGISTRY __all__ = [ "SemanticSegmentor", "SEM_SEG_HEADS_REGISTRY", "SemSegFPNHead", "build_sem_seg_head", ] SEM_SEG_HEADS_REGISTRY = Registry("SEM_SEG_HEADS") SEM_SEG_HEADS_REGISTRY.__doc__ = """ Registry for semantic segmentation heads, which make semantic segmentation predictions from feature maps. """ @META_ARCH_REGISTRY.register() class SemanticSegmentor(nn.Module): """ Main class for semantic segmentation architectures. """ @configurable def __init__( self, *, backbone: Backbone, sem_seg_head: nn.Module, pixel_mean: Tuple[float], pixel_std: Tuple[float], ): """ Args: backbone: a backbone module, must follow detectron2's backbone interface sem_seg_head: a module that predicts semantic segmentation from backbone features pixel_mean, pixel_std: list or tuple with #channels element, representing the per-channel mean and std to be used to normalize the input image """ super().__init__() self.backbone = backbone self.sem_seg_head = sem_seg_head self.register_buffer("pixel_mean", torch.tensor(pixel_mean).view(-1, 1, 1), False) self.register_buffer("pixel_std", torch.tensor(pixel_std).view(-1, 1, 1), False) @classmethod def from_config(cls, cfg): backbone = build_backbone(cfg) sem_seg_head = build_sem_seg_head(cfg, backbone.output_shape()) return { "backbone": backbone, "sem_seg_head": sem_seg_head, "pixel_mean": cfg.MODEL.PIXEL_MEAN, "pixel_std": cfg.MODEL.PIXEL_STD, } @property def device(self): return self.pixel_mean.device def forward(self, batched_inputs): """ Args: batched_inputs: a list, batched outputs of :class:`DatasetMapper`. Each item in the list contains the inputs for one image. For now, each item in the list is a dict that contains: * "image": Tensor, image in (C, H, W) format. * "sem_seg": semantic segmentation ground truth * Other information that's included in the original dicts, such as: "height", "width" (int): the output resolution of the model (may be different from input resolution), used in inference. Returns: list[dict]: Each dict is the output for one input image. The dict contains one key "sem_seg" whose value is a Tensor that represents the per-pixel segmentation prediced by the head. The prediction has shape KxHxW that represents the logits of each class for each pixel. """ images = [x["image"].to(self.device) for x in batched_inputs] images = [(x - self.pixel_mean) / self.pixel_std for x in images] images = ImageList.from_tensors( images, self.backbone.size_divisibility, padding_constraints=self.backbone.padding_constraints, ) features = self.backbone(images.tensor) if "sem_seg" in batched_inputs[0]: targets = [x["sem_seg"].to(self.device) for x in batched_inputs] targets = ImageList.from_tensors( targets, self.backbone.size_divisibility, self.sem_seg_head.ignore_value, self.backbone.padding_constraints, ).tensor else: targets = None results, losses = self.sem_seg_head(features, targets) if self.training: return losses processed_results = [] for result, input_per_image, image_size in zip(results, batched_inputs, images.image_sizes): height = input_per_image.get("height", image_size[0]) width = input_per_image.get("width", image_size[1]) r = sem_seg_postprocess(result, image_size, height, width) processed_results.append({"sem_seg": r}) return processed_results def build_sem_seg_head(cfg, input_shape): """ Build a semantic segmentation head from `cfg.MODEL.SEM_SEG_HEAD.NAME`. """ name = cfg.MODEL.SEM_SEG_HEAD.NAME return SEM_SEG_HEADS_REGISTRY.get(name)(cfg, input_shape) @SEM_SEG_HEADS_REGISTRY.register() class SemSegFPNHead(nn.Module): """ A semantic segmentation head described in :paper:`PanopticFPN`. It takes a list of FPN features as input, and applies a sequence of 3x3 convs and upsampling to scale all of them to the stride defined by ``common_stride``. Then these features are added and used to make final predictions by another 1x1 conv layer. """ @configurable def __init__( self, input_shape: Dict[str, ShapeSpec], *, num_classes: int, conv_dims: int, common_stride: int, loss_weight: float = 1.0, norm: Optional[Union[str, Callable]] = None, ignore_value: int = -1, ): """ NOTE: this interface is experimental. Args: input_shape: shapes (channels and stride) of the input features num_classes: number of classes to predict conv_dims: number of output channels for the intermediate conv layers. common_stride: the common stride that all features will be upscaled to loss_weight: loss weight norm (str or callable): normalization for all conv layers ignore_value: category id to be ignored during training. """ super().__init__() input_shape = sorted(input_shape.items(), key=lambda x: x[1].stride) if not len(input_shape): raise ValueError("SemSegFPNHead(input_shape=) cannot be empty!") self.in_features = [k for k, v in input_shape] feature_strides = [v.stride for k, v in input_shape] feature_channels = [v.channels for k, v in input_shape] self.ignore_value = ignore_value self.common_stride = common_stride self.loss_weight = loss_weight self.scale_heads = [] for in_feature, stride, channels in zip( self.in_features, feature_strides, feature_channels ): head_ops = [] head_length = max(1, int(np.log2(stride) - np.log2(self.common_stride))) for k in range(head_length): norm_module = get_norm(norm, conv_dims) conv = Conv2d( channels if k == 0 else conv_dims, conv_dims, kernel_size=3, stride=1, padding=1, bias=not norm, norm=norm_module, activation=F.relu, ) weight_init.c2_msra_fill(conv) head_ops.append(conv) if stride != self.common_stride: head_ops.append( nn.Upsample(scale_factor=2, mode="bilinear", align_corners=False) ) self.scale_heads.append(nn.Sequential(*head_ops)) self.add_module(in_feature, self.scale_heads[-1]) self.predictor = Conv2d(conv_dims, num_classes, kernel_size=1, stride=1, padding=0) weight_init.c2_msra_fill(self.predictor) @classmethod def from_config(cls, cfg, input_shape: Dict[str, ShapeSpec]): return { "input_shape": { k: v for k, v in input_shape.items() if k in cfg.MODEL.SEM_SEG_HEAD.IN_FEATURES }, "ignore_value": cfg.MODEL.SEM_SEG_HEAD.IGNORE_VALUE, "num_classes": cfg.MODEL.SEM_SEG_HEAD.NUM_CLASSES, "conv_dims": cfg.MODEL.SEM_SEG_HEAD.CONVS_DIM, "common_stride": cfg.MODEL.SEM_SEG_HEAD.COMMON_STRIDE, "norm": cfg.MODEL.SEM_SEG_HEAD.NORM, "loss_weight": cfg.MODEL.SEM_SEG_HEAD.LOSS_WEIGHT, } def forward(self, features, targets=None): """ Returns: In training, returns (None, dict of losses) In inference, returns (CxHxW logits, {}) """ x = self.layers(features) if self.training: return None, self.losses(x, targets) else: x = F.interpolate( x, scale_factor=self.common_stride, mode="bilinear", align_corners=False ) return x, {} def layers(self, features): for i, f in enumerate(self.in_features): if i == 0: x = self.scale_heads[i](features[f]) else: x = x + self.scale_heads[i](features[f]) x = self.predictor(x) return x def losses(self, predictions, targets): predictions = predictions.float() # https://github.com/pytorch/pytorch/issues/48163 predictions = F.interpolate( predictions, scale_factor=self.common_stride, mode="bilinear", align_corners=False, ) loss = F.cross_entropy( predictions, targets, reduction="mean", ignore_index=self.ignore_value ) losses = {"loss_sem_seg": loss * self.loss_weight} return losses ================================================ FILE: detectron2/detectron2/modeling/mmdet_wrapper.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import itertools import logging import numpy as np from collections import OrderedDict from collections.abc import Mapping from typing import Dict, List, Optional, Tuple, Union import torch from omegaconf import DictConfig, OmegaConf from torch import Tensor, nn from detectron2.layers import ShapeSpec from detectron2.structures import BitMasks, Boxes, ImageList, Instances from detectron2.utils.events import get_event_storage from .backbone import Backbone logger = logging.getLogger(__name__) def _to_container(cfg): """ mmdet will assert the type of dict/list. So convert omegaconf objects to dict/list. """ if isinstance(cfg, DictConfig): cfg = OmegaConf.to_container(cfg, resolve=True) from mmcv.utils import ConfigDict return ConfigDict(cfg) class MMDetBackbone(Backbone): """ Wrapper of mmdetection backbones to use in detectron2. mmdet backbones produce list/tuple of tensors, while detectron2 backbones produce a dict of tensors. This class wraps the given backbone to produce output in detectron2's convention, so it can be used in place of detectron2 backbones. """ def __init__( self, backbone: Union[nn.Module, Mapping], neck: Union[nn.Module, Mapping, None] = None, *, output_shapes: List[ShapeSpec], output_names: Optional[List[str]] = None, ): """ Args: backbone: either a backbone module or a mmdet config dict that defines a backbone. The backbone takes a 4D image tensor and returns a sequence of tensors. neck: either a backbone module or a mmdet config dict that defines a neck. The neck takes outputs of backbone and returns a sequence of tensors. If None, no neck is used. output_shapes: shape for every output of the backbone (or neck, if given). stride and channels are often needed. output_names: names for every output of the backbone (or neck, if given). By default, will use "out0", "out1", ... """ super().__init__() if isinstance(backbone, Mapping): from mmdet.models import build_backbone backbone = build_backbone(_to_container(backbone)) self.backbone = backbone if isinstance(neck, Mapping): from mmdet.models import build_neck neck = build_neck(_to_container(neck)) self.neck = neck # "Neck" weights, if any, are part of neck itself. This is the interface # of mmdet so we follow it. Reference: # https://github.com/open-mmlab/mmdetection/blob/master/mmdet/models/detectors/two_stage.py logger.info("Initializing mmdet backbone weights...") self.backbone.init_weights() # train() in mmdet modules is non-trivial, and has to be explicitly # called. Reference: # https://github.com/open-mmlab/mmdetection/blob/master/mmdet/models/backbones/resnet.py self.backbone.train() if self.neck is not None: logger.info("Initializing mmdet neck weights ...") if isinstance(self.neck, nn.Sequential): for m in self.neck: m.init_weights() else: self.neck.init_weights() self.neck.train() self._output_shapes = output_shapes if not output_names: output_names = [f"out{i}" for i in range(len(output_shapes))] self._output_names = output_names def forward(self, x) -> Dict[str, Tensor]: outs = self.backbone(x) if self.neck is not None: outs = self.neck(outs) assert isinstance( outs, (list, tuple) ), "mmdet backbone should return a list/tuple of tensors!" if len(outs) != len(self._output_shapes): raise ValueError( "Length of output_shapes does not match outputs from the mmdet backbone: " f"{len(outs)} != {len(self._output_shapes)}" ) return {k: v for k, v in zip(self._output_names, outs)} def output_shape(self) -> Dict[str, ShapeSpec]: return {k: v for k, v in zip(self._output_names, self._output_shapes)} class MMDetDetector(nn.Module): """ Wrapper of a mmdetection detector model, for detection and instance segmentation. Input/output formats of this class follow detectron2's convention, so a mmdetection model can be trained and evaluated in detectron2. """ def __init__( self, detector: Union[nn.Module, Mapping], *, # Default is 32 regardless of model: # https://github.com/open-mmlab/mmdetection/tree/master/configs/_base_/datasets size_divisibility=32, pixel_mean: Tuple[float], pixel_std: Tuple[float], ): """ Args: detector: a mmdet detector, or a mmdet config dict that defines a detector. size_divisibility: pad input images to multiple of this number pixel_mean: per-channel mean to normalize input image pixel_std: per-channel stddev to normalize input image """ super().__init__() if isinstance(detector, Mapping): from mmdet.models import build_detector detector = build_detector(_to_container(detector)) self.detector = detector self.size_divisibility = size_divisibility self.register_buffer("pixel_mean", torch.tensor(pixel_mean).view(-1, 1, 1), False) self.register_buffer("pixel_std", torch.tensor(pixel_std).view(-1, 1, 1), False) assert ( self.pixel_mean.shape == self.pixel_std.shape ), f"{self.pixel_mean} and {self.pixel_std} have different shapes!" def forward(self, batched_inputs: List[Dict[str, torch.Tensor]]): images = [x["image"].to(self.device) for x in batched_inputs] images = [(x - self.pixel_mean) / self.pixel_std for x in images] images = ImageList.from_tensors(images, size_divisibility=self.size_divisibility).tensor metas = [] rescale = {"height" in x for x in batched_inputs} if len(rescale) != 1: raise ValueError("Some inputs have original height/width, but some don't!") rescale = list(rescale)[0] output_shapes = [] for input in batched_inputs: meta = {} c, h, w = input["image"].shape meta["img_shape"] = meta["ori_shape"] = (h, w, c) if rescale: scale_factor = np.array( [w / input["width"], h / input["height"]] * 2, dtype="float32" ) ori_shape = (input["height"], input["width"]) output_shapes.append(ori_shape) meta["ori_shape"] = ori_shape + (c,) else: scale_factor = 1.0 output_shapes.append((h, w)) meta["scale_factor"] = scale_factor meta["flip"] = False padh, padw = images.shape[-2:] meta["pad_shape"] = (padh, padw, c) metas.append(meta) if self.training: gt_instances = [x["instances"].to(self.device) for x in batched_inputs] if gt_instances[0].has("gt_masks"): from mmdet.core import PolygonMasks as mm_PolygonMasks, BitmapMasks as mm_BitMasks def convert_mask(m, shape): # mmdet mask format if isinstance(m, BitMasks): return mm_BitMasks(m.tensor.cpu().numpy(), shape[0], shape[1]) else: return mm_PolygonMasks(m.polygons, shape[0], shape[1]) gt_masks = [convert_mask(x.gt_masks, x.image_size) for x in gt_instances] losses_and_metrics = self.detector.forward_train( images, metas, [x.gt_boxes.tensor for x in gt_instances], [x.gt_classes for x in gt_instances], gt_masks=gt_masks, ) else: losses_and_metrics = self.detector.forward_train( images, metas, [x.gt_boxes.tensor for x in gt_instances], [x.gt_classes for x in gt_instances], ) return _parse_losses(losses_and_metrics) else: results = self.detector.simple_test(images, metas, rescale=rescale) results = [ {"instances": _convert_mmdet_result(r, shape)} for r, shape in zip(results, output_shapes) ] return results @property def device(self): return self.pixel_mean.device # Reference: show_result() in # https://github.com/open-mmlab/mmdetection/blob/master/mmdet/models/detectors/base.py def _convert_mmdet_result(result, shape: Tuple[int, int]) -> Instances: if isinstance(result, tuple): bbox_result, segm_result = result if isinstance(segm_result, tuple): segm_result = segm_result[0] else: bbox_result, segm_result = result, None bboxes = torch.from_numpy(np.vstack(bbox_result)) # Nx5 bboxes, scores = bboxes[:, :4], bboxes[:, -1] labels = [ torch.full((bbox.shape[0],), i, dtype=torch.int32) for i, bbox in enumerate(bbox_result) ] labels = torch.cat(labels) inst = Instances(shape) inst.pred_boxes = Boxes(bboxes) inst.scores = scores inst.pred_classes = labels if segm_result is not None and len(labels) > 0: segm_result = list(itertools.chain(*segm_result)) segm_result = [torch.from_numpy(x) if isinstance(x, np.ndarray) else x for x in segm_result] segm_result = torch.stack(segm_result, dim=0) inst.pred_masks = segm_result return inst # reference: https://github.com/open-mmlab/mmdetection/blob/master/mmdet/models/detectors/base.py def _parse_losses(losses: Dict[str, Tensor]) -> Dict[str, Tensor]: log_vars = OrderedDict() for loss_name, loss_value in losses.items(): if isinstance(loss_value, torch.Tensor): log_vars[loss_name] = loss_value.mean() elif isinstance(loss_value, list): log_vars[loss_name] = sum(_loss.mean() for _loss in loss_value) else: raise TypeError(f"{loss_name} is not a tensor or list of tensors") if "loss" not in loss_name: # put metrics to storage; don't return them storage = get_event_storage() value = log_vars.pop(loss_name).cpu().item() storage.put_scalar(loss_name, value) return log_vars ================================================ FILE: detectron2/detectron2/modeling/poolers.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import math from typing import List, Optional import torch from torch import nn from torchvision.ops import RoIPool from detectron2.layers import ROIAlign, ROIAlignRotated, cat, nonzero_tuple, shapes_to_tensor from detectron2.structures import Boxes """ To export ROIPooler to torchscript, in this file, variables that should be annotated with `Union[List[Boxes], List[RotatedBoxes]]` are only annotated with `List[Boxes]`. TODO: Correct these annotations when torchscript support `Union`. https://github.com/pytorch/pytorch/issues/41412 """ __all__ = ["ROIPooler"] def assign_boxes_to_levels( box_lists: List[Boxes], min_level: int, max_level: int, canonical_box_size: int, canonical_level: int, ): """ Map each box in `box_lists` to a feature map level index and return the assignment vector. Args: box_lists (list[Boxes] | list[RotatedBoxes]): A list of N Boxes or N RotatedBoxes, where N is the number of images in the batch. min_level (int): Smallest feature map level index. The input is considered index 0, the output of stage 1 is index 1, and so. max_level (int): Largest feature map level index. canonical_box_size (int): A canonical box size in pixels (sqrt(box area)). canonical_level (int): The feature map level index on which a canonically-sized box should be placed. Returns: A tensor of length M, where M is the total number of boxes aggregated over all N batch images. The memory layout corresponds to the concatenation of boxes from all images. Each element is the feature map index, as an offset from `self.min_level`, for the corresponding box (so value i means the box is at `self.min_level + i`). """ box_sizes = torch.sqrt(cat([boxes.area() for boxes in box_lists])) # Eqn.(1) in FPN paper level_assignments = torch.floor( canonical_level + torch.log2(box_sizes / canonical_box_size + 1e-8) ) # clamp level to (min, max), in case the box size is too large or too small # for the available feature maps level_assignments = torch.clamp(level_assignments, min=min_level, max=max_level) return level_assignments.to(torch.int64) - min_level # script the module to avoid hardcoded device type @torch.jit.script_if_tracing def _convert_boxes_to_pooler_format(boxes: torch.Tensor, sizes: torch.Tensor) -> torch.Tensor: sizes = sizes.to(device=boxes.device) indices = torch.repeat_interleave( torch.arange(len(sizes), dtype=boxes.dtype, device=boxes.device), sizes ) return cat([indices[:, None], boxes], dim=1) def convert_boxes_to_pooler_format(box_lists: List[Boxes]): """ Convert all boxes in `box_lists` to the low-level format used by ROI pooling ops (see description under Returns). Args: box_lists (list[Boxes] | list[RotatedBoxes]): A list of N Boxes or N RotatedBoxes, where N is the number of images in the batch. Returns: When input is list[Boxes]: A tensor of shape (M, 5), where M is the total number of boxes aggregated over all N batch images. The 5 columns are (batch index, x0, y0, x1, y1), where batch index is the index in [0, N) identifying which batch image the box with corners at (x0, y0, x1, y1) comes from. When input is list[RotatedBoxes]: A tensor of shape (M, 6), where M is the total number of boxes aggregated over all N batch images. The 6 columns are (batch index, x_ctr, y_ctr, width, height, angle_degrees), where batch index is the index in [0, N) identifying which batch image the rotated box (x_ctr, y_ctr, width, height, angle_degrees) comes from. """ boxes = torch.cat([x.tensor for x in box_lists], dim=0) # __len__ returns Tensor in tracing. sizes = shapes_to_tensor([x.__len__() for x in box_lists]) return _convert_boxes_to_pooler_format(boxes, sizes) @torch.jit.script_if_tracing def _create_zeros( batch_target: Optional[torch.Tensor], channels: int, height: int, width: int, like_tensor: torch.Tensor, ) -> torch.Tensor: batches = batch_target.shape[0] if batch_target is not None else 0 sizes = (batches, channels, height, width) return torch.zeros(sizes, dtype=like_tensor.dtype, device=like_tensor.device) class ROIPooler(nn.Module): """ Region of interest feature map pooler that supports pooling from one or more feature maps. """ def __init__( self, output_size, scales, sampling_ratio, pooler_type, canonical_box_size=224, canonical_level=4, ): """ Args: output_size (int, tuple[int] or list[int]): output size of the pooled region, e.g., 14 x 14. If tuple or list is given, the length must be 2. scales (list[float]): The scale for each low-level pooling op relative to the input image. For a feature map with stride s relative to the input image, scale is defined as 1/s. The stride must be power of 2. When there are multiple scales, they must form a pyramid, i.e. they must be a monotically decreasing geometric sequence with a factor of 1/2. sampling_ratio (int): The `sampling_ratio` parameter for the ROIAlign op. pooler_type (string): Name of the type of pooling operation that should be applied. For instance, "ROIPool" or "ROIAlignV2". canonical_box_size (int): A canonical box size in pixels (sqrt(box area)). The default is heuristically defined as 224 pixels in the FPN paper (based on ImageNet pre-training). canonical_level (int): The feature map level index from which a canonically-sized box should be placed. The default is defined as level 4 (stride=16) in the FPN paper, i.e., a box of size 224x224 will be placed on the feature with stride=16. The box placement for all boxes will be determined from their sizes w.r.t canonical_box_size. For example, a box whose area is 4x that of a canonical box should be used to pool features from feature level ``canonical_level+1``. Note that the actual input feature maps given to this module may not have sufficiently many levels for the input boxes. If the boxes are too large or too small for the input feature maps, the closest level will be used. """ super().__init__() if isinstance(output_size, int): output_size = (output_size, output_size) assert len(output_size) == 2 assert isinstance(output_size[0], int) and isinstance(output_size[1], int) self.output_size = output_size if pooler_type == "ROIAlign": self.level_poolers = nn.ModuleList( ROIAlign( output_size, spatial_scale=scale, sampling_ratio=sampling_ratio, aligned=False ) for scale in scales ) elif pooler_type == "ROIAlignV2": self.level_poolers = nn.ModuleList( ROIAlign( output_size, spatial_scale=scale, sampling_ratio=sampling_ratio, aligned=True ) for scale in scales ) elif pooler_type == "ROIPool": self.level_poolers = nn.ModuleList( RoIPool(output_size, spatial_scale=scale) for scale in scales ) elif pooler_type == "ROIAlignRotated": self.level_poolers = nn.ModuleList( ROIAlignRotated(output_size, spatial_scale=scale, sampling_ratio=sampling_ratio) for scale in scales ) else: raise ValueError("Unknown pooler type: {}".format(pooler_type)) # Map scale (defined as 1 / stride) to its feature map level under the # assumption that stride is a power of 2. min_level = -(math.log2(scales[0])) max_level = -(math.log2(scales[-1])) assert math.isclose(min_level, int(min_level)) and math.isclose( max_level, int(max_level) ), "Featuremap stride is not power of 2!" self.min_level = int(min_level) self.max_level = int(max_level) assert ( len(scales) == self.max_level - self.min_level + 1 ), "[ROIPooler] Sizes of input featuremaps do not form a pyramid!" assert 0 <= self.min_level and self.min_level <= self.max_level self.canonical_level = canonical_level assert canonical_box_size > 0 self.canonical_box_size = canonical_box_size def forward(self, x: List[torch.Tensor], box_lists: List[Boxes]): """ Args: x (list[Tensor]): A list of feature maps of NCHW shape, with scales matching those used to construct this module. box_lists (list[Boxes] | list[RotatedBoxes]): A list of N Boxes or N RotatedBoxes, where N is the number of images in the batch. The box coordinates are defined on the original image and will be scaled by the `scales` argument of :class:`ROIPooler`. Returns: Tensor: A tensor of shape (M, C, output_size, output_size) where M is the total number of boxes aggregated over all N batch images and C is the number of channels in `x`. """ num_level_assignments = len(self.level_poolers) assert isinstance(x, list) and isinstance( box_lists, list ), "Arguments to pooler must be lists" assert ( len(x) == num_level_assignments ), "unequal value, num_level_assignments={}, but x is list of {} Tensors".format( num_level_assignments, len(x) ) assert len(box_lists) == x[0].size( 0 ), "unequal value, x[0] batch dim 0 is {}, but box_list has length {}".format( x[0].size(0), len(box_lists) ) if len(box_lists) == 0: return _create_zeros(None, x[0].shape[1], *self.output_size, x[0]) pooler_fmt_boxes = convert_boxes_to_pooler_format(box_lists) if num_level_assignments == 1: return self.level_poolers[0](x[0], pooler_fmt_boxes) level_assignments = assign_boxes_to_levels( box_lists, self.min_level, self.max_level, self.canonical_box_size, self.canonical_level ) num_channels = x[0].shape[1] output_size = self.output_size[0] output = _create_zeros(pooler_fmt_boxes, num_channels, output_size, output_size, x[0]) for level, pooler in enumerate(self.level_poolers): inds = nonzero_tuple(level_assignments == level)[0] pooler_fmt_boxes_level = pooler_fmt_boxes[inds] # Use index_put_ instead of advance indexing, to avoid pytorch/issues/49852 output.index_put_((inds,), pooler(x[level], pooler_fmt_boxes_level)) return output ================================================ FILE: detectron2/detectron2/modeling/postprocessing.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch from torch.nn import functional as F from detectron2.structures import Instances, ROIMasks # perhaps should rename to "resize_instance" def detector_postprocess( results: Instances, output_height: int, output_width: int, mask_threshold: float = 0.5 ): """ Resize the output instances. The input images are often resized when entering an object detector. As a result, we often need the outputs of the detector in a different resolution from its inputs. This function will resize the raw outputs of an R-CNN detector to produce outputs according to the desired output resolution. Args: results (Instances): the raw outputs from the detector. `results.image_size` contains the input image resolution the detector sees. This object might be modified in-place. output_height, output_width: the desired output resolution. Returns: Instances: the resized output from the model, based on the output resolution """ if isinstance(output_width, torch.Tensor): # This shape might (but not necessarily) be tensors during tracing. # Converts integer tensors to float temporaries to ensure true # division is performed when computing scale_x and scale_y. output_width_tmp = output_width.float() output_height_tmp = output_height.float() new_size = torch.stack([output_height, output_width]) else: new_size = (output_height, output_width) output_width_tmp = output_width output_height_tmp = output_height scale_x, scale_y = ( output_width_tmp / results.image_size[1], output_height_tmp / results.image_size[0], ) results = Instances(new_size, **results.get_fields()) if results.has("pred_boxes"): output_boxes = results.pred_boxes elif results.has("proposal_boxes"): output_boxes = results.proposal_boxes else: output_boxes = None assert output_boxes is not None, "Predictions must contain boxes!" output_boxes.scale(scale_x, scale_y) output_boxes.clip(results.image_size) results = results[output_boxes.nonempty()] if results.has("pred_masks"): if isinstance(results.pred_masks, ROIMasks): roi_masks = results.pred_masks else: # pred_masks is a tensor of shape (N, 1, M, M) roi_masks = ROIMasks(results.pred_masks[:, 0, :, :]) results.pred_masks = roi_masks.to_bitmasks( results.pred_boxes, output_height, output_width, mask_threshold ).tensor # TODO return ROIMasks/BitMask object in the future if results.has("pred_keypoints"): results.pred_keypoints[:, :, 0] *= scale_x results.pred_keypoints[:, :, 1] *= scale_y return results def sem_seg_postprocess(result, img_size, output_height, output_width): """ Return semantic segmentation predictions in the original resolution. The input images are often resized when entering semantic segmentor. Moreover, in same cases, they also padded inside segmentor to be divisible by maximum network stride. As a result, we often need the predictions of the segmentor in a different resolution from its inputs. Args: result (Tensor): semantic segmentation prediction logits. A tensor of shape (C, H, W), where C is the number of classes, and H, W are the height and width of the prediction. img_size (tuple): image size that segmentor is taking as input. output_height, output_width: the desired output resolution. Returns: semantic segmentation prediction (Tensor): A tensor of the shape (C, output_height, output_width) that contains per-pixel soft predictions. """ result = result[:, : img_size[0], : img_size[1]].expand(1, -1, -1, -1) result = F.interpolate( result, size=(output_height, output_width), mode="bilinear", align_corners=False )[0] return result ================================================ FILE: detectron2/detectron2/modeling/proposal_generator/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .build import PROPOSAL_GENERATOR_REGISTRY, build_proposal_generator from .rpn import RPN_HEAD_REGISTRY, build_rpn_head, RPN, StandardRPNHead __all__ = list(globals().keys()) ================================================ FILE: detectron2/detectron2/modeling/proposal_generator/build.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.utils.registry import Registry PROPOSAL_GENERATOR_REGISTRY = Registry("PROPOSAL_GENERATOR") PROPOSAL_GENERATOR_REGISTRY.__doc__ = """ Registry for proposal generator, which produces object proposals from feature maps. The registered object will be called with `obj(cfg, input_shape)`. The call should return a `nn.Module` object. """ from . import rpn, rrpn # noqa F401 isort:skip def build_proposal_generator(cfg, input_shape): """ Build a proposal generator from `cfg.MODEL.PROPOSAL_GENERATOR.NAME`. The name can be "PrecomputedProposals" to use no proposal generator. """ name = cfg.MODEL.PROPOSAL_GENERATOR.NAME if name == "PrecomputedProposals": return None return PROPOSAL_GENERATOR_REGISTRY.get(name)(cfg, input_shape) ================================================ FILE: detectron2/detectron2/modeling/proposal_generator/proposal_utils.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import math from typing import List, Tuple, Union import torch from detectron2.layers import batched_nms, cat, move_device_like from detectron2.structures import Boxes, Instances logger = logging.getLogger(__name__) def _is_tracing(): # (fixed in TORCH_VERSION >= 1.9) if torch.jit.is_scripting(): # https://github.com/pytorch/pytorch/issues/47379 return False else: return torch.jit.is_tracing() def find_top_rpn_proposals( proposals: List[torch.Tensor], pred_objectness_logits: List[torch.Tensor], image_sizes: List[Tuple[int, int]], nms_thresh: float, pre_nms_topk: int, post_nms_topk: int, min_box_size: float, training: bool, ): """ For each feature map, select the `pre_nms_topk` highest scoring proposals, apply NMS, clip proposals, and remove small boxes. Return the `post_nms_topk` highest scoring proposals among all the feature maps for each image. Args: proposals (list[Tensor]): A list of L tensors. Tensor i has shape (N, Hi*Wi*A, 4). All proposal predictions on the feature maps. pred_objectness_logits (list[Tensor]): A list of L tensors. Tensor i has shape (N, Hi*Wi*A). image_sizes (list[tuple]): sizes (h, w) for each image nms_thresh (float): IoU threshold to use for NMS pre_nms_topk (int): number of top k scoring proposals to keep before applying NMS. When RPN is run on multiple feature maps (as in FPN) this number is per feature map. post_nms_topk (int): number of top k scoring proposals to keep after applying NMS. When RPN is run on multiple feature maps (as in FPN) this number is total, over all feature maps. min_box_size (float): minimum proposal box side length in pixels (absolute units wrt input images). training (bool): True if proposals are to be used in training, otherwise False. This arg exists only to support a legacy bug; look for the "NB: Legacy bug ..." comment. Returns: list[Instances]: list of N Instances. The i-th Instances stores post_nms_topk object proposals for image i, sorted by their objectness score in descending order. """ num_images = len(image_sizes) device = ( proposals[0].device if torch.jit.is_scripting() else ("cpu" if torch.jit.is_tracing() else proposals[0].device) ) # 1. Select top-k anchor for every level and every image topk_scores = [] # #lvl Tensor, each of shape N x topk topk_proposals = [] level_ids = [] # #lvl Tensor, each of shape (topk,) batch_idx = move_device_like(torch.arange(num_images, device=device), proposals[0]) for level_id, (proposals_i, logits_i) in enumerate(zip(proposals, pred_objectness_logits)): Hi_Wi_A = logits_i.shape[1] if isinstance(Hi_Wi_A, torch.Tensor): # it's a tensor in tracing num_proposals_i = torch.clamp(Hi_Wi_A, max=pre_nms_topk) else: num_proposals_i = min(Hi_Wi_A, pre_nms_topk) topk_scores_i, topk_idx = logits_i.topk(num_proposals_i, dim=1) # each is N x topk topk_proposals_i = proposals_i[batch_idx[:, None], topk_idx] # N x topk x 4 topk_proposals.append(topk_proposals_i) topk_scores.append(topk_scores_i) level_ids.append( move_device_like( torch.full((num_proposals_i,), level_id, dtype=torch.int64, device=device), proposals[0], ) ) # 2. Concat all levels together topk_scores = cat(topk_scores, dim=1) topk_proposals = cat(topk_proposals, dim=1) level_ids = cat(level_ids, dim=0) # 3. For each image, run a per-level NMS, and choose topk results. results: List[Instances] = [] for n, image_size in enumerate(image_sizes): boxes = Boxes(topk_proposals[n]) scores_per_img = topk_scores[n] lvl = level_ids valid_mask = torch.isfinite(boxes.tensor).all(dim=1) & torch.isfinite(scores_per_img) if not valid_mask.all(): if training: raise FloatingPointError( "Predicted boxes or scores contain Inf/NaN. Training has diverged." ) boxes = boxes[valid_mask] scores_per_img = scores_per_img[valid_mask] lvl = lvl[valid_mask] boxes.clip(image_size) # filter empty boxes keep = boxes.nonempty(threshold=min_box_size) if _is_tracing() or keep.sum().item() != len(boxes): boxes, scores_per_img, lvl = boxes[keep], scores_per_img[keep], lvl[keep] keep = batched_nms(boxes.tensor, scores_per_img, lvl, nms_thresh) # In Detectron1, there was different behavior during training vs. testing. # (https://github.com/facebookresearch/Detectron/issues/459) # During training, topk is over the proposals from *all* images in the training batch. # During testing, it is over the proposals for each image separately. # As a result, the training behavior becomes batch-dependent, # and the configuration "POST_NMS_TOPK_TRAIN" end up relying on the batch size. # This bug is addressed in Detectron2 to make the behavior independent of batch size. keep = keep[:post_nms_topk] # keep is already sorted res = Instances(image_size) res.proposal_boxes = boxes[keep] res.objectness_logits = scores_per_img[keep] results.append(res) return results def add_ground_truth_to_proposals( gt: Union[List[Instances], List[Boxes]], proposals: List[Instances] ) -> List[Instances]: """ Call `add_ground_truth_to_proposals_single_image` for all images. Args: gt(Union[List[Instances], List[Boxes]): list of N elements. Element i is a Instances representing the ground-truth for image i. proposals (list[Instances]): list of N elements. Element i is a Instances representing the proposals for image i. Returns: list[Instances]: list of N Instances. Each is the proposals for the image, with field "proposal_boxes" and "objectness_logits". """ assert gt is not None if len(proposals) != len(gt): raise ValueError("proposals and gt should have the same length as the number of images!") if len(proposals) == 0: return proposals return [ add_ground_truth_to_proposals_single_image(gt_i, proposals_i) for gt_i, proposals_i in zip(gt, proposals) ] def add_ground_truth_to_proposals_single_image( gt: Union[Instances, Boxes], proposals: Instances ) -> Instances: """ Augment `proposals` with `gt`. Args: Same as `add_ground_truth_to_proposals`, but with gt and proposals per image. Returns: Same as `add_ground_truth_to_proposals`, but for only one image. """ if isinstance(gt, Boxes): # convert Boxes to Instances gt = Instances(proposals.image_size, gt_boxes=gt) gt_boxes = gt.gt_boxes device = proposals.objectness_logits.device # Assign all ground-truth boxes an objectness logit corresponding to # P(object) = sigmoid(logit) =~ 1. gt_logit_value = math.log((1.0 - 1e-10) / (1 - (1.0 - 1e-10))) gt_logits = gt_logit_value * torch.ones(len(gt_boxes), device=device) # Concatenating gt_boxes with proposals requires them to have the same fields gt_proposal = Instances(proposals.image_size, **gt.get_fields()) gt_proposal.proposal_boxes = gt_boxes gt_proposal.objectness_logits = gt_logits for key in proposals.get_fields().keys(): assert gt_proposal.has( key ), "The attribute '{}' in `proposals` does not exist in `gt`".format(key) # NOTE: Instances.cat only use fields from the first item. Extra fields in latter items # will be thrown away. new_proposals = Instances.cat([proposals, gt_proposal]) return new_proposals ================================================ FILE: detectron2/detectron2/modeling/proposal_generator/rpn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Dict, List, Optional, Tuple, Union import torch import torch.nn.functional as F from torch import nn from detectron2.config import configurable from detectron2.layers import Conv2d, ShapeSpec, cat from detectron2.structures import Boxes, ImageList, Instances, pairwise_iou from detectron2.utils.events import get_event_storage from detectron2.utils.memory import retry_if_cuda_oom from detectron2.utils.registry import Registry from ..anchor_generator import build_anchor_generator from ..box_regression import Box2BoxTransform, _dense_box_regression_loss from ..matcher import Matcher from ..sampling import subsample_labels from .build import PROPOSAL_GENERATOR_REGISTRY from .proposal_utils import find_top_rpn_proposals RPN_HEAD_REGISTRY = Registry("RPN_HEAD") RPN_HEAD_REGISTRY.__doc__ = """ Registry for RPN heads, which take feature maps and perform objectness classification and bounding box regression for anchors. The registered object will be called with `obj(cfg, input_shape)`. The call should return a `nn.Module` object. """ """ Shape shorthand in this module: N: number of images in the minibatch L: number of feature maps per image on which RPN is run A: number of cell anchors (must be the same for all feature maps) Hi, Wi: height and width of the i-th feature map B: size of the box parameterization Naming convention: objectness: refers to the binary classification of an anchor as object vs. not object. deltas: refers to the 4-d (dx, dy, dw, dh) deltas that parameterize the box2box transform (see :class:`box_regression.Box2BoxTransform`), or 5d for rotated boxes. pred_objectness_logits: predicted objectness scores in [-inf, +inf]; use sigmoid(pred_objectness_logits) to estimate P(object). gt_labels: ground-truth binary classification labels for objectness pred_anchor_deltas: predicted box2box transform deltas gt_anchor_deltas: ground-truth box2box transform deltas """ def build_rpn_head(cfg, input_shape): """ Build an RPN head defined by `cfg.MODEL.RPN.HEAD_NAME`. """ name = cfg.MODEL.RPN.HEAD_NAME return RPN_HEAD_REGISTRY.get(name)(cfg, input_shape) @RPN_HEAD_REGISTRY.register() class StandardRPNHead(nn.Module): """ Standard RPN classification and regression heads described in :paper:`Faster R-CNN`. Uses a 3x3 conv to produce a shared hidden state from which one 1x1 conv predicts objectness logits for each anchor and a second 1x1 conv predicts bounding-box deltas specifying how to deform each anchor into an object proposal. """ @configurable def __init__( self, *, in_channels: int, num_anchors: int, box_dim: int = 4, conv_dims: List[int] = (-1,) ): """ NOTE: this interface is experimental. Args: in_channels (int): number of input feature channels. When using multiple input features, they must have the same number of channels. num_anchors (int): number of anchors to predict for *each spatial position* on the feature map. The total number of anchors for each feature map will be `num_anchors * H * W`. box_dim (int): dimension of a box, which is also the number of box regression predictions to make for each anchor. An axis aligned box has box_dim=4, while a rotated box has box_dim=5. conv_dims (list[int]): a list of integers representing the output channels of N conv layers. Set it to -1 to use the same number of output channels as input channels. """ super().__init__() cur_channels = in_channels # Keeping the old variable names and structure for backwards compatiblity. # Otherwise the old checkpoints will fail to load. if len(conv_dims) == 1: out_channels = cur_channels if conv_dims[0] == -1 else conv_dims[0] # 3x3 conv for the hidden representation self.conv = self._get_rpn_conv(cur_channels, out_channels) cur_channels = out_channels else: self.conv = nn.Sequential() for k, conv_dim in enumerate(conv_dims): out_channels = cur_channels if conv_dim == -1 else conv_dim if out_channels <= 0: raise ValueError( f"Conv output channels should be greater than 0. Got {out_channels}" ) conv = self._get_rpn_conv(cur_channels, out_channels) self.conv.add_module(f"conv{k}", conv) cur_channels = out_channels # 1x1 conv for predicting objectness logits self.objectness_logits = nn.Conv2d(cur_channels, num_anchors, kernel_size=1, stride=1) # 1x1 conv for predicting box2box transform deltas self.anchor_deltas = nn.Conv2d(cur_channels, num_anchors * box_dim, kernel_size=1, stride=1) # Keeping the order of weights initialization same for backwards compatiblility. for layer in self.modules(): if isinstance(layer, nn.Conv2d): nn.init.normal_(layer.weight, std=0.01) nn.init.constant_(layer.bias, 0) def _get_rpn_conv(self, in_channels, out_channels): return Conv2d( in_channels, out_channels, kernel_size=3, stride=1, padding=1, activation=nn.ReLU(), ) @classmethod def from_config(cls, cfg, input_shape): # Standard RPN is shared across levels: in_channels = [s.channels for s in input_shape] assert len(set(in_channels)) == 1, "Each level must have the same channel!" in_channels = in_channels[0] # RPNHead should take the same input as anchor generator # NOTE: it assumes that creating an anchor generator does not have unwanted side effect. anchor_generator = build_anchor_generator(cfg, input_shape) num_anchors = anchor_generator.num_anchors box_dim = anchor_generator.box_dim assert ( len(set(num_anchors)) == 1 ), "Each level must have the same number of anchors per spatial position" return { "in_channels": in_channels, "num_anchors": num_anchors[0], "box_dim": box_dim, "conv_dims": cfg.MODEL.RPN.CONV_DIMS, } def forward(self, features: List[torch.Tensor]): """ Args: features (list[Tensor]): list of feature maps Returns: list[Tensor]: A list of L elements. Element i is a tensor of shape (N, A, Hi, Wi) representing the predicted objectness logits for all anchors. A is the number of cell anchors. list[Tensor]: A list of L elements. Element i is a tensor of shape (N, A*box_dim, Hi, Wi) representing the predicted "deltas" used to transform anchors to proposals. """ pred_objectness_logits = [] pred_anchor_deltas = [] for x in features: t = self.conv(x) pred_objectness_logits.append(self.objectness_logits(t)) pred_anchor_deltas.append(self.anchor_deltas(t)) return pred_objectness_logits, pred_anchor_deltas @PROPOSAL_GENERATOR_REGISTRY.register() class RPN(nn.Module): """ Region Proposal Network, introduced by :paper:`Faster R-CNN`. """ @configurable def __init__( self, *, in_features: List[str], head: nn.Module, anchor_generator: nn.Module, anchor_matcher: Matcher, box2box_transform: Box2BoxTransform, batch_size_per_image: int, positive_fraction: float, pre_nms_topk: Tuple[float, float], post_nms_topk: Tuple[float, float], nms_thresh: float = 0.7, min_box_size: float = 0.0, anchor_boundary_thresh: float = -1.0, loss_weight: Union[float, Dict[str, float]] = 1.0, box_reg_loss_type: str = "smooth_l1", smooth_l1_beta: float = 0.0, ): """ NOTE: this interface is experimental. Args: in_features (list[str]): list of names of input features to use head (nn.Module): a module that predicts logits and regression deltas for each level from a list of per-level features anchor_generator (nn.Module): a module that creates anchors from a list of features. Usually an instance of :class:`AnchorGenerator` anchor_matcher (Matcher): label the anchors by matching them with ground truth. box2box_transform (Box2BoxTransform): defines the transform from anchors boxes to instance boxes batch_size_per_image (int): number of anchors per image to sample for training positive_fraction (float): fraction of foreground anchors to sample for training pre_nms_topk (tuple[float]): (train, test) that represents the number of top k proposals to select before NMS, in training and testing. post_nms_topk (tuple[float]): (train, test) that represents the number of top k proposals to select after NMS, in training and testing. nms_thresh (float): NMS threshold used to de-duplicate the predicted proposals min_box_size (float): remove proposal boxes with any side smaller than this threshold, in the unit of input image pixels anchor_boundary_thresh (float): legacy option loss_weight (float|dict): weights to use for losses. Can be single float for weighting all rpn losses together, or a dict of individual weightings. Valid dict keys are: "loss_rpn_cls" - applied to classification loss "loss_rpn_loc" - applied to box regression loss box_reg_loss_type (str): Loss type to use. Supported losses: "smooth_l1", "giou". smooth_l1_beta (float): beta parameter for the smooth L1 regression loss. Default to use L1 loss. Only used when `box_reg_loss_type` is "smooth_l1" """ super().__init__() self.in_features = in_features self.rpn_head = head self.anchor_generator = anchor_generator self.anchor_matcher = anchor_matcher self.box2box_transform = box2box_transform self.batch_size_per_image = batch_size_per_image self.positive_fraction = positive_fraction # Map from self.training state to train/test settings self.pre_nms_topk = {True: pre_nms_topk[0], False: pre_nms_topk[1]} self.post_nms_topk = {True: post_nms_topk[0], False: post_nms_topk[1]} self.nms_thresh = nms_thresh self.min_box_size = float(min_box_size) self.anchor_boundary_thresh = anchor_boundary_thresh if isinstance(loss_weight, float): loss_weight = {"loss_rpn_cls": loss_weight, "loss_rpn_loc": loss_weight} self.loss_weight = loss_weight self.box_reg_loss_type = box_reg_loss_type self.smooth_l1_beta = smooth_l1_beta @classmethod def from_config(cls, cfg, input_shape: Dict[str, ShapeSpec]): in_features = cfg.MODEL.RPN.IN_FEATURES ret = { "in_features": in_features, "min_box_size": cfg.MODEL.PROPOSAL_GENERATOR.MIN_SIZE, "nms_thresh": cfg.MODEL.RPN.NMS_THRESH, "batch_size_per_image": cfg.MODEL.RPN.BATCH_SIZE_PER_IMAGE, "positive_fraction": cfg.MODEL.RPN.POSITIVE_FRACTION, "loss_weight": { "loss_rpn_cls": cfg.MODEL.RPN.LOSS_WEIGHT, "loss_rpn_loc": cfg.MODEL.RPN.BBOX_REG_LOSS_WEIGHT * cfg.MODEL.RPN.LOSS_WEIGHT, }, "anchor_boundary_thresh": cfg.MODEL.RPN.BOUNDARY_THRESH, "box2box_transform": Box2BoxTransform(weights=cfg.MODEL.RPN.BBOX_REG_WEIGHTS), "box_reg_loss_type": cfg.MODEL.RPN.BBOX_REG_LOSS_TYPE, "smooth_l1_beta": cfg.MODEL.RPN.SMOOTH_L1_BETA, } ret["pre_nms_topk"] = (cfg.MODEL.RPN.PRE_NMS_TOPK_TRAIN, cfg.MODEL.RPN.PRE_NMS_TOPK_TEST) ret["post_nms_topk"] = (cfg.MODEL.RPN.POST_NMS_TOPK_TRAIN, cfg.MODEL.RPN.POST_NMS_TOPK_TEST) ret["anchor_generator"] = build_anchor_generator(cfg, [input_shape[f] for f in in_features]) ret["anchor_matcher"] = Matcher( cfg.MODEL.RPN.IOU_THRESHOLDS, cfg.MODEL.RPN.IOU_LABELS, allow_low_quality_matches=True ) ret["head"] = build_rpn_head(cfg, [input_shape[f] for f in in_features]) return ret def _subsample_labels(self, label): """ Randomly sample a subset of positive and negative examples, and overwrite the label vector to the ignore value (-1) for all elements that are not included in the sample. Args: labels (Tensor): a vector of -1, 0, 1. Will be modified in-place and returned. """ pos_idx, neg_idx = subsample_labels( label, self.batch_size_per_image, self.positive_fraction, 0 ) # Fill with the ignore label (-1), then set positive and negative labels label.fill_(-1) label.scatter_(0, pos_idx, 1) label.scatter_(0, neg_idx, 0) return label @torch.jit.unused @torch.no_grad() def label_and_sample_anchors( self, anchors: List[Boxes], gt_instances: List[Instances] ) -> Tuple[List[torch.Tensor], List[torch.Tensor]]: """ Args: anchors (list[Boxes]): anchors for each feature map. gt_instances: the ground-truth instances for each image. Returns: list[Tensor]: List of #img tensors. i-th element is a vector of labels whose length is the total number of anchors across all feature maps R = sum(Hi * Wi * A). Label values are in {-1, 0, 1}, with meanings: -1 = ignore; 0 = negative class; 1 = positive class. list[Tensor]: i-th element is a Rx4 tensor. The values are the matched gt boxes for each anchor. Values are undefined for those anchors not labeled as 1. """ anchors = Boxes.cat(anchors) gt_boxes = [x.gt_boxes for x in gt_instances] image_sizes = [x.image_size for x in gt_instances] del gt_instances gt_labels = [] matched_gt_boxes = [] for image_size_i, gt_boxes_i in zip(image_sizes, gt_boxes): """ image_size_i: (h, w) for the i-th image gt_boxes_i: ground-truth boxes for i-th image """ match_quality_matrix = retry_if_cuda_oom(pairwise_iou)(gt_boxes_i, anchors) matched_idxs, gt_labels_i = retry_if_cuda_oom(self.anchor_matcher)(match_quality_matrix) # Matching is memory-expensive and may result in CPU tensors. But the result is small gt_labels_i = gt_labels_i.to(device=gt_boxes_i.device) del match_quality_matrix if self.anchor_boundary_thresh >= 0: # Discard anchors that go out of the boundaries of the image # NOTE: This is legacy functionality that is turned off by default in Detectron2 anchors_inside_image = anchors.inside_box(image_size_i, self.anchor_boundary_thresh) gt_labels_i[~anchors_inside_image] = -1 # A vector of labels (-1, 0, 1) for each anchor gt_labels_i = self._subsample_labels(gt_labels_i) if len(gt_boxes_i) == 0: # These values won't be used anyway since the anchor is labeled as background matched_gt_boxes_i = torch.zeros_like(anchors.tensor) else: # TODO wasted indexing computation for ignored boxes matched_gt_boxes_i = gt_boxes_i[matched_idxs].tensor gt_labels.append(gt_labels_i) # N,AHW matched_gt_boxes.append(matched_gt_boxes_i) return gt_labels, matched_gt_boxes @torch.jit.unused def losses( self, anchors: List[Boxes], pred_objectness_logits: List[torch.Tensor], gt_labels: List[torch.Tensor], pred_anchor_deltas: List[torch.Tensor], gt_boxes: List[torch.Tensor], ) -> Dict[str, torch.Tensor]: """ Return the losses from a set of RPN predictions and their associated ground-truth. Args: anchors (list[Boxes or RotatedBoxes]): anchors for each feature map, each has shape (Hi*Wi*A, B), where B is box dimension (4 or 5). pred_objectness_logits (list[Tensor]): A list of L elements. Element i is a tensor of shape (N, Hi*Wi*A) representing the predicted objectness logits for all anchors. gt_labels (list[Tensor]): Output of :meth:`label_and_sample_anchors`. pred_anchor_deltas (list[Tensor]): A list of L elements. Element i is a tensor of shape (N, Hi*Wi*A, 4 or 5) representing the predicted "deltas" used to transform anchors to proposals. gt_boxes (list[Tensor]): Output of :meth:`label_and_sample_anchors`. Returns: dict[loss name -> loss value]: A dict mapping from loss name to loss value. Loss names are: `loss_rpn_cls` for objectness classification and `loss_rpn_loc` for proposal localization. """ num_images = len(gt_labels) gt_labels = torch.stack(gt_labels) # (N, sum(Hi*Wi*Ai)) # Log the number of positive/negative anchors per-image that's used in training pos_mask = gt_labels == 1 num_pos_anchors = pos_mask.sum().item() num_neg_anchors = (gt_labels == 0).sum().item() storage = get_event_storage() storage.put_scalar("rpn/num_pos_anchors", num_pos_anchors / num_images) storage.put_scalar("rpn/num_neg_anchors", num_neg_anchors / num_images) localization_loss = _dense_box_regression_loss( anchors, self.box2box_transform, pred_anchor_deltas, gt_boxes, pos_mask, box_reg_loss_type=self.box_reg_loss_type, smooth_l1_beta=self.smooth_l1_beta, ) valid_mask = gt_labels >= 0 objectness_loss = F.binary_cross_entropy_with_logits( cat(pred_objectness_logits, dim=1)[valid_mask], gt_labels[valid_mask].to(torch.float32), reduction="sum", ) normalizer = self.batch_size_per_image * num_images losses = { "loss_rpn_cls": objectness_loss / normalizer, # The original Faster R-CNN paper uses a slightly different normalizer # for loc loss. But it doesn't matter in practice "loss_rpn_loc": localization_loss / normalizer, } losses = {k: v * self.loss_weight.get(k, 1.0) for k, v in losses.items()} return losses def forward( self, images: ImageList, features: Dict[str, torch.Tensor], gt_instances: Optional[List[Instances]] = None, ): """ Args: images (ImageList): input images of length `N` features (dict[str, Tensor]): input data as a mapping from feature map name to tensor. Axis 0 represents the number of images `N` in the input data; axes 1-3 are channels, height, and width, which may vary between feature maps (e.g., if a feature pyramid is used). gt_instances (list[Instances], optional): a length `N` list of `Instances`s. Each `Instances` stores ground-truth instances for the corresponding image. Returns: proposals: list[Instances]: contains fields "proposal_boxes", "objectness_logits" loss: dict[Tensor] or None """ features = [features[f] for f in self.in_features] anchors = self.anchor_generator(features) pred_objectness_logits, pred_anchor_deltas = self.rpn_head(features) # Transpose the Hi*Wi*A dimension to the middle: pred_objectness_logits = [ # (N, A, Hi, Wi) -> (N, Hi, Wi, A) -> (N, Hi*Wi*A) score.permute(0, 2, 3, 1).flatten(1) for score in pred_objectness_logits ] pred_anchor_deltas = [ # (N, A*B, Hi, Wi) -> (N, A, B, Hi, Wi) -> (N, Hi, Wi, A, B) -> (N, Hi*Wi*A, B) x.view(x.shape[0], -1, self.anchor_generator.box_dim, x.shape[-2], x.shape[-1]) .permute(0, 3, 4, 1, 2) .flatten(1, -2) for x in pred_anchor_deltas ] if self.training: assert gt_instances is not None, "RPN requires gt_instances in training!" gt_labels, gt_boxes = self.label_and_sample_anchors(anchors, gt_instances) losses = self.losses( anchors, pred_objectness_logits, gt_labels, pred_anchor_deltas, gt_boxes ) else: losses = {} proposals = self.predict_proposals( anchors, pred_objectness_logits, pred_anchor_deltas, images.image_sizes ) return proposals, losses def predict_proposals( self, anchors: List[Boxes], pred_objectness_logits: List[torch.Tensor], pred_anchor_deltas: List[torch.Tensor], image_sizes: List[Tuple[int, int]], ): """ Decode all the predicted box regression deltas to proposals. Find the top proposals by applying NMS and removing boxes that are too small. Returns: proposals (list[Instances]): list of N Instances. The i-th Instances stores post_nms_topk object proposals for image i, sorted by their objectness score in descending order. """ # The proposals are treated as fixed for joint training with roi heads. # This approach ignores the derivative w.r.t. the proposal boxes’ coordinates that # are also network responses. with torch.no_grad(): pred_proposals = self._decode_proposals(anchors, pred_anchor_deltas) return find_top_rpn_proposals( pred_proposals, pred_objectness_logits, image_sizes, self.nms_thresh, self.pre_nms_topk[self.training], self.post_nms_topk[self.training], self.min_box_size, self.training, ) def _decode_proposals(self, anchors: List[Boxes], pred_anchor_deltas: List[torch.Tensor]): """ Transform anchors into proposals by applying the predicted anchor deltas. Returns: proposals (list[Tensor]): A list of L tensors. Tensor i has shape (N, Hi*Wi*A, B) """ N = pred_anchor_deltas[0].shape[0] proposals = [] # For each feature map for anchors_i, pred_anchor_deltas_i in zip(anchors, pred_anchor_deltas): B = anchors_i.tensor.size(1) pred_anchor_deltas_i = pred_anchor_deltas_i.reshape(-1, B) # Expand anchors to shape (N*Hi*Wi*A, B) anchors_i = anchors_i.tensor.unsqueeze(0).expand(N, -1, -1).reshape(-1, B) proposals_i = self.box2box_transform.apply_deltas(pred_anchor_deltas_i, anchors_i) # Append feature map proposals with shape (N, Hi*Wi*A, B) proposals.append(proposals_i.view(N, -1, B)) return proposals ================================================ FILE: detectron2/detectron2/modeling/proposal_generator/rrpn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import itertools import logging from typing import Dict, List import torch from detectron2.config import configurable from detectron2.layers import ShapeSpec, batched_nms_rotated, cat from detectron2.structures import Instances, RotatedBoxes, pairwise_iou_rotated from detectron2.utils.memory import retry_if_cuda_oom from ..box_regression import Box2BoxTransformRotated from .build import PROPOSAL_GENERATOR_REGISTRY from .proposal_utils import _is_tracing from .rpn import RPN logger = logging.getLogger(__name__) def find_top_rrpn_proposals( proposals, pred_objectness_logits, image_sizes, nms_thresh, pre_nms_topk, post_nms_topk, min_box_size, training, ): """ For each feature map, select the `pre_nms_topk` highest scoring proposals, apply NMS, clip proposals, and remove small boxes. Return the `post_nms_topk` highest scoring proposals among all the feature maps if `training` is True, otherwise, returns the highest `post_nms_topk` scoring proposals for each feature map. Args: proposals (list[Tensor]): A list of L tensors. Tensor i has shape (N, Hi*Wi*A, 5). All proposal predictions on the feature maps. pred_objectness_logits (list[Tensor]): A list of L tensors. Tensor i has shape (N, Hi*Wi*A). image_sizes (list[tuple]): sizes (h, w) for each image nms_thresh (float): IoU threshold to use for NMS pre_nms_topk (int): number of top k scoring proposals to keep before applying NMS. When RRPN is run on multiple feature maps (as in FPN) this number is per feature map. post_nms_topk (int): number of top k scoring proposals to keep after applying NMS. When RRPN is run on multiple feature maps (as in FPN) this number is total, over all feature maps. min_box_size(float): minimum proposal box side length in pixels (absolute units wrt input images). training (bool): True if proposals are to be used in training, otherwise False. This arg exists only to support a legacy bug; look for the "NB: Legacy bug ..." comment. Returns: proposals (list[Instances]): list of N Instances. The i-th Instances stores post_nms_topk object proposals for image i. """ num_images = len(image_sizes) device = proposals[0].device # 1. Select top-k anchor for every level and every image topk_scores = [] # #lvl Tensor, each of shape N x topk topk_proposals = [] level_ids = [] # #lvl Tensor, each of shape (topk,) batch_idx = torch.arange(num_images, device=device) for level_id, proposals_i, logits_i in zip( itertools.count(), proposals, pred_objectness_logits ): Hi_Wi_A = logits_i.shape[1] if isinstance(Hi_Wi_A, torch.Tensor): # it's a tensor in tracing num_proposals_i = torch.clamp(Hi_Wi_A, max=pre_nms_topk) else: num_proposals_i = min(Hi_Wi_A, pre_nms_topk) topk_scores_i, topk_idx = logits_i.topk(num_proposals_i, dim=1) # each is N x topk topk_proposals_i = proposals_i[batch_idx[:, None], topk_idx] # N x topk x 5 topk_proposals.append(topk_proposals_i) topk_scores.append(topk_scores_i) level_ids.append(torch.full((num_proposals_i,), level_id, dtype=torch.int64, device=device)) # 2. Concat all levels together topk_scores = cat(topk_scores, dim=1) topk_proposals = cat(topk_proposals, dim=1) level_ids = cat(level_ids, dim=0) # 3. For each image, run a per-level NMS, and choose topk results. results = [] for n, image_size in enumerate(image_sizes): boxes = RotatedBoxes(topk_proposals[n]) scores_per_img = topk_scores[n] lvl = level_ids valid_mask = torch.isfinite(boxes.tensor).all(dim=1) & torch.isfinite(scores_per_img) if not valid_mask.all(): if training: raise FloatingPointError( "Predicted boxes or scores contain Inf/NaN. Training has diverged." ) boxes = boxes[valid_mask] scores_per_img = scores_per_img[valid_mask] lvl = lvl[valid_mask] boxes.clip(image_size) # filter empty boxes keep = boxes.nonempty(threshold=min_box_size) if _is_tracing() or keep.sum().item() != len(boxes): boxes, scores_per_img, lvl = (boxes[keep], scores_per_img[keep], lvl[keep]) keep = batched_nms_rotated(boxes.tensor, scores_per_img, lvl, nms_thresh) # In Detectron1, there was different behavior during training vs. testing. # (https://github.com/facebookresearch/Detectron/issues/459) # During training, topk is over the proposals from *all* images in the training batch. # During testing, it is over the proposals for each image separately. # As a result, the training behavior becomes batch-dependent, # and the configuration "POST_NMS_TOPK_TRAIN" end up relying on the batch size. # This bug is addressed in Detectron2 to make the behavior independent of batch size. keep = keep[:post_nms_topk] res = Instances(image_size) res.proposal_boxes = boxes[keep] res.objectness_logits = scores_per_img[keep] results.append(res) return results @PROPOSAL_GENERATOR_REGISTRY.register() class RRPN(RPN): """ Rotated Region Proposal Network described in :paper:`RRPN`. """ @configurable def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if self.anchor_boundary_thresh >= 0: raise NotImplementedError( "anchor_boundary_thresh is a legacy option not implemented for RRPN." ) @classmethod def from_config(cls, cfg, input_shape: Dict[str, ShapeSpec]): ret = super().from_config(cfg, input_shape) ret["box2box_transform"] = Box2BoxTransformRotated(weights=cfg.MODEL.RPN.BBOX_REG_WEIGHTS) return ret @torch.no_grad() def label_and_sample_anchors(self, anchors: List[RotatedBoxes], gt_instances: List[Instances]): """ Args: anchors (list[RotatedBoxes]): anchors for each feature map. gt_instances: the ground-truth instances for each image. Returns: list[Tensor]: List of #img tensors. i-th element is a vector of labels whose length is the total number of anchors across feature maps. Label values are in {-1, 0, 1}, with meanings: -1 = ignore; 0 = negative class; 1 = positive class. list[Tensor]: i-th element is a Nx5 tensor, where N is the total number of anchors across feature maps. The values are the matched gt boxes for each anchor. Values are undefined for those anchors not labeled as 1. """ anchors = RotatedBoxes.cat(anchors) gt_boxes = [x.gt_boxes for x in gt_instances] del gt_instances gt_labels = [] matched_gt_boxes = [] for gt_boxes_i in gt_boxes: """ gt_boxes_i: ground-truth boxes for i-th image """ match_quality_matrix = retry_if_cuda_oom(pairwise_iou_rotated)(gt_boxes_i, anchors) matched_idxs, gt_labels_i = retry_if_cuda_oom(self.anchor_matcher)(match_quality_matrix) # Matching is memory-expensive and may result in CPU tensors. But the result is small gt_labels_i = gt_labels_i.to(device=gt_boxes_i.device) # A vector of labels (-1, 0, 1) for each anchor gt_labels_i = self._subsample_labels(gt_labels_i) if len(gt_boxes_i) == 0: # These values won't be used anyway since the anchor is labeled as background matched_gt_boxes_i = torch.zeros_like(anchors.tensor) else: # TODO wasted indexing computation for ignored boxes matched_gt_boxes_i = gt_boxes_i[matched_idxs].tensor gt_labels.append(gt_labels_i) # N,AHW matched_gt_boxes.append(matched_gt_boxes_i) return gt_labels, matched_gt_boxes @torch.no_grad() def predict_proposals(self, anchors, pred_objectness_logits, pred_anchor_deltas, image_sizes): pred_proposals = self._decode_proposals(anchors, pred_anchor_deltas) return find_top_rrpn_proposals( pred_proposals, pred_objectness_logits, image_sizes, self.nms_thresh, self.pre_nms_topk[self.training], self.post_nms_topk[self.training], self.min_box_size, self.training, ) ================================================ FILE: detectron2/detectron2/modeling/roi_heads/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .box_head import ROI_BOX_HEAD_REGISTRY, build_box_head, FastRCNNConvFCHead from .keypoint_head import ( ROI_KEYPOINT_HEAD_REGISTRY, build_keypoint_head, BaseKeypointRCNNHead, KRCNNConvDeconvUpsampleHead, ) from .mask_head import ( ROI_MASK_HEAD_REGISTRY, build_mask_head, BaseMaskRCNNHead, MaskRCNNConvUpsampleHead, ) from .roi_heads import ( ROI_HEADS_REGISTRY, ROIHeads, Res5ROIHeads, StandardROIHeads, build_roi_heads, select_foreground_proposals, ) from .cascade_rcnn import CascadeROIHeads from .rotated_fast_rcnn import RROIHeads from .fast_rcnn import FastRCNNOutputLayers from . import cascade_rcnn # isort:skip __all__ = list(globals().keys()) ================================================ FILE: detectron2/detectron2/modeling/roi_heads/box_head.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from typing import List import fvcore.nn.weight_init as weight_init import torch from torch import nn from detectron2.config import configurable from detectron2.layers import Conv2d, ShapeSpec, get_norm from detectron2.utils.registry import Registry __all__ = ["FastRCNNConvFCHead", "build_box_head", "ROI_BOX_HEAD_REGISTRY"] ROI_BOX_HEAD_REGISTRY = Registry("ROI_BOX_HEAD") ROI_BOX_HEAD_REGISTRY.__doc__ = """ Registry for box heads, which make box predictions from per-region features. The registered object will be called with `obj(cfg, input_shape)`. """ # To get torchscript support, we make the head a subclass of `nn.Sequential`. # Therefore, to add new layers in this head class, please make sure they are # added in the order they will be used in forward(). @ROI_BOX_HEAD_REGISTRY.register() class FastRCNNConvFCHead(nn.Sequential): """ A head with several 3x3 conv layers (each followed by norm & relu) and then several fc layers (each followed by relu). """ @configurable def __init__( self, input_shape: ShapeSpec, *, conv_dims: List[int], fc_dims: List[int], conv_norm="" ): """ NOTE: this interface is experimental. Args: input_shape (ShapeSpec): shape of the input feature. conv_dims (list[int]): the output dimensions of the conv layers fc_dims (list[int]): the output dimensions of the fc layers conv_norm (str or callable): normalization for the conv layers. See :func:`detectron2.layers.get_norm` for supported types. """ super().__init__() assert len(conv_dims) + len(fc_dims) > 0 self._output_size = (input_shape.channels, input_shape.height, input_shape.width) self.conv_norm_relus = [] for k, conv_dim in enumerate(conv_dims): conv = Conv2d( self._output_size[0], conv_dim, kernel_size=3, padding=1, bias=not conv_norm, norm=get_norm(conv_norm, conv_dim), activation=nn.ReLU(), ) self.add_module("conv{}".format(k + 1), conv) self.conv_norm_relus.append(conv) self._output_size = (conv_dim, self._output_size[1], self._output_size[2]) self.fcs = [] for k, fc_dim in enumerate(fc_dims): if k == 0: self.add_module("flatten", nn.Flatten()) fc = nn.Linear(int(np.prod(self._output_size)), fc_dim) self.add_module("fc{}".format(k + 1), fc) self.add_module("fc_relu{}".format(k + 1), nn.ReLU()) self.fcs.append(fc) self._output_size = fc_dim for layer in self.conv_norm_relus: weight_init.c2_msra_fill(layer) for layer in self.fcs: weight_init.c2_xavier_fill(layer) @classmethod def from_config(cls, cfg, input_shape): num_conv = cfg.MODEL.ROI_BOX_HEAD.NUM_CONV conv_dim = cfg.MODEL.ROI_BOX_HEAD.CONV_DIM num_fc = cfg.MODEL.ROI_BOX_HEAD.NUM_FC fc_dim = cfg.MODEL.ROI_BOX_HEAD.FC_DIM return { "input_shape": input_shape, "conv_dims": [conv_dim] * num_conv, "fc_dims": [fc_dim] * num_fc, "conv_norm": cfg.MODEL.ROI_BOX_HEAD.NORM, } def forward(self, x): for layer in self: x = layer(x) return x @property @torch.jit.unused def output_shape(self): """ Returns: ShapeSpec: the output feature shape """ o = self._output_size if isinstance(o, int): return ShapeSpec(channels=o) else: return ShapeSpec(channels=o[0], height=o[1], width=o[2]) def build_box_head(cfg, input_shape): """ Build a box head defined by `cfg.MODEL.ROI_BOX_HEAD.NAME`. """ name = cfg.MODEL.ROI_BOX_HEAD.NAME return ROI_BOX_HEAD_REGISTRY.get(name)(cfg, input_shape) ================================================ FILE: detectron2/detectron2/modeling/roi_heads/cascade_rcnn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import List import torch from torch import nn from torch.autograd.function import Function from detectron2.config import configurable from detectron2.layers import ShapeSpec from detectron2.structures import Boxes, Instances, pairwise_iou from detectron2.utils.events import get_event_storage from ..box_regression import Box2BoxTransform from ..matcher import Matcher from ..poolers import ROIPooler from .box_head import build_box_head from .fast_rcnn import FastRCNNOutputLayers, fast_rcnn_inference from .roi_heads import ROI_HEADS_REGISTRY, StandardROIHeads class _ScaleGradient(Function): @staticmethod def forward(ctx, input, scale): ctx.scale = scale return input @staticmethod def backward(ctx, grad_output): return grad_output * ctx.scale, None @ROI_HEADS_REGISTRY.register() class CascadeROIHeads(StandardROIHeads): """ The ROI heads that implement :paper:`Cascade R-CNN`. """ @configurable def __init__( self, *, box_in_features: List[str], box_pooler: ROIPooler, box_heads: List[nn.Module], box_predictors: List[nn.Module], proposal_matchers: List[Matcher], **kwargs, ): """ NOTE: this interface is experimental. Args: box_pooler (ROIPooler): pooler that extracts region features from given boxes box_heads (list[nn.Module]): box head for each cascade stage box_predictors (list[nn.Module]): box predictor for each cascade stage proposal_matchers (list[Matcher]): matcher with different IoU thresholds to match boxes with ground truth for each stage. The first matcher matches RPN proposals with ground truth, the other matchers use boxes predicted by the previous stage as proposals and match them with ground truth. """ assert "proposal_matcher" not in kwargs, ( "CascadeROIHeads takes 'proposal_matchers=' for each stage instead " "of one 'proposal_matcher='." ) # The first matcher matches RPN proposals with ground truth, done in the base class kwargs["proposal_matcher"] = proposal_matchers[0] num_stages = self.num_cascade_stages = len(box_heads) box_heads = nn.ModuleList(box_heads) box_predictors = nn.ModuleList(box_predictors) assert len(box_predictors) == num_stages, f"{len(box_predictors)} != {num_stages}!" assert len(proposal_matchers) == num_stages, f"{len(proposal_matchers)} != {num_stages}!" super().__init__( box_in_features=box_in_features, box_pooler=box_pooler, box_head=box_heads, box_predictor=box_predictors, **kwargs, ) self.proposal_matchers = proposal_matchers @classmethod def from_config(cls, cfg, input_shape): ret = super().from_config(cfg, input_shape) ret.pop("proposal_matcher") return ret @classmethod def _init_box_head(cls, cfg, input_shape): # fmt: off in_features = cfg.MODEL.ROI_HEADS.IN_FEATURES pooler_resolution = cfg.MODEL.ROI_BOX_HEAD.POOLER_RESOLUTION pooler_scales = tuple(1.0 / input_shape[k].stride for k in in_features) sampling_ratio = cfg.MODEL.ROI_BOX_HEAD.POOLER_SAMPLING_RATIO pooler_type = cfg.MODEL.ROI_BOX_HEAD.POOLER_TYPE cascade_bbox_reg_weights = cfg.MODEL.ROI_BOX_CASCADE_HEAD.BBOX_REG_WEIGHTS cascade_ious = cfg.MODEL.ROI_BOX_CASCADE_HEAD.IOUS assert len(cascade_bbox_reg_weights) == len(cascade_ious) assert cfg.MODEL.ROI_BOX_HEAD.CLS_AGNOSTIC_BBOX_REG, \ "CascadeROIHeads only support class-agnostic regression now!" assert cascade_ious[0] == cfg.MODEL.ROI_HEADS.IOU_THRESHOLDS[0] # fmt: on in_channels = [input_shape[f].channels for f in in_features] # Check all channel counts are equal assert len(set(in_channels)) == 1, in_channels in_channels = in_channels[0] box_pooler = ROIPooler( output_size=pooler_resolution, scales=pooler_scales, sampling_ratio=sampling_ratio, pooler_type=pooler_type, ) pooled_shape = ShapeSpec( channels=in_channels, width=pooler_resolution, height=pooler_resolution ) box_heads, box_predictors, proposal_matchers = [], [], [] for match_iou, bbox_reg_weights in zip(cascade_ious, cascade_bbox_reg_weights): box_head = build_box_head(cfg, pooled_shape) box_heads.append(box_head) box_predictors.append( FastRCNNOutputLayers( cfg, box_head.output_shape, box2box_transform=Box2BoxTransform(weights=bbox_reg_weights), ) ) proposal_matchers.append(Matcher([match_iou], [0, 1], allow_low_quality_matches=False)) return { "box_in_features": in_features, "box_pooler": box_pooler, "box_heads": box_heads, "box_predictors": box_predictors, "proposal_matchers": proposal_matchers, } def forward(self, images, features, proposals, targets=None): del images if self.training: proposals = self.label_and_sample_proposals(proposals, targets) if self.training: # Need targets to box head losses = self._forward_box(features, proposals, targets) losses.update(self._forward_mask(features, proposals)) losses.update(self._forward_keypoint(features, proposals)) return proposals, losses else: pred_instances = self._forward_box(features, proposals) pred_instances = self.forward_with_given_boxes(features, pred_instances) return pred_instances, {} def _forward_box(self, features, proposals, targets=None): """ Args: features, targets: the same as in Same as in :meth:`ROIHeads.forward`. proposals (list[Instances]): the per-image object proposals with their matching ground truth. Each has fields "proposal_boxes", and "objectness_logits", "gt_classes", "gt_boxes". """ features = [features[f] for f in self.box_in_features] head_outputs = [] # (predictor, predictions, proposals) prev_pred_boxes = None image_sizes = [x.image_size for x in proposals] for k in range(self.num_cascade_stages): if k > 0: # The output boxes of the previous stage are used to create the input # proposals of the next stage. proposals = self._create_proposals_from_boxes(prev_pred_boxes, image_sizes) if self.training: proposals = self._match_and_label_boxes(proposals, k, targets) predictions = self._run_stage(features, proposals, k) prev_pred_boxes = self.box_predictor[k].predict_boxes(predictions, proposals) head_outputs.append((self.box_predictor[k], predictions, proposals)) if self.training: losses = {} storage = get_event_storage() for stage, (predictor, predictions, proposals) in enumerate(head_outputs): with storage.name_scope("stage{}".format(stage)): stage_losses = predictor.losses(predictions, proposals) losses.update({k + "_stage{}".format(stage): v for k, v in stage_losses.items()}) return losses else: # Each is a list[Tensor] of length #image. Each tensor is Ri x (K+1) scores_per_stage = [h[0].predict_probs(h[1], h[2]) for h in head_outputs] # Average the scores across heads scores = [ sum(list(scores_per_image)) * (1.0 / self.num_cascade_stages) for scores_per_image in zip(*scores_per_stage) ] # Use the boxes of the last head predictor, predictions, proposals = head_outputs[-1] boxes = predictor.predict_boxes(predictions, proposals) pred_instances, _ = fast_rcnn_inference( boxes, scores, image_sizes, predictor.test_score_thresh, predictor.test_nms_thresh, predictor.test_topk_per_image, ) return pred_instances @torch.no_grad() def _match_and_label_boxes(self, proposals, stage, targets): """ Match proposals with groundtruth using the matcher at the given stage. Label the proposals as foreground or background based on the match. Args: proposals (list[Instances]): One Instances for each image, with the field "proposal_boxes". stage (int): the current stage targets (list[Instances]): the ground truth instances Returns: list[Instances]: the same proposals, but with fields "gt_classes" and "gt_boxes" """ num_fg_samples, num_bg_samples = [], [] for proposals_per_image, targets_per_image in zip(proposals, targets): match_quality_matrix = pairwise_iou( targets_per_image.gt_boxes, proposals_per_image.proposal_boxes ) # proposal_labels are 0 or 1 matched_idxs, proposal_labels = self.proposal_matchers[stage](match_quality_matrix) if len(targets_per_image) > 0: gt_classes = targets_per_image.gt_classes[matched_idxs] # Label unmatched proposals (0 label from matcher) as background (label=num_classes) gt_classes[proposal_labels == 0] = self.num_classes gt_boxes = targets_per_image.gt_boxes[matched_idxs] else: gt_classes = torch.zeros_like(matched_idxs) + self.num_classes gt_boxes = Boxes( targets_per_image.gt_boxes.tensor.new_zeros((len(proposals_per_image), 4)) ) proposals_per_image.gt_classes = gt_classes proposals_per_image.gt_boxes = gt_boxes num_fg_samples.append((proposal_labels == 1).sum().item()) num_bg_samples.append(proposal_labels.numel() - num_fg_samples[-1]) # Log the number of fg/bg samples in each stage storage = get_event_storage() storage.put_scalar( "stage{}/roi_head/num_fg_samples".format(stage), sum(num_fg_samples) / len(num_fg_samples), ) storage.put_scalar( "stage{}/roi_head/num_bg_samples".format(stage), sum(num_bg_samples) / len(num_bg_samples), ) return proposals def _run_stage(self, features, proposals, stage): """ Args: features (list[Tensor]): #lvl input features to ROIHeads proposals (list[Instances]): #image Instances, with the field "proposal_boxes" stage (int): the current stage Returns: Same output as `FastRCNNOutputLayers.forward()`. """ box_features = self.box_pooler(features, [x.proposal_boxes for x in proposals]) # The original implementation averages the losses among heads, # but scale up the parameter gradients of the heads. # This is equivalent to adding the losses among heads, # but scale down the gradients on features. if self.training: box_features = _ScaleGradient.apply(box_features, 1.0 / self.num_cascade_stages) box_features = self.box_head[stage](box_features) return self.box_predictor[stage](box_features) def _create_proposals_from_boxes(self, boxes, image_sizes): """ Args: boxes (list[Tensor]): per-image predicted boxes, each of shape Ri x 4 image_sizes (list[tuple]): list of image shapes in (h, w) Returns: list[Instances]: per-image proposals with the given boxes. """ # Just like RPN, the proposals should not have gradients boxes = [Boxes(b.detach()) for b in boxes] proposals = [] for boxes_per_image, image_size in zip(boxes, image_sizes): boxes_per_image.clip(image_size) if self.training: # do not filter empty boxes at inference time, # because the scores from each stage need to be aligned and added later boxes_per_image = boxes_per_image[boxes_per_image.nonempty()] prop = Instances(image_size) prop.proposal_boxes = boxes_per_image proposals.append(prop) return proposals ================================================ FILE: detectron2/detectron2/modeling/roi_heads/fast_rcnn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging from typing import Callable, Dict, List, Optional, Tuple, Union import torch from torch import nn from torch.nn import functional as F from detectron2.config import configurable from detectron2.data.detection_utils import get_fed_loss_cls_weights from detectron2.layers import ShapeSpec, batched_nms, cat, cross_entropy, nonzero_tuple from detectron2.modeling.box_regression import Box2BoxTransform, _dense_box_regression_loss from detectron2.structures import Boxes, Instances from detectron2.utils.events import get_event_storage __all__ = ["fast_rcnn_inference", "FastRCNNOutputLayers"] logger = logging.getLogger(__name__) """ Shape shorthand in this module: N: number of images in the minibatch R: number of ROIs, combined over all images, in the minibatch Ri: number of ROIs in image i K: number of foreground classes. E.g.,there are 80 foreground classes in COCO. Naming convention: deltas: refers to the 4-d (dx, dy, dw, dh) deltas that parameterize the box2box transform (see :class:`box_regression.Box2BoxTransform`). pred_class_logits: predicted class scores in [-inf, +inf]; use softmax(pred_class_logits) to estimate P(class). gt_classes: ground-truth classification labels in [0, K], where [0, K) represent foreground object classes and K represents the background class. pred_proposal_deltas: predicted box2box transform deltas for transforming proposals to detection box predictions. gt_proposal_deltas: ground-truth box2box transform deltas """ def fast_rcnn_inference( boxes: List[torch.Tensor], scores: List[torch.Tensor], image_shapes: List[Tuple[int, int]], score_thresh: float, nms_thresh: float, topk_per_image: int, ): """ Call `fast_rcnn_inference_single_image` for all images. Args: boxes (list[Tensor]): A list of Tensors of predicted class-specific or class-agnostic boxes for each image. Element i has shape (Ri, K * 4) if doing class-specific regression, or (Ri, 4) if doing class-agnostic regression, where Ri is the number of predicted objects for image i. This is compatible with the output of :meth:`FastRCNNOutputLayers.predict_boxes`. scores (list[Tensor]): A list of Tensors of predicted class scores for each image. Element i has shape (Ri, K + 1), where Ri is the number of predicted objects for image i. Compatible with the output of :meth:`FastRCNNOutputLayers.predict_probs`. image_shapes (list[tuple]): A list of (width, height) tuples for each image in the batch. score_thresh (float): Only return detections with a confidence score exceeding this threshold. nms_thresh (float): The threshold to use for box non-maximum suppression. Value in [0, 1]. topk_per_image (int): The number of top scoring detections to return. Set < 0 to return all detections. Returns: instances: (list[Instances]): A list of N instances, one for each image in the batch, that stores the topk most confidence detections. kept_indices: (list[Tensor]): A list of 1D tensor of length of N, each element indicates the corresponding boxes/scores index in [0, Ri) from the input, for image i. """ result_per_image = [ fast_rcnn_inference_single_image( boxes_per_image, scores_per_image, image_shape, score_thresh, nms_thresh, topk_per_image ) for scores_per_image, boxes_per_image, image_shape in zip(scores, boxes, image_shapes) ] return [x[0] for x in result_per_image], [x[1] for x in result_per_image] def _log_classification_stats(pred_logits, gt_classes, prefix="fast_rcnn"): """ Log the classification metrics to EventStorage. Args: pred_logits: Rx(K+1) logits. The last column is for background class. gt_classes: R labels """ num_instances = gt_classes.numel() if num_instances == 0: return pred_classes = pred_logits.argmax(dim=1) bg_class_ind = pred_logits.shape[1] - 1 fg_inds = (gt_classes >= 0) & (gt_classes < bg_class_ind) num_fg = fg_inds.nonzero().numel() fg_gt_classes = gt_classes[fg_inds] fg_pred_classes = pred_classes[fg_inds] num_false_negative = (fg_pred_classes == bg_class_ind).nonzero().numel() num_accurate = (pred_classes == gt_classes).nonzero().numel() fg_num_accurate = (fg_pred_classes == fg_gt_classes).nonzero().numel() storage = get_event_storage() storage.put_scalar(f"{prefix}/cls_accuracy", num_accurate / num_instances) if num_fg > 0: storage.put_scalar(f"{prefix}/fg_cls_accuracy", fg_num_accurate / num_fg) storage.put_scalar(f"{prefix}/false_negative", num_false_negative / num_fg) def fast_rcnn_inference_single_image( boxes, scores, image_shape: Tuple[int, int], score_thresh: float, nms_thresh: float, topk_per_image: int, ): """ Single-image inference. Return bounding-box detection results by thresholding on scores and applying non-maximum suppression (NMS). Args: Same as `fast_rcnn_inference`, but with boxes, scores, and image shapes per image. Returns: Same as `fast_rcnn_inference`, but for only one image. """ valid_mask = torch.isfinite(boxes).all(dim=1) & torch.isfinite(scores).all(dim=1) if not valid_mask.all(): boxes = boxes[valid_mask] scores = scores[valid_mask] scores = scores[:, :-1] num_bbox_reg_classes = boxes.shape[1] // 4 # Convert to Boxes to use the `clip` function ... boxes = Boxes(boxes.reshape(-1, 4)) boxes.clip(image_shape) boxes = boxes.tensor.view(-1, num_bbox_reg_classes, 4) # R x C x 4 # 1. Filter results based on detection scores. It can make NMS more efficient # by filtering out low-confidence detections. filter_mask = scores > score_thresh # R x K # R' x 2. First column contains indices of the R predictions; # Second column contains indices of classes. filter_inds = filter_mask.nonzero() if num_bbox_reg_classes == 1: boxes = boxes[filter_inds[:, 0], 0] else: boxes = boxes[filter_mask] scores = scores[filter_mask] # 2. Apply NMS for each class independently. keep = batched_nms(boxes, scores, filter_inds[:, 1], nms_thresh) if topk_per_image >= 0: keep = keep[:topk_per_image] boxes, scores, filter_inds = boxes[keep], scores[keep], filter_inds[keep] result = Instances(image_shape) result.pred_boxes = Boxes(boxes) result.scores = scores result.pred_classes = filter_inds[:, 1] return result, filter_inds[:, 0] class FastRCNNOutputLayers(nn.Module): """ Two linear layers for predicting Fast R-CNN outputs: 1. proposal-to-detection box regression deltas 2. classification scores """ @configurable def __init__( self, input_shape: ShapeSpec, *, box2box_transform, num_classes: int, test_score_thresh: float = 0.0, test_nms_thresh: float = 0.5, test_topk_per_image: int = 100, cls_agnostic_bbox_reg: bool = False, smooth_l1_beta: float = 0.0, box_reg_loss_type: str = "smooth_l1", loss_weight: Union[float, Dict[str, float]] = 1.0, use_fed_loss: bool = False, use_sigmoid_ce: bool = False, get_fed_loss_cls_weights: Optional[Callable] = None, fed_loss_num_classes: int = 50, ): """ NOTE: this interface is experimental. Args: input_shape (ShapeSpec): shape of the input feature to this module box2box_transform (Box2BoxTransform or Box2BoxTransformRotated): num_classes (int): number of foreground classes test_score_thresh (float): threshold to filter predictions results. test_nms_thresh (float): NMS threshold for prediction results. test_topk_per_image (int): number of top predictions to produce per image. cls_agnostic_bbox_reg (bool): whether to use class agnostic for bbox regression smooth_l1_beta (float): transition point from L1 to L2 loss. Only used if `box_reg_loss_type` is "smooth_l1" box_reg_loss_type (str): Box regression loss type. One of: "smooth_l1", "giou", "diou", "ciou" loss_weight (float|dict): weights to use for losses. Can be single float for weighting all losses, or a dict of individual weightings. Valid dict keys are: * "loss_cls": applied to classification loss * "loss_box_reg": applied to box regression loss use_fed_loss (bool): whether to use federated loss which samples additional negative classes to calculate the loss use_sigmoid_ce (bool): whether to calculate the loss using weighted average of binary cross entropy with logits. This could be used together with federated loss get_fed_loss_cls_weights (Callable): a callable which takes dataset name and frequency weight power, and returns the probabilities to sample negative classes for federated loss. The implementation can be found in detectron2/data/detection_utils.py fed_loss_num_classes (int): number of federated classes to keep in total """ super().__init__() if isinstance(input_shape, int): # some backward compatibility input_shape = ShapeSpec(channels=input_shape) self.num_classes = num_classes input_size = input_shape.channels * (input_shape.width or 1) * (input_shape.height or 1) # prediction layer for num_classes foreground classes and one background class (hence + 1) self.cls_score = nn.Linear(input_size, num_classes + 1) num_bbox_reg_classes = 1 if cls_agnostic_bbox_reg else num_classes box_dim = len(box2box_transform.weights) self.bbox_pred = nn.Linear(input_size, num_bbox_reg_classes * box_dim) nn.init.normal_(self.cls_score.weight, std=0.01) nn.init.normal_(self.bbox_pred.weight, std=0.001) for l in [self.cls_score, self.bbox_pred]: nn.init.constant_(l.bias, 0) self.box2box_transform = box2box_transform self.smooth_l1_beta = smooth_l1_beta self.test_score_thresh = test_score_thresh self.test_nms_thresh = test_nms_thresh self.test_topk_per_image = test_topk_per_image self.box_reg_loss_type = box_reg_loss_type if isinstance(loss_weight, float): loss_weight = {"loss_cls": loss_weight, "loss_box_reg": loss_weight} self.loss_weight = loss_weight self.use_fed_loss = use_fed_loss self.use_sigmoid_ce = use_sigmoid_ce self.fed_loss_num_classes = fed_loss_num_classes if self.use_fed_loss: assert self.use_sigmoid_ce, "Please use sigmoid cross entropy loss with federated loss" fed_loss_cls_weights = get_fed_loss_cls_weights() assert ( len(fed_loss_cls_weights) == self.num_classes ), "Please check the provided fed_loss_cls_weights. Their size should match num_classes" self.register_buffer("fed_loss_cls_weights", fed_loss_cls_weights) @classmethod def from_config(cls, cfg, input_shape): return { "input_shape": input_shape, "box2box_transform": Box2BoxTransform(weights=cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS), # fmt: off "num_classes" : cfg.MODEL.ROI_HEADS.NUM_CLASSES, "cls_agnostic_bbox_reg" : cfg.MODEL.ROI_BOX_HEAD.CLS_AGNOSTIC_BBOX_REG, "smooth_l1_beta" : cfg.MODEL.ROI_BOX_HEAD.SMOOTH_L1_BETA, "test_score_thresh" : cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST, "test_nms_thresh" : cfg.MODEL.ROI_HEADS.NMS_THRESH_TEST, "test_topk_per_image" : cfg.TEST.DETECTIONS_PER_IMAGE, "box_reg_loss_type" : cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_LOSS_TYPE, "loss_weight" : {"loss_box_reg": cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_LOSS_WEIGHT}, # noqa "use_fed_loss" : cfg.MODEL.ROI_BOX_HEAD.USE_FED_LOSS, "use_sigmoid_ce" : cfg.MODEL.ROI_BOX_HEAD.USE_SIGMOID_CE, "get_fed_loss_cls_weights" : lambda: get_fed_loss_cls_weights(dataset_names=cfg.DATASETS.TRAIN, freq_weight_power=cfg.MODEL.ROI_BOX_HEAD.FED_LOSS_FREQ_WEIGHT_POWER), # noqa "fed_loss_num_classes" : cfg.MODEL.ROI_BOX_HEAD.FED_LOSS_NUM_CLASSES, # fmt: on } def forward(self, x): """ Args: x: per-region features of shape (N, ...) for N bounding boxes to predict. Returns: (Tensor, Tensor): First tensor: shape (N,K+1), scores for each of the N box. Each row contains the scores for K object categories and 1 background class. Second tensor: bounding box regression deltas for each box. Shape is shape (N,Kx4), or (N,4) for class-agnostic regression. """ if x.dim() > 2: x = torch.flatten(x, start_dim=1) scores = self.cls_score(x) proposal_deltas = self.bbox_pred(x) return scores, proposal_deltas def losses(self, predictions, proposals): """ Args: predictions: return values of :meth:`forward()`. proposals (list[Instances]): proposals that match the features that were used to compute predictions. The fields ``proposal_boxes``, ``gt_boxes``, ``gt_classes`` are expected. Returns: Dict[str, Tensor]: dict of losses """ scores, proposal_deltas = predictions # parse classification outputs gt_classes = ( cat([p.gt_classes for p in proposals], dim=0) if len(proposals) else torch.empty(0) ) _log_classification_stats(scores, gt_classes) # parse box regression outputs if len(proposals): proposal_boxes = cat([p.proposal_boxes.tensor for p in proposals], dim=0) # Nx4 assert not proposal_boxes.requires_grad, "Proposals should not require gradients!" # If "gt_boxes" does not exist, the proposals must be all negative and # should not be included in regression loss computation. # Here we just use proposal_boxes as an arbitrary placeholder because its # value won't be used in self.box_reg_loss(). gt_boxes = cat( [(p.gt_boxes if p.has("gt_boxes") else p.proposal_boxes).tensor for p in proposals], dim=0, ) else: proposal_boxes = gt_boxes = torch.empty((0, 4), device=proposal_deltas.device) if self.use_sigmoid_ce: loss_cls = self.sigmoid_cross_entropy_loss(scores, gt_classes) else: loss_cls = cross_entropy(scores, gt_classes, reduction="mean") losses = { "loss_cls": loss_cls, "loss_box_reg": self.box_reg_loss( proposal_boxes, gt_boxes, proposal_deltas, gt_classes ), } return {k: v * self.loss_weight.get(k, 1.0) for k, v in losses.items()} # Implementation from https://github.com/xingyizhou/CenterNet2/blob/master/projects/CenterNet2/centernet/modeling/roi_heads/fed_loss.py # noqa # with slight modifications def get_fed_loss_classes(self, gt_classes, num_fed_loss_classes, num_classes, weight): """ Args: gt_classes: a long tensor of shape R that contains the gt class label of each proposal. num_fed_loss_classes: minimum number of classes to keep when calculating federated loss. Will sample negative classes if number of unique gt_classes is smaller than this value. num_classes: number of foreground classes weight: probabilities used to sample negative classes Returns: Tensor: classes to keep when calculating the federated loss, including both unique gt classes and sampled negative classes. """ unique_gt_classes = torch.unique(gt_classes) prob = unique_gt_classes.new_ones(num_classes + 1).float() prob[-1] = 0 if len(unique_gt_classes) < num_fed_loss_classes: prob[:num_classes] = weight.float().clone() prob[unique_gt_classes] = 0 sampled_negative_classes = torch.multinomial( prob, num_fed_loss_classes - len(unique_gt_classes), replacement=False ) fed_loss_classes = torch.cat([unique_gt_classes, sampled_negative_classes]) else: fed_loss_classes = unique_gt_classes return fed_loss_classes # Implementation from https://github.com/xingyizhou/CenterNet2/blob/master/projects/CenterNet2/centernet/modeling/roi_heads/custom_fast_rcnn.py#L113 # noqa # with slight modifications def sigmoid_cross_entropy_loss(self, pred_class_logits, gt_classes): """ Args: pred_class_logits: shape (N, K+1), scores for each of the N box. Each row contains the scores for K object categories and 1 background class gt_classes: a long tensor of shape R that contains the gt class label of each proposal. """ if pred_class_logits.numel() == 0: return pred_class_logits.new_zeros([1])[0] N = pred_class_logits.shape[0] K = pred_class_logits.shape[1] - 1 target = pred_class_logits.new_zeros(N, K + 1) target[range(len(gt_classes)), gt_classes] = 1 target = target[:, :K] cls_loss = F.binary_cross_entropy_with_logits( pred_class_logits[:, :-1], target, reduction="none" ) if self.use_fed_loss: fed_loss_classes = self.get_fed_loss_classes( gt_classes, num_fed_loss_classes=self.fed_loss_num_classes, num_classes=K, weight=self.fed_loss_cls_weights, ) fed_loss_classes_mask = fed_loss_classes.new_zeros(K + 1) fed_loss_classes_mask[fed_loss_classes] = 1 fed_loss_classes_mask = fed_loss_classes_mask[:K] weight = fed_loss_classes_mask.view(1, K).expand(N, K).float() else: weight = 1 loss = torch.sum(cls_loss * weight) / N return loss def box_reg_loss(self, proposal_boxes, gt_boxes, pred_deltas, gt_classes): """ Args: proposal_boxes/gt_boxes are tensors with the same shape (R, 4 or 5). pred_deltas has shape (R, 4 or 5), or (R, num_classes * (4 or 5)). gt_classes is a long tensor of shape R, the gt class label of each proposal. R shall be the number of proposals. """ box_dim = proposal_boxes.shape[1] # 4 or 5 # Regression loss is only computed for foreground proposals (those matched to a GT) fg_inds = nonzero_tuple((gt_classes >= 0) & (gt_classes < self.num_classes))[0] if pred_deltas.shape[1] == box_dim: # cls-agnostic regression fg_pred_deltas = pred_deltas[fg_inds] else: fg_pred_deltas = pred_deltas.view(-1, self.num_classes, box_dim)[ fg_inds, gt_classes[fg_inds] ] loss_box_reg = _dense_box_regression_loss( [proposal_boxes[fg_inds]], self.box2box_transform, [fg_pred_deltas.unsqueeze(0)], [gt_boxes[fg_inds]], ..., self.box_reg_loss_type, self.smooth_l1_beta, ) # The reg loss is normalized using the total number of regions (R), not the number # of foreground regions even though the box regression loss is only defined on # foreground regions. Why? Because doing so gives equal training influence to # each foreground example. To see how, consider two different minibatches: # (1) Contains a single foreground region # (2) Contains 100 foreground regions # If we normalize by the number of foreground regions, the single example in # minibatch (1) will be given 100 times as much influence as each foreground # example in minibatch (2). Normalizing by the total number of regions, R, # means that the single example in minibatch (1) and each of the 100 examples # in minibatch (2) are given equal influence. return loss_box_reg / max(gt_classes.numel(), 1.0) # return 0 if empty def inference(self, predictions: Tuple[torch.Tensor, torch.Tensor], proposals: List[Instances]): """ Args: predictions: return values of :meth:`forward()`. proposals (list[Instances]): proposals that match the features that were used to compute predictions. The ``proposal_boxes`` field is expected. Returns: list[Instances]: same as `fast_rcnn_inference`. list[Tensor]: same as `fast_rcnn_inference`. """ boxes = self.predict_boxes(predictions, proposals) scores = self.predict_probs(predictions, proposals) image_shapes = [x.image_size for x in proposals] return fast_rcnn_inference( boxes, scores, image_shapes, self.test_score_thresh, self.test_nms_thresh, self.test_topk_per_image, ) def predict_boxes_for_gt_classes(self, predictions, proposals): """ Args: predictions: return values of :meth:`forward()`. proposals (list[Instances]): proposals that match the features that were used to compute predictions. The fields ``proposal_boxes``, ``gt_classes`` are expected. Returns: list[Tensor]: A list of Tensors of predicted boxes for GT classes in case of class-specific box head. Element i of the list has shape (Ri, B), where Ri is the number of proposals for image i and B is the box dimension (4 or 5) """ if not len(proposals): return [] scores, proposal_deltas = predictions proposal_boxes = cat([p.proposal_boxes.tensor for p in proposals], dim=0) N, B = proposal_boxes.shape predict_boxes = self.box2box_transform.apply_deltas( proposal_deltas, proposal_boxes ) # Nx(KxB) K = predict_boxes.shape[1] // B if K > 1: gt_classes = torch.cat([p.gt_classes for p in proposals], dim=0) # Some proposals are ignored or have a background class. Their gt_classes # cannot be used as index. gt_classes = gt_classes.clamp_(0, K - 1) predict_boxes = predict_boxes.view(N, K, B)[ torch.arange(N, dtype=torch.long, device=predict_boxes.device), gt_classes ] num_prop_per_image = [len(p) for p in proposals] return predict_boxes.split(num_prop_per_image) def predict_boxes( self, predictions: Tuple[torch.Tensor, torch.Tensor], proposals: List[Instances] ): """ Args: predictions: return values of :meth:`forward()`. proposals (list[Instances]): proposals that match the features that were used to compute predictions. The ``proposal_boxes`` field is expected. Returns: list[Tensor]: A list of Tensors of predicted class-specific or class-agnostic boxes for each image. Element i has shape (Ri, K * B) or (Ri, B), where Ri is the number of proposals for image i and B is the box dimension (4 or 5) """ if not len(proposals): return [] _, proposal_deltas = predictions num_prop_per_image = [len(p) for p in proposals] proposal_boxes = cat([p.proposal_boxes.tensor for p in proposals], dim=0) predict_boxes = self.box2box_transform.apply_deltas( proposal_deltas, proposal_boxes, ) # Nx(KxB) return predict_boxes.split(num_prop_per_image) def predict_probs( self, predictions: Tuple[torch.Tensor, torch.Tensor], proposals: List[Instances] ): """ Args: predictions: return values of :meth:`forward()`. proposals (list[Instances]): proposals that match the features that were used to compute predictions. Returns: list[Tensor]: A list of Tensors of predicted class probabilities for each image. Element i has shape (Ri, K + 1), where Ri is the number of proposals for image i. """ scores, _ = predictions num_inst_per_image = [len(p) for p in proposals] if self.use_sigmoid_ce: probs = scores.sigmoid() else: probs = F.softmax(scores, dim=-1) return probs.split(num_inst_per_image, dim=0) ================================================ FILE: detectron2/detectron2/modeling/roi_heads/keypoint_head.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import List import torch from torch import nn from torch.nn import functional as F from detectron2.config import configurable from detectron2.layers import Conv2d, ConvTranspose2d, cat, interpolate from detectron2.structures import Instances, heatmaps_to_keypoints from detectron2.utils.events import get_event_storage from detectron2.utils.registry import Registry _TOTAL_SKIPPED = 0 __all__ = [ "ROI_KEYPOINT_HEAD_REGISTRY", "build_keypoint_head", "BaseKeypointRCNNHead", "KRCNNConvDeconvUpsampleHead", ] ROI_KEYPOINT_HEAD_REGISTRY = Registry("ROI_KEYPOINT_HEAD") ROI_KEYPOINT_HEAD_REGISTRY.__doc__ = """ Registry for keypoint heads, which make keypoint predictions from per-region features. The registered object will be called with `obj(cfg, input_shape)`. """ def build_keypoint_head(cfg, input_shape): """ Build a keypoint head from `cfg.MODEL.ROI_KEYPOINT_HEAD.NAME`. """ name = cfg.MODEL.ROI_KEYPOINT_HEAD.NAME return ROI_KEYPOINT_HEAD_REGISTRY.get(name)(cfg, input_shape) def keypoint_rcnn_loss(pred_keypoint_logits, instances, normalizer): """ Arguments: pred_keypoint_logits (Tensor): A tensor of shape (N, K, S, S) where N is the total number of instances in the batch, K is the number of keypoints, and S is the side length of the keypoint heatmap. The values are spatial logits. instances (list[Instances]): A list of M Instances, where M is the batch size. These instances are predictions from the model that are in 1:1 correspondence with pred_keypoint_logits. Each Instances should contain a `gt_keypoints` field containing a `structures.Keypoint` instance. normalizer (float): Normalize the loss by this amount. If not specified, we normalize by the number of visible keypoints in the minibatch. Returns a scalar tensor containing the loss. """ heatmaps = [] valid = [] keypoint_side_len = pred_keypoint_logits.shape[2] for instances_per_image in instances: if len(instances_per_image) == 0: continue keypoints = instances_per_image.gt_keypoints heatmaps_per_image, valid_per_image = keypoints.to_heatmap( instances_per_image.proposal_boxes.tensor, keypoint_side_len ) heatmaps.append(heatmaps_per_image.view(-1)) valid.append(valid_per_image.view(-1)) if len(heatmaps): keypoint_targets = cat(heatmaps, dim=0) valid = cat(valid, dim=0).to(dtype=torch.uint8) valid = torch.nonzero(valid).squeeze(1) # torch.mean (in binary_cross_entropy_with_logits) doesn't # accept empty tensors, so handle it separately if len(heatmaps) == 0 or valid.numel() == 0: global _TOTAL_SKIPPED _TOTAL_SKIPPED += 1 storage = get_event_storage() storage.put_scalar("kpts_num_skipped_batches", _TOTAL_SKIPPED, smoothing_hint=False) return pred_keypoint_logits.sum() * 0 N, K, H, W = pred_keypoint_logits.shape pred_keypoint_logits = pred_keypoint_logits.view(N * K, H * W) keypoint_loss = F.cross_entropy( pred_keypoint_logits[valid], keypoint_targets[valid], reduction="sum" ) # If a normalizer isn't specified, normalize by the number of visible keypoints in the minibatch if normalizer is None: normalizer = valid.numel() keypoint_loss /= normalizer return keypoint_loss def keypoint_rcnn_inference(pred_keypoint_logits: torch.Tensor, pred_instances: List[Instances]): """ Post process each predicted keypoint heatmap in `pred_keypoint_logits` into (x, y, score) and add it to the `pred_instances` as a `pred_keypoints` field. Args: pred_keypoint_logits (Tensor): A tensor of shape (R, K, S, S) where R is the total number of instances in the batch, K is the number of keypoints, and S is the side length of the keypoint heatmap. The values are spatial logits. pred_instances (list[Instances]): A list of N Instances, where N is the number of images. Returns: None. Each element in pred_instances will contain extra "pred_keypoints" and "pred_keypoint_heatmaps" fields. "pred_keypoints" is a tensor of shape (#instance, K, 3) where the last dimension corresponds to (x, y, score). The scores are larger than 0. "pred_keypoint_heatmaps" contains the raw keypoint logits as passed to this function. """ # flatten all bboxes from all images together (list[Boxes] -> Rx4 tensor) bboxes_flat = cat([b.pred_boxes.tensor for b in pred_instances], dim=0) pred_keypoint_logits = pred_keypoint_logits.detach() keypoint_results = heatmaps_to_keypoints(pred_keypoint_logits, bboxes_flat.detach()) num_instances_per_image = [len(i) for i in pred_instances] keypoint_results = keypoint_results[:, :, [0, 1, 3]].split(num_instances_per_image, dim=0) heatmap_results = pred_keypoint_logits.split(num_instances_per_image, dim=0) for keypoint_results_per_image, heatmap_results_per_image, instances_per_image in zip( keypoint_results, heatmap_results, pred_instances ): # keypoint_results_per_image is (num instances)x(num keypoints)x(x, y, score) # heatmap_results_per_image is (num instances)x(num keypoints)x(side)x(side) instances_per_image.pred_keypoints = keypoint_results_per_image instances_per_image.pred_keypoint_heatmaps = heatmap_results_per_image class BaseKeypointRCNNHead(nn.Module): """ Implement the basic Keypoint R-CNN losses and inference logic described in Sec. 5 of :paper:`Mask R-CNN`. """ @configurable def __init__(self, *, num_keypoints, loss_weight=1.0, loss_normalizer=1.0): """ NOTE: this interface is experimental. Args: num_keypoints (int): number of keypoints to predict loss_weight (float): weight to multiple on the keypoint loss loss_normalizer (float or str): If float, divide the loss by `loss_normalizer * #images`. If 'visible', the loss is normalized by the total number of visible keypoints across images. """ super().__init__() self.num_keypoints = num_keypoints self.loss_weight = loss_weight assert loss_normalizer == "visible" or isinstance(loss_normalizer, float), loss_normalizer self.loss_normalizer = loss_normalizer @classmethod def from_config(cls, cfg, input_shape): ret = { "loss_weight": cfg.MODEL.ROI_KEYPOINT_HEAD.LOSS_WEIGHT, "num_keypoints": cfg.MODEL.ROI_KEYPOINT_HEAD.NUM_KEYPOINTS, } normalize_by_visible = ( cfg.MODEL.ROI_KEYPOINT_HEAD.NORMALIZE_LOSS_BY_VISIBLE_KEYPOINTS ) # noqa if not normalize_by_visible: batch_size_per_image = cfg.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE positive_sample_fraction = cfg.MODEL.ROI_HEADS.POSITIVE_FRACTION ret["loss_normalizer"] = ( ret["num_keypoints"] * batch_size_per_image * positive_sample_fraction ) else: ret["loss_normalizer"] = "visible" return ret def forward(self, x, instances: List[Instances]): """ Args: x: input 4D region feature(s) provided by :class:`ROIHeads`. instances (list[Instances]): contains the boxes & labels corresponding to the input features. Exact format is up to its caller to decide. Typically, this is the foreground instances in training, with "proposal_boxes" field and other gt annotations. In inference, it contains boxes that are already predicted. Returns: A dict of losses if in training. The predicted "instances" if in inference. """ x = self.layers(x) if self.training: num_images = len(instances) normalizer = ( None if self.loss_normalizer == "visible" else num_images * self.loss_normalizer ) return { "loss_keypoint": keypoint_rcnn_loss(x, instances, normalizer=normalizer) * self.loss_weight } else: keypoint_rcnn_inference(x, instances) return instances def layers(self, x): """ Neural network layers that makes predictions from regional input features. """ raise NotImplementedError # To get torchscript support, we make the head a subclass of `nn.Sequential`. # Therefore, to add new layers in this head class, please make sure they are # added in the order they will be used in forward(). @ROI_KEYPOINT_HEAD_REGISTRY.register() class KRCNNConvDeconvUpsampleHead(BaseKeypointRCNNHead, nn.Sequential): """ A standard keypoint head containing a series of 3x3 convs, followed by a transpose convolution and bilinear interpolation for upsampling. It is described in Sec. 5 of :paper:`Mask R-CNN`. """ @configurable def __init__(self, input_shape, *, num_keypoints, conv_dims, **kwargs): """ NOTE: this interface is experimental. Args: input_shape (ShapeSpec): shape of the input feature conv_dims: an iterable of output channel counts for each conv in the head e.g. (512, 512, 512) for three convs outputting 512 channels. """ super().__init__(num_keypoints=num_keypoints, **kwargs) # default up_scale to 2.0 (this can be made an option) up_scale = 2.0 in_channels = input_shape.channels for idx, layer_channels in enumerate(conv_dims, 1): module = Conv2d(in_channels, layer_channels, 3, stride=1, padding=1) self.add_module("conv_fcn{}".format(idx), module) self.add_module("conv_fcn_relu{}".format(idx), nn.ReLU()) in_channels = layer_channels deconv_kernel = 4 self.score_lowres = ConvTranspose2d( in_channels, num_keypoints, deconv_kernel, stride=2, padding=deconv_kernel // 2 - 1 ) self.up_scale = up_scale for name, param in self.named_parameters(): if "bias" in name: nn.init.constant_(param, 0) elif "weight" in name: # Caffe2 implementation uses MSRAFill, which in fact # corresponds to kaiming_normal_ in PyTorch nn.init.kaiming_normal_(param, mode="fan_out", nonlinearity="relu") @classmethod def from_config(cls, cfg, input_shape): ret = super().from_config(cfg, input_shape) ret["input_shape"] = input_shape ret["conv_dims"] = cfg.MODEL.ROI_KEYPOINT_HEAD.CONV_DIMS return ret def layers(self, x): for layer in self: x = layer(x) x = interpolate(x, scale_factor=self.up_scale, mode="bilinear", align_corners=False) return x ================================================ FILE: detectron2/detectron2/modeling/roi_heads/mask_head.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import List import fvcore.nn.weight_init as weight_init import torch from torch import nn from torch.nn import functional as F from detectron2.config import configurable from detectron2.layers import Conv2d, ConvTranspose2d, ShapeSpec, cat, get_norm from detectron2.layers.wrappers import move_device_like from detectron2.structures import Instances from detectron2.utils.events import get_event_storage from detectron2.utils.registry import Registry __all__ = [ "BaseMaskRCNNHead", "MaskRCNNConvUpsampleHead", "build_mask_head", "ROI_MASK_HEAD_REGISTRY", ] ROI_MASK_HEAD_REGISTRY = Registry("ROI_MASK_HEAD") ROI_MASK_HEAD_REGISTRY.__doc__ = """ Registry for mask heads, which predicts instance masks given per-region features. The registered object will be called with `obj(cfg, input_shape)`. """ @torch.jit.unused def mask_rcnn_loss(pred_mask_logits: torch.Tensor, instances: List[Instances], vis_period: int = 0): """ Compute the mask prediction loss defined in the Mask R-CNN paper. Args: pred_mask_logits (Tensor): A tensor of shape (B, C, Hmask, Wmask) or (B, 1, Hmask, Wmask) for class-specific or class-agnostic, where B is the total number of predicted masks in all images, C is the number of foreground classes, and Hmask, Wmask are the height and width of the mask predictions. The values are logits. instances (list[Instances]): A list of N Instances, where N is the number of images in the batch. These instances are in 1:1 correspondence with the pred_mask_logits. The ground-truth labels (class, box, mask, ...) associated with each instance are stored in fields. vis_period (int): the period (in steps) to dump visualization. Returns: mask_loss (Tensor): A scalar tensor containing the loss. """ cls_agnostic_mask = pred_mask_logits.size(1) == 1 total_num_masks = pred_mask_logits.size(0) mask_side_len = pred_mask_logits.size(2) assert pred_mask_logits.size(2) == pred_mask_logits.size(3), "Mask prediction must be square!" gt_classes = [] gt_masks = [] for instances_per_image in instances: if len(instances_per_image) == 0: continue if not cls_agnostic_mask: gt_classes_per_image = instances_per_image.gt_classes.to(dtype=torch.int64) gt_classes.append(gt_classes_per_image) gt_masks_per_image = instances_per_image.gt_masks.crop_and_resize( instances_per_image.proposal_boxes.tensor, mask_side_len ).to(device=pred_mask_logits.device) # A tensor of shape (N, M, M), N=#instances in the image; M=mask_side_len gt_masks.append(gt_masks_per_image) if len(gt_masks) == 0: return pred_mask_logits.sum() * 0 gt_masks = cat(gt_masks, dim=0) if cls_agnostic_mask: pred_mask_logits = pred_mask_logits[:, 0] else: indices = torch.arange(total_num_masks) gt_classes = cat(gt_classes, dim=0) pred_mask_logits = pred_mask_logits[indices, gt_classes] if gt_masks.dtype == torch.bool: gt_masks_bool = gt_masks else: # Here we allow gt_masks to be float as well (depend on the implementation of rasterize()) gt_masks_bool = gt_masks > 0.5 gt_masks = gt_masks.to(dtype=torch.float32) # Log the training accuracy (using gt classes and 0.5 threshold) mask_incorrect = (pred_mask_logits > 0.0) != gt_masks_bool mask_accuracy = 1 - (mask_incorrect.sum().item() / max(mask_incorrect.numel(), 1.0)) num_positive = gt_masks_bool.sum().item() false_positive = (mask_incorrect & ~gt_masks_bool).sum().item() / max( gt_masks_bool.numel() - num_positive, 1.0 ) false_negative = (mask_incorrect & gt_masks_bool).sum().item() / max(num_positive, 1.0) storage = get_event_storage() storage.put_scalar("mask_rcnn/accuracy", mask_accuracy) storage.put_scalar("mask_rcnn/false_positive", false_positive) storage.put_scalar("mask_rcnn/false_negative", false_negative) if vis_period > 0 and storage.iter % vis_period == 0: pred_masks = pred_mask_logits.sigmoid() vis_masks = torch.cat([pred_masks, gt_masks], axis=2) name = "Left: mask prediction; Right: mask GT" for idx, vis_mask in enumerate(vis_masks): vis_mask = torch.stack([vis_mask] * 3, axis=0) storage.put_image(name + f" ({idx})", vis_mask) mask_loss = F.binary_cross_entropy_with_logits(pred_mask_logits, gt_masks, reduction="mean") return mask_loss def mask_rcnn_inference(pred_mask_logits: torch.Tensor, pred_instances: List[Instances]): """ Convert pred_mask_logits to estimated foreground probability masks while also extracting only the masks for the predicted classes in pred_instances. For each predicted box, the mask of the same class is attached to the instance by adding a new "pred_masks" field to pred_instances. Args: pred_mask_logits (Tensor): A tensor of shape (B, C, Hmask, Wmask) or (B, 1, Hmask, Wmask) for class-specific or class-agnostic, where B is the total number of predicted masks in all images, C is the number of foreground classes, and Hmask, Wmask are the height and width of the mask predictions. The values are logits. pred_instances (list[Instances]): A list of N Instances, where N is the number of images in the batch. Each Instances must have field "pred_classes". Returns: None. pred_instances will contain an extra "pred_masks" field storing a mask of size (Hmask, Wmask) for predicted class. Note that the masks are returned as a soft (non-quantized) masks the resolution predicted by the network; post-processing steps, such as resizing the predicted masks to the original image resolution and/or binarizing them, is left to the caller. """ cls_agnostic_mask = pred_mask_logits.size(1) == 1 if cls_agnostic_mask: mask_probs_pred = pred_mask_logits.sigmoid() else: # Select masks corresponding to the predicted classes num_masks = pred_mask_logits.shape[0] class_pred = cat([i.pred_classes for i in pred_instances]) device = ( class_pred.device if torch.jit.is_scripting() else ("cpu" if torch.jit.is_tracing() else class_pred.device) ) indices = move_device_like(torch.arange(num_masks, device=device), class_pred) mask_probs_pred = pred_mask_logits[indices, class_pred][:, None].sigmoid() # mask_probs_pred.shape: (B, 1, Hmask, Wmask) num_boxes_per_image = [len(i) for i in pred_instances] mask_probs_pred = mask_probs_pred.split(num_boxes_per_image, dim=0) for prob, instances in zip(mask_probs_pred, pred_instances): instances.pred_masks = prob # (1, Hmask, Wmask) class BaseMaskRCNNHead(nn.Module): """ Implement the basic Mask R-CNN losses and inference logic described in :paper:`Mask R-CNN` """ @configurable def __init__(self, *, loss_weight: float = 1.0, vis_period: int = 0): """ NOTE: this interface is experimental. Args: loss_weight (float): multiplier of the loss vis_period (int): visualization period """ super().__init__() self.vis_period = vis_period self.loss_weight = loss_weight @classmethod def from_config(cls, cfg, input_shape): return {"vis_period": cfg.VIS_PERIOD} def forward(self, x, instances: List[Instances]): """ Args: x: input region feature(s) provided by :class:`ROIHeads`. instances (list[Instances]): contains the boxes & labels corresponding to the input features. Exact format is up to its caller to decide. Typically, this is the foreground instances in training, with "proposal_boxes" field and other gt annotations. In inference, it contains boxes that are already predicted. Returns: A dict of losses in training. The predicted "instances" in inference. """ x = self.layers(x) if self.training: return {"loss_mask": mask_rcnn_loss(x, instances, self.vis_period) * self.loss_weight} else: mask_rcnn_inference(x, instances) return instances def layers(self, x): """ Neural network layers that makes predictions from input features. """ raise NotImplementedError # To get torchscript support, we make the head a subclass of `nn.Sequential`. # Therefore, to add new layers in this head class, please make sure they are # added in the order they will be used in forward(). @ROI_MASK_HEAD_REGISTRY.register() class MaskRCNNConvUpsampleHead(BaseMaskRCNNHead, nn.Sequential): """ A mask head with several conv layers, plus an upsample layer (with `ConvTranspose2d`). Predictions are made with a final 1x1 conv layer. """ @configurable def __init__(self, input_shape: ShapeSpec, *, num_classes, conv_dims, conv_norm="", **kwargs): """ NOTE: this interface is experimental. Args: input_shape (ShapeSpec): shape of the input feature num_classes (int): the number of foreground classes (i.e. background is not included). 1 if using class agnostic prediction. conv_dims (list[int]): a list of N>0 integers representing the output dimensions of N-1 conv layers and the last upsample layer. conv_norm (str or callable): normalization for the conv layers. See :func:`detectron2.layers.get_norm` for supported types. """ super().__init__(**kwargs) assert len(conv_dims) >= 1, "conv_dims have to be non-empty!" self.conv_norm_relus = [] cur_channels = input_shape.channels for k, conv_dim in enumerate(conv_dims[:-1]): conv = Conv2d( cur_channels, conv_dim, kernel_size=3, stride=1, padding=1, bias=not conv_norm, norm=get_norm(conv_norm, conv_dim), activation=nn.ReLU(), ) self.add_module("mask_fcn{}".format(k + 1), conv) self.conv_norm_relus.append(conv) cur_channels = conv_dim self.deconv = ConvTranspose2d( cur_channels, conv_dims[-1], kernel_size=2, stride=2, padding=0 ) self.add_module("deconv_relu", nn.ReLU()) cur_channels = conv_dims[-1] self.predictor = Conv2d(cur_channels, num_classes, kernel_size=1, stride=1, padding=0) for layer in self.conv_norm_relus + [self.deconv]: weight_init.c2_msra_fill(layer) # use normal distribution initialization for mask prediction layer nn.init.normal_(self.predictor.weight, std=0.001) if self.predictor.bias is not None: nn.init.constant_(self.predictor.bias, 0) @classmethod def from_config(cls, cfg, input_shape): ret = super().from_config(cfg, input_shape) conv_dim = cfg.MODEL.ROI_MASK_HEAD.CONV_DIM num_conv = cfg.MODEL.ROI_MASK_HEAD.NUM_CONV ret.update( conv_dims=[conv_dim] * (num_conv + 1), # +1 for ConvTranspose conv_norm=cfg.MODEL.ROI_MASK_HEAD.NORM, input_shape=input_shape, ) if cfg.MODEL.ROI_MASK_HEAD.CLS_AGNOSTIC_MASK: ret["num_classes"] = 1 else: ret["num_classes"] = cfg.MODEL.ROI_HEADS.NUM_CLASSES return ret def layers(self, x): for layer in self: x = layer(x) return x def build_mask_head(cfg, input_shape): """ Build a mask head defined by `cfg.MODEL.ROI_MASK_HEAD.NAME`. """ name = cfg.MODEL.ROI_MASK_HEAD.NAME return ROI_MASK_HEAD_REGISTRY.get(name)(cfg, input_shape) ================================================ FILE: detectron2/detectron2/modeling/roi_heads/roi_heads.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import inspect import logging import numpy as np from typing import Dict, List, Optional, Tuple import torch from torch import nn from detectron2.config import configurable from detectron2.layers import ShapeSpec, nonzero_tuple from detectron2.structures import Boxes, ImageList, Instances, pairwise_iou from detectron2.utils.events import get_event_storage from detectron2.utils.registry import Registry from ..backbone.resnet import BottleneckBlock, ResNet from ..matcher import Matcher from ..poolers import ROIPooler from ..proposal_generator.proposal_utils import add_ground_truth_to_proposals from ..sampling import subsample_labels from .box_head import build_box_head from .fast_rcnn import FastRCNNOutputLayers from .keypoint_head import build_keypoint_head from .mask_head import build_mask_head ROI_HEADS_REGISTRY = Registry("ROI_HEADS") ROI_HEADS_REGISTRY.__doc__ = """ Registry for ROI heads in a generalized R-CNN model. ROIHeads take feature maps and region proposals, and perform per-region computation. The registered object will be called with `obj(cfg, input_shape)`. The call is expected to return an :class:`ROIHeads`. """ logger = logging.getLogger(__name__) def build_roi_heads(cfg, input_shape): """ Build ROIHeads defined by `cfg.MODEL.ROI_HEADS.NAME`. """ name = cfg.MODEL.ROI_HEADS.NAME return ROI_HEADS_REGISTRY.get(name)(cfg, input_shape) def select_foreground_proposals( proposals: List[Instances], bg_label: int ) -> Tuple[List[Instances], List[torch.Tensor]]: """ Given a list of N Instances (for N images), each containing a `gt_classes` field, return a list of Instances that contain only instances with `gt_classes != -1 && gt_classes != bg_label`. Args: proposals (list[Instances]): A list of N Instances, where N is the number of images in the batch. bg_label: label index of background class. Returns: list[Instances]: N Instances, each contains only the selected foreground instances. list[Tensor]: N boolean vector, correspond to the selection mask of each Instances object. True for selected instances. """ assert isinstance(proposals, (list, tuple)) assert isinstance(proposals[0], Instances) assert proposals[0].has("gt_classes") fg_proposals = [] fg_selection_masks = [] for proposals_per_image in proposals: gt_classes = proposals_per_image.gt_classes fg_selection_mask = (gt_classes != -1) & (gt_classes != bg_label) fg_idxs = fg_selection_mask.nonzero().squeeze(1) fg_proposals.append(proposals_per_image[fg_idxs]) fg_selection_masks.append(fg_selection_mask) return fg_proposals, fg_selection_masks def select_proposals_with_visible_keypoints(proposals: List[Instances]) -> List[Instances]: """ Args: proposals (list[Instances]): a list of N Instances, where N is the number of images. Returns: proposals: only contains proposals with at least one visible keypoint. Note that this is still slightly different from Detectron. In Detectron, proposals for training keypoint head are re-sampled from all the proposals with IOU>threshold & >=1 visible keypoint. Here, the proposals are first sampled from all proposals with IOU>threshold, then proposals with no visible keypoint are filtered out. This strategy seems to make no difference on Detectron and is easier to implement. """ ret = [] all_num_fg = [] for proposals_per_image in proposals: # If empty/unannotated image (hard negatives), skip filtering for train if len(proposals_per_image) == 0: ret.append(proposals_per_image) continue gt_keypoints = proposals_per_image.gt_keypoints.tensor # #fg x K x 3 vis_mask = gt_keypoints[:, :, 2] >= 1 xs, ys = gt_keypoints[:, :, 0], gt_keypoints[:, :, 1] proposal_boxes = proposals_per_image.proposal_boxes.tensor.unsqueeze(dim=1) # #fg x 1 x 4 kp_in_box = ( (xs >= proposal_boxes[:, :, 0]) & (xs <= proposal_boxes[:, :, 2]) & (ys >= proposal_boxes[:, :, 1]) & (ys <= proposal_boxes[:, :, 3]) ) selection = (kp_in_box & vis_mask).any(dim=1) selection_idxs = nonzero_tuple(selection)[0] all_num_fg.append(selection_idxs.numel()) ret.append(proposals_per_image[selection_idxs]) storage = get_event_storage() storage.put_scalar("keypoint_head/num_fg_samples", np.mean(all_num_fg)) return ret class ROIHeads(torch.nn.Module): """ ROIHeads perform all per-region computation in an R-CNN. It typically contains logic to 1. (in training only) match proposals with ground truth and sample them 2. crop the regions and extract per-region features using proposals 3. make per-region predictions with different heads It can have many variants, implemented as subclasses of this class. This base class contains the logic to match/sample proposals. But it is not necessary to inherit this class if the sampling logic is not needed. """ @configurable def __init__( self, *, num_classes, batch_size_per_image, positive_fraction, proposal_matcher, proposal_append_gt=True, ): """ NOTE: this interface is experimental. Args: num_classes (int): number of foreground classes (i.e. background is not included) batch_size_per_image (int): number of proposals to sample for training positive_fraction (float): fraction of positive (foreground) proposals to sample for training. proposal_matcher (Matcher): matcher that matches proposals and ground truth proposal_append_gt (bool): whether to include ground truth as proposals as well """ super().__init__() self.batch_size_per_image = batch_size_per_image self.positive_fraction = positive_fraction self.num_classes = num_classes self.proposal_matcher = proposal_matcher self.proposal_append_gt = proposal_append_gt @classmethod def from_config(cls, cfg): return { "batch_size_per_image": cfg.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE, "positive_fraction": cfg.MODEL.ROI_HEADS.POSITIVE_FRACTION, "num_classes": cfg.MODEL.ROI_HEADS.NUM_CLASSES, "proposal_append_gt": cfg.MODEL.ROI_HEADS.PROPOSAL_APPEND_GT, # Matcher to assign box proposals to gt boxes "proposal_matcher": Matcher( cfg.MODEL.ROI_HEADS.IOU_THRESHOLDS, cfg.MODEL.ROI_HEADS.IOU_LABELS, allow_low_quality_matches=False, ), } def _sample_proposals( self, matched_idxs: torch.Tensor, matched_labels: torch.Tensor, gt_classes: torch.Tensor ) -> Tuple[torch.Tensor, torch.Tensor]: """ Based on the matching between N proposals and M groundtruth, sample the proposals and set their classification labels. Args: matched_idxs (Tensor): a vector of length N, each is the best-matched gt index in [0, M) for each proposal. matched_labels (Tensor): a vector of length N, the matcher's label (one of cfg.MODEL.ROI_HEADS.IOU_LABELS) for each proposal. gt_classes (Tensor): a vector of length M. Returns: Tensor: a vector of indices of sampled proposals. Each is in [0, N). Tensor: a vector of the same length, the classification label for each sampled proposal. Each sample is labeled as either a category in [0, num_classes) or the background (num_classes). """ has_gt = gt_classes.numel() > 0 # Get the corresponding GT for each proposal if has_gt: gt_classes = gt_classes[matched_idxs] # Label unmatched proposals (0 label from matcher) as background (label=num_classes) gt_classes[matched_labels == 0] = self.num_classes # Label ignore proposals (-1 label) gt_classes[matched_labels == -1] = -1 else: gt_classes = torch.zeros_like(matched_idxs) + self.num_classes sampled_fg_idxs, sampled_bg_idxs = subsample_labels( gt_classes, self.batch_size_per_image, self.positive_fraction, self.num_classes ) sampled_idxs = torch.cat([sampled_fg_idxs, sampled_bg_idxs], dim=0) return sampled_idxs, gt_classes[sampled_idxs] @torch.no_grad() def label_and_sample_proposals( self, proposals: List[Instances], targets: List[Instances] ) -> List[Instances]: """ Prepare some proposals to be used to train the ROI heads. It performs box matching between `proposals` and `targets`, and assigns training labels to the proposals. It returns ``self.batch_size_per_image`` random samples from proposals and groundtruth boxes, with a fraction of positives that is no larger than ``self.positive_fraction``. Args: See :meth:`ROIHeads.forward` Returns: list[Instances]: length `N` list of `Instances`s containing the proposals sampled for training. Each `Instances` has the following fields: - proposal_boxes: the proposal boxes - gt_boxes: the ground-truth box that the proposal is assigned to (this is only meaningful if the proposal has a label > 0; if label = 0 then the ground-truth box is random) Other fields such as "gt_classes", "gt_masks", that's included in `targets`. """ # Augment proposals with ground-truth boxes. # In the case of learned proposals (e.g., RPN), when training starts # the proposals will be low quality due to random initialization. # It's possible that none of these initial # proposals have high enough overlap with the gt objects to be used # as positive examples for the second stage components (box head, # cls head, mask head). Adding the gt boxes to the set of proposals # ensures that the second stage components will have some positive # examples from the start of training. For RPN, this augmentation improves # convergence and empirically improves box AP on COCO by about 0.5 # points (under one tested configuration). if self.proposal_append_gt: proposals = add_ground_truth_to_proposals(targets, proposals) proposals_with_gt = [] num_fg_samples = [] num_bg_samples = [] for proposals_per_image, targets_per_image in zip(proposals, targets): has_gt = len(targets_per_image) > 0 match_quality_matrix = pairwise_iou( targets_per_image.gt_boxes, proposals_per_image.proposal_boxes ) matched_idxs, matched_labels = self.proposal_matcher(match_quality_matrix) sampled_idxs, gt_classes = self._sample_proposals( matched_idxs, matched_labels, targets_per_image.gt_classes ) # Set target attributes of the sampled proposals: proposals_per_image = proposals_per_image[sampled_idxs] proposals_per_image.gt_classes = gt_classes if has_gt: sampled_targets = matched_idxs[sampled_idxs] # We index all the attributes of targets that start with "gt_" # and have not been added to proposals yet (="gt_classes"). # NOTE: here the indexing waste some compute, because heads # like masks, keypoints, etc, will filter the proposals again, # (by foreground/background, or number of keypoints in the image, etc) # so we essentially index the data twice. for (trg_name, trg_value) in targets_per_image.get_fields().items(): if trg_name.startswith("gt_") and not proposals_per_image.has(trg_name): proposals_per_image.set(trg_name, trg_value[sampled_targets]) # If no GT is given in the image, we don't know what a dummy gt value can be. # Therefore the returned proposals won't have any gt_* fields, except for a # gt_classes full of background label. num_bg_samples.append((gt_classes == self.num_classes).sum().item()) num_fg_samples.append(gt_classes.numel() - num_bg_samples[-1]) proposals_with_gt.append(proposals_per_image) # Log the number of fg/bg samples that are selected for training ROI heads storage = get_event_storage() storage.put_scalar("roi_head/num_fg_samples", np.mean(num_fg_samples)) storage.put_scalar("roi_head/num_bg_samples", np.mean(num_bg_samples)) return proposals_with_gt def forward( self, images: ImageList, features: Dict[str, torch.Tensor], proposals: List[Instances], targets: Optional[List[Instances]] = None, ) -> Tuple[List[Instances], Dict[str, torch.Tensor]]: """ Args: images (ImageList): features (dict[str,Tensor]): input data as a mapping from feature map name to tensor. Axis 0 represents the number of images `N` in the input data; axes 1-3 are channels, height, and width, which may vary between feature maps (e.g., if a feature pyramid is used). proposals (list[Instances]): length `N` list of `Instances`. The i-th `Instances` contains object proposals for the i-th input image, with fields "proposal_boxes" and "objectness_logits". targets (list[Instances], optional): length `N` list of `Instances`. The i-th `Instances` contains the ground-truth per-instance annotations for the i-th input image. Specify `targets` during training only. It may have the following fields: - gt_boxes: the bounding box of each instance. - gt_classes: the label for each instance with a category ranging in [0, #class]. - gt_masks: PolygonMasks or BitMasks, the ground-truth masks of each instance. - gt_keypoints: NxKx3, the groud-truth keypoints for each instance. Returns: list[Instances]: length `N` list of `Instances` containing the detected instances. Returned during inference only; may be [] during training. dict[str->Tensor]: mapping from a named loss to a tensor storing the loss. Used during training only. """ raise NotImplementedError() @ROI_HEADS_REGISTRY.register() class Res5ROIHeads(ROIHeads): """ The ROIHeads in a typical "C4" R-CNN model, where the box and mask head share the cropping and the per-region feature computation by a Res5 block. See :paper:`ResNet` Appendix A. """ @configurable def __init__( self, *, in_features: List[str], pooler: ROIPooler, res5: nn.Module, box_predictor: nn.Module, mask_head: Optional[nn.Module] = None, **kwargs, ): """ NOTE: this interface is experimental. Args: in_features (list[str]): list of backbone feature map names to use for feature extraction pooler (ROIPooler): pooler to extra region features from backbone res5 (nn.Sequential): a CNN to compute per-region features, to be used by ``box_predictor`` and ``mask_head``. Typically this is a "res5" block from a ResNet. box_predictor (nn.Module): make box predictions from the feature. Should have the same interface as :class:`FastRCNNOutputLayers`. mask_head (nn.Module): transform features to make mask predictions """ super().__init__(**kwargs) self.in_features = in_features self.pooler = pooler if isinstance(res5, (list, tuple)): res5 = nn.Sequential(*res5) self.res5 = res5 self.box_predictor = box_predictor self.mask_on = mask_head is not None if self.mask_on: self.mask_head = mask_head @classmethod def from_config(cls, cfg, input_shape): # fmt: off ret = super().from_config(cfg) in_features = ret["in_features"] = cfg.MODEL.ROI_HEADS.IN_FEATURES pooler_resolution = cfg.MODEL.ROI_BOX_HEAD.POOLER_RESOLUTION pooler_type = cfg.MODEL.ROI_BOX_HEAD.POOLER_TYPE pooler_scales = (1.0 / input_shape[in_features[0]].stride, ) sampling_ratio = cfg.MODEL.ROI_BOX_HEAD.POOLER_SAMPLING_RATIO mask_on = cfg.MODEL.MASK_ON # fmt: on assert not cfg.MODEL.KEYPOINT_ON assert len(in_features) == 1 ret["pooler"] = ROIPooler( output_size=pooler_resolution, scales=pooler_scales, sampling_ratio=sampling_ratio, pooler_type=pooler_type, ) # Compatbility with old moco code. Might be useful. # See notes in StandardROIHeads.from_config if not inspect.ismethod(cls._build_res5_block): logger.warning( "The behavior of _build_res5_block may change. " "Please do not depend on private methods." ) cls._build_res5_block = classmethod(cls._build_res5_block) ret["res5"], out_channels = cls._build_res5_block(cfg) ret["box_predictor"] = FastRCNNOutputLayers( cfg, ShapeSpec(channels=out_channels, height=1, width=1) ) if mask_on: ret["mask_head"] = build_mask_head( cfg, ShapeSpec(channels=out_channels, width=pooler_resolution, height=pooler_resolution), ) return ret @classmethod def _build_res5_block(cls, cfg): # fmt: off stage_channel_factor = 2 ** 3 # res5 is 8x res2 num_groups = cfg.MODEL.RESNETS.NUM_GROUPS width_per_group = cfg.MODEL.RESNETS.WIDTH_PER_GROUP bottleneck_channels = num_groups * width_per_group * stage_channel_factor out_channels = cfg.MODEL.RESNETS.RES2_OUT_CHANNELS * stage_channel_factor stride_in_1x1 = cfg.MODEL.RESNETS.STRIDE_IN_1X1 norm = cfg.MODEL.RESNETS.NORM assert not cfg.MODEL.RESNETS.DEFORM_ON_PER_STAGE[-1], \ "Deformable conv is not yet supported in res5 head." # fmt: on blocks = ResNet.make_stage( BottleneckBlock, 3, stride_per_block=[2, 1, 1], in_channels=out_channels // 2, bottleneck_channels=bottleneck_channels, out_channels=out_channels, num_groups=num_groups, norm=norm, stride_in_1x1=stride_in_1x1, ) return nn.Sequential(*blocks), out_channels def _shared_roi_transform(self, features: List[torch.Tensor], boxes: List[Boxes]): x = self.pooler(features, boxes) return self.res5(x) def forward( self, images: ImageList, features: Dict[str, torch.Tensor], proposals: List[Instances], targets: Optional[List[Instances]] = None, ): """ See :meth:`ROIHeads.forward`. """ del images if self.training: assert targets proposals = self.label_and_sample_proposals(proposals, targets) del targets proposal_boxes = [x.proposal_boxes for x in proposals] box_features = self._shared_roi_transform( [features[f] for f in self.in_features], proposal_boxes ) predictions = self.box_predictor(box_features.mean(dim=[2, 3])) if self.training: del features losses = self.box_predictor.losses(predictions, proposals) if self.mask_on: proposals, fg_selection_masks = select_foreground_proposals( proposals, self.num_classes ) # Since the ROI feature transform is shared between boxes and masks, # we don't need to recompute features. The mask loss is only defined # on foreground proposals, so we need to select out the foreground # features. mask_features = box_features[torch.cat(fg_selection_masks, dim=0)] del box_features losses.update(self.mask_head(mask_features, proposals)) return [], losses else: pred_instances, _ = self.box_predictor.inference(predictions, proposals) pred_instances = self.forward_with_given_boxes(features, pred_instances) return pred_instances, {} def forward_with_given_boxes( self, features: Dict[str, torch.Tensor], instances: List[Instances] ) -> List[Instances]: """ Use the given boxes in `instances` to produce other (non-box) per-ROI outputs. Args: features: same as in `forward()` instances (list[Instances]): instances to predict other outputs. Expect the keys "pred_boxes" and "pred_classes" to exist. Returns: instances (Instances): the same `Instances` object, with extra fields such as `pred_masks` or `pred_keypoints`. """ assert not self.training assert instances[0].has("pred_boxes") and instances[0].has("pred_classes") if self.mask_on: feature_list = [features[f] for f in self.in_features] x = self._shared_roi_transform(feature_list, [x.pred_boxes for x in instances]) return self.mask_head(x, instances) else: return instances @ROI_HEADS_REGISTRY.register() class StandardROIHeads(ROIHeads): """ It's "standard" in a sense that there is no ROI transform sharing or feature sharing between tasks. Each head independently processes the input features by each head's own pooler and head. This class is used by most models, such as FPN and C5. To implement more models, you can subclass it and implement a different :meth:`forward()` or a head. """ @configurable def __init__( self, *, box_in_features: List[str], box_pooler: ROIPooler, box_head: nn.Module, box_predictor: nn.Module, mask_in_features: Optional[List[str]] = None, mask_pooler: Optional[ROIPooler] = None, mask_head: Optional[nn.Module] = None, keypoint_in_features: Optional[List[str]] = None, keypoint_pooler: Optional[ROIPooler] = None, keypoint_head: Optional[nn.Module] = None, train_on_pred_boxes: bool = False, **kwargs, ): """ NOTE: this interface is experimental. Args: box_in_features (list[str]): list of feature names to use for the box head. box_pooler (ROIPooler): pooler to extra region features for box head box_head (nn.Module): transform features to make box predictions box_predictor (nn.Module): make box predictions from the feature. Should have the same interface as :class:`FastRCNNOutputLayers`. mask_in_features (list[str]): list of feature names to use for the mask pooler or mask head. None if not using mask head. mask_pooler (ROIPooler): pooler to extract region features from image features. The mask head will then take region features to make predictions. If None, the mask head will directly take the dict of image features defined by `mask_in_features` mask_head (nn.Module): transform features to make mask predictions keypoint_in_features, keypoint_pooler, keypoint_head: similar to ``mask_*``. train_on_pred_boxes (bool): whether to use proposal boxes or predicted boxes from the box head to train other heads. """ super().__init__(**kwargs) # keep self.in_features for backward compatibility self.in_features = self.box_in_features = box_in_features self.box_pooler = box_pooler self.box_head = box_head self.box_predictor = box_predictor self.mask_on = mask_in_features is not None if self.mask_on: self.mask_in_features = mask_in_features self.mask_pooler = mask_pooler self.mask_head = mask_head self.keypoint_on = keypoint_in_features is not None if self.keypoint_on: self.keypoint_in_features = keypoint_in_features self.keypoint_pooler = keypoint_pooler self.keypoint_head = keypoint_head self.train_on_pred_boxes = train_on_pred_boxes @classmethod def from_config(cls, cfg, input_shape): ret = super().from_config(cfg) ret["train_on_pred_boxes"] = cfg.MODEL.ROI_BOX_HEAD.TRAIN_ON_PRED_BOXES # Subclasses that have not been updated to use from_config style construction # may have overridden _init_*_head methods. In this case, those overridden methods # will not be classmethods and we need to avoid trying to call them here. # We test for this with ismethod which only returns True for bound methods of cls. # Such subclasses will need to handle calling their overridden _init_*_head methods. if inspect.ismethod(cls._init_box_head): ret.update(cls._init_box_head(cfg, input_shape)) if inspect.ismethod(cls._init_mask_head): ret.update(cls._init_mask_head(cfg, input_shape)) if inspect.ismethod(cls._init_keypoint_head): ret.update(cls._init_keypoint_head(cfg, input_shape)) return ret @classmethod def _init_box_head(cls, cfg, input_shape): # fmt: off in_features = cfg.MODEL.ROI_HEADS.IN_FEATURES pooler_resolution = cfg.MODEL.ROI_BOX_HEAD.POOLER_RESOLUTION pooler_scales = tuple(1.0 / input_shape[k].stride for k in in_features) sampling_ratio = cfg.MODEL.ROI_BOX_HEAD.POOLER_SAMPLING_RATIO pooler_type = cfg.MODEL.ROI_BOX_HEAD.POOLER_TYPE # fmt: on # If StandardROIHeads is applied on multiple feature maps (as in FPN), # then we share the same predictors and therefore the channel counts must be the same in_channels = [input_shape[f].channels for f in in_features] # Check all channel counts are equal assert len(set(in_channels)) == 1, in_channels in_channels = in_channels[0] box_pooler = ROIPooler( output_size=pooler_resolution, scales=pooler_scales, sampling_ratio=sampling_ratio, pooler_type=pooler_type, ) # Here we split "box head" and "box predictor", which is mainly due to historical reasons. # They are used together so the "box predictor" layers should be part of the "box head". # New subclasses of ROIHeads do not need "box predictor"s. box_head = build_box_head( cfg, ShapeSpec(channels=in_channels, height=pooler_resolution, width=pooler_resolution) ) box_predictor = FastRCNNOutputLayers(cfg, box_head.output_shape) return { "box_in_features": in_features, "box_pooler": box_pooler, "box_head": box_head, "box_predictor": box_predictor, } @classmethod def _init_mask_head(cls, cfg, input_shape): if not cfg.MODEL.MASK_ON: return {} # fmt: off in_features = cfg.MODEL.ROI_HEADS.IN_FEATURES pooler_resolution = cfg.MODEL.ROI_MASK_HEAD.POOLER_RESOLUTION pooler_scales = tuple(1.0 / input_shape[k].stride for k in in_features) sampling_ratio = cfg.MODEL.ROI_MASK_HEAD.POOLER_SAMPLING_RATIO pooler_type = cfg.MODEL.ROI_MASK_HEAD.POOLER_TYPE # fmt: on in_channels = [input_shape[f].channels for f in in_features][0] ret = {"mask_in_features": in_features} ret["mask_pooler"] = ( ROIPooler( output_size=pooler_resolution, scales=pooler_scales, sampling_ratio=sampling_ratio, pooler_type=pooler_type, ) if pooler_type else None ) if pooler_type: shape = ShapeSpec( channels=in_channels, width=pooler_resolution, height=pooler_resolution ) else: shape = {f: input_shape[f] for f in in_features} ret["mask_head"] = build_mask_head(cfg, shape) return ret @classmethod def _init_keypoint_head(cls, cfg, input_shape): if not cfg.MODEL.KEYPOINT_ON: return {} # fmt: off in_features = cfg.MODEL.ROI_HEADS.IN_FEATURES pooler_resolution = cfg.MODEL.ROI_KEYPOINT_HEAD.POOLER_RESOLUTION pooler_scales = tuple(1.0 / input_shape[k].stride for k in in_features) # noqa sampling_ratio = cfg.MODEL.ROI_KEYPOINT_HEAD.POOLER_SAMPLING_RATIO pooler_type = cfg.MODEL.ROI_KEYPOINT_HEAD.POOLER_TYPE # fmt: on in_channels = [input_shape[f].channels for f in in_features][0] ret = {"keypoint_in_features": in_features} ret["keypoint_pooler"] = ( ROIPooler( output_size=pooler_resolution, scales=pooler_scales, sampling_ratio=sampling_ratio, pooler_type=pooler_type, ) if pooler_type else None ) if pooler_type: shape = ShapeSpec( channels=in_channels, width=pooler_resolution, height=pooler_resolution ) else: shape = {f: input_shape[f] for f in in_features} ret["keypoint_head"] = build_keypoint_head(cfg, shape) return ret def forward( self, images: ImageList, features: Dict[str, torch.Tensor], proposals: List[Instances], targets: Optional[List[Instances]] = None, ) -> Tuple[List[Instances], Dict[str, torch.Tensor]]: """ See :class:`ROIHeads.forward`. """ del images if self.training: assert targets, "'targets' argument is required during training" proposals = self.label_and_sample_proposals(proposals, targets) del targets if self.training: losses = self._forward_box(features, proposals) # Usually the original proposals used by the box head are used by the mask, keypoint # heads. But when `self.train_on_pred_boxes is True`, proposals will contain boxes # predicted by the box head. losses.update(self._forward_mask(features, proposals)) losses.update(self._forward_keypoint(features, proposals)) return proposals, losses else: pred_instances = self._forward_box(features, proposals) # During inference cascaded prediction is used: the mask and keypoints heads are only # applied to the top scoring box detections. pred_instances = self.forward_with_given_boxes(features, pred_instances) return pred_instances, {} def forward_with_given_boxes( self, features: Dict[str, torch.Tensor], instances: List[Instances] ) -> List[Instances]: """ Use the given boxes in `instances` to produce other (non-box) per-ROI outputs. This is useful for downstream tasks where a box is known, but need to obtain other attributes (outputs of other heads). Test-time augmentation also uses this. Args: features: same as in `forward()` instances (list[Instances]): instances to predict other outputs. Expect the keys "pred_boxes" and "pred_classes" to exist. Returns: list[Instances]: the same `Instances` objects, with extra fields such as `pred_masks` or `pred_keypoints`. """ assert not self.training assert instances[0].has("pred_boxes") and instances[0].has("pred_classes") instances = self._forward_mask(features, instances) instances = self._forward_keypoint(features, instances) return instances def _forward_box(self, features: Dict[str, torch.Tensor], proposals: List[Instances]): """ Forward logic of the box prediction branch. If `self.train_on_pred_boxes is True`, the function puts predicted boxes in the `proposal_boxes` field of `proposals` argument. Args: features (dict[str, Tensor]): mapping from feature map names to tensor. Same as in :meth:`ROIHeads.forward`. proposals (list[Instances]): the per-image object proposals with their matching ground truth. Each has fields "proposal_boxes", and "objectness_logits", "gt_classes", "gt_boxes". Returns: In training, a dict of losses. In inference, a list of `Instances`, the predicted instances. """ features = [features[f] for f in self.box_in_features] box_features = self.box_pooler(features, [x.proposal_boxes for x in proposals]) box_features = self.box_head(box_features) predictions = self.box_predictor(box_features) del box_features if self.training: losses = self.box_predictor.losses(predictions, proposals) # proposals is modified in-place below, so losses must be computed first. if self.train_on_pred_boxes: with torch.no_grad(): pred_boxes = self.box_predictor.predict_boxes_for_gt_classes( predictions, proposals ) for proposals_per_image, pred_boxes_per_image in zip(proposals, pred_boxes): proposals_per_image.proposal_boxes = Boxes(pred_boxes_per_image) return losses else: pred_instances, _ = self.box_predictor.inference(predictions, proposals) return pred_instances def _forward_mask(self, features: Dict[str, torch.Tensor], instances: List[Instances]): """ Forward logic of the mask prediction branch. Args: features (dict[str, Tensor]): mapping from feature map names to tensor. Same as in :meth:`ROIHeads.forward`. instances (list[Instances]): the per-image instances to train/predict masks. In training, they can be the proposals. In inference, they can be the boxes predicted by R-CNN box head. Returns: In training, a dict of losses. In inference, update `instances` with new fields "pred_masks" and return it. """ if not self.mask_on: return {} if self.training else instances if self.training: # head is only trained on positive proposals. instances, _ = select_foreground_proposals(instances, self.num_classes) if self.mask_pooler is not None: features = [features[f] for f in self.mask_in_features] boxes = [x.proposal_boxes if self.training else x.pred_boxes for x in instances] features = self.mask_pooler(features, boxes) else: features = {f: features[f] for f in self.mask_in_features} return self.mask_head(features, instances) def _forward_keypoint(self, features: Dict[str, torch.Tensor], instances: List[Instances]): """ Forward logic of the keypoint prediction branch. Args: features (dict[str, Tensor]): mapping from feature map names to tensor. Same as in :meth:`ROIHeads.forward`. instances (list[Instances]): the per-image instances to train/predict keypoints. In training, they can be the proposals. In inference, they can be the boxes predicted by R-CNN box head. Returns: In training, a dict of losses. In inference, update `instances` with new fields "pred_keypoints" and return it. """ if not self.keypoint_on: return {} if self.training else instances if self.training: # head is only trained on positive proposals with >=1 visible keypoints. instances, _ = select_foreground_proposals(instances, self.num_classes) instances = select_proposals_with_visible_keypoints(instances) if self.keypoint_pooler is not None: features = [features[f] for f in self.keypoint_in_features] boxes = [x.proposal_boxes if self.training else x.pred_boxes for x in instances] features = self.keypoint_pooler(features, boxes) else: features = {f: features[f] for f in self.keypoint_in_features} return self.keypoint_head(features, instances) ================================================ FILE: detectron2/detectron2/modeling/roi_heads/rotated_fast_rcnn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np import torch from detectron2.config import configurable from detectron2.layers import ShapeSpec, batched_nms_rotated from detectron2.structures import Instances, RotatedBoxes, pairwise_iou_rotated from detectron2.utils.events import get_event_storage from ..box_regression import Box2BoxTransformRotated from ..poolers import ROIPooler from ..proposal_generator.proposal_utils import add_ground_truth_to_proposals from .box_head import build_box_head from .fast_rcnn import FastRCNNOutputLayers from .roi_heads import ROI_HEADS_REGISTRY, StandardROIHeads logger = logging.getLogger(__name__) """ Shape shorthand in this module: N: number of images in the minibatch R: number of ROIs, combined over all images, in the minibatch Ri: number of ROIs in image i K: number of foreground classes. E.g.,there are 80 foreground classes in COCO. Naming convention: deltas: refers to the 5-d (dx, dy, dw, dh, da) deltas that parameterize the box2box transform (see :class:`box_regression.Box2BoxTransformRotated`). pred_class_logits: predicted class scores in [-inf, +inf]; use softmax(pred_class_logits) to estimate P(class). gt_classes: ground-truth classification labels in [0, K], where [0, K) represent foreground object classes and K represents the background class. pred_proposal_deltas: predicted rotated box2box transform deltas for transforming proposals to detection box predictions. gt_proposal_deltas: ground-truth rotated box2box transform deltas """ def fast_rcnn_inference_rotated( boxes, scores, image_shapes, score_thresh, nms_thresh, topk_per_image ): """ Call `fast_rcnn_inference_single_image_rotated` for all images. Args: boxes (list[Tensor]): A list of Tensors of predicted class-specific or class-agnostic boxes for each image. Element i has shape (Ri, K * 5) if doing class-specific regression, or (Ri, 5) if doing class-agnostic regression, where Ri is the number of predicted objects for image i. This is compatible with the output of :meth:`FastRCNNOutputLayers.predict_boxes`. scores (list[Tensor]): A list of Tensors of predicted class scores for each image. Element i has shape (Ri, K + 1), where Ri is the number of predicted objects for image i. Compatible with the output of :meth:`FastRCNNOutputLayers.predict_probs`. image_shapes (list[tuple]): A list of (width, height) tuples for each image in the batch. score_thresh (float): Only return detections with a confidence score exceeding this threshold. nms_thresh (float): The threshold to use for box non-maximum suppression. Value in [0, 1]. topk_per_image (int): The number of top scoring detections to return. Set < 0 to return all detections. Returns: instances: (list[Instances]): A list of N instances, one for each image in the batch, that stores the topk most confidence detections. kept_indices: (list[Tensor]): A list of 1D tensor of length of N, each element indicates the corresponding boxes/scores index in [0, Ri) from the input, for image i. """ result_per_image = [ fast_rcnn_inference_single_image_rotated( boxes_per_image, scores_per_image, image_shape, score_thresh, nms_thresh, topk_per_image ) for scores_per_image, boxes_per_image, image_shape in zip(scores, boxes, image_shapes) ] return [x[0] for x in result_per_image], [x[1] for x in result_per_image] @torch.no_grad() def fast_rcnn_inference_single_image_rotated( boxes, scores, image_shape, score_thresh, nms_thresh, topk_per_image ): """ Single-image inference. Return rotated bounding-box detection results by thresholding on scores and applying rotated non-maximum suppression (Rotated NMS). Args: Same as `fast_rcnn_inference_rotated`, but with rotated boxes, scores, and image shapes per image. Returns: Same as `fast_rcnn_inference_rotated`, but for only one image. """ valid_mask = torch.isfinite(boxes).all(dim=1) & torch.isfinite(scores).all(dim=1) if not valid_mask.all(): boxes = boxes[valid_mask] scores = scores[valid_mask] B = 5 # box dimension scores = scores[:, :-1] num_bbox_reg_classes = boxes.shape[1] // B # Convert to Boxes to use the `clip` function ... boxes = RotatedBoxes(boxes.reshape(-1, B)) boxes.clip(image_shape) boxes = boxes.tensor.view(-1, num_bbox_reg_classes, B) # R x C x B # Filter results based on detection scores filter_mask = scores > score_thresh # R x K # R' x 2. First column contains indices of the R predictions; # Second column contains indices of classes. filter_inds = filter_mask.nonzero() if num_bbox_reg_classes == 1: boxes = boxes[filter_inds[:, 0], 0] else: boxes = boxes[filter_mask] scores = scores[filter_mask] # Apply per-class Rotated NMS keep = batched_nms_rotated(boxes, scores, filter_inds[:, 1], nms_thresh) if topk_per_image >= 0: keep = keep[:topk_per_image] boxes, scores, filter_inds = boxes[keep], scores[keep], filter_inds[keep] result = Instances(image_shape) result.pred_boxes = RotatedBoxes(boxes) result.scores = scores result.pred_classes = filter_inds[:, 1] return result, filter_inds[:, 0] class RotatedFastRCNNOutputLayers(FastRCNNOutputLayers): """ Two linear layers for predicting Rotated Fast R-CNN outputs. """ @classmethod def from_config(cls, cfg, input_shape): args = super().from_config(cfg, input_shape) args["box2box_transform"] = Box2BoxTransformRotated( weights=cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS ) return args def inference(self, predictions, proposals): """ Returns: list[Instances]: same as `fast_rcnn_inference_rotated`. list[Tensor]: same as `fast_rcnn_inference_rotated`. """ boxes = self.predict_boxes(predictions, proposals) scores = self.predict_probs(predictions, proposals) image_shapes = [x.image_size for x in proposals] return fast_rcnn_inference_rotated( boxes, scores, image_shapes, self.test_score_thresh, self.test_nms_thresh, self.test_topk_per_image, ) @ROI_HEADS_REGISTRY.register() class RROIHeads(StandardROIHeads): """ This class is used by Rotated Fast R-CNN to detect rotated boxes. For now, it only supports box predictions but not mask or keypoints. """ @configurable def __init__(self, **kwargs): """ NOTE: this interface is experimental. """ super().__init__(**kwargs) assert ( not self.mask_on and not self.keypoint_on ), "Mask/Keypoints not supported in Rotated ROIHeads." assert not self.train_on_pred_boxes, "train_on_pred_boxes not implemented for RROIHeads!" @classmethod def _init_box_head(cls, cfg, input_shape): # fmt: off in_features = cfg.MODEL.ROI_HEADS.IN_FEATURES pooler_resolution = cfg.MODEL.ROI_BOX_HEAD.POOLER_RESOLUTION pooler_scales = tuple(1.0 / input_shape[k].stride for k in in_features) sampling_ratio = cfg.MODEL.ROI_BOX_HEAD.POOLER_SAMPLING_RATIO pooler_type = cfg.MODEL.ROI_BOX_HEAD.POOLER_TYPE # fmt: on assert pooler_type in ["ROIAlignRotated"], pooler_type # assume all channel counts are equal in_channels = [input_shape[f].channels for f in in_features][0] box_pooler = ROIPooler( output_size=pooler_resolution, scales=pooler_scales, sampling_ratio=sampling_ratio, pooler_type=pooler_type, ) box_head = build_box_head( cfg, ShapeSpec(channels=in_channels, height=pooler_resolution, width=pooler_resolution) ) # This line is the only difference v.s. StandardROIHeads box_predictor = RotatedFastRCNNOutputLayers(cfg, box_head.output_shape) return { "box_in_features": in_features, "box_pooler": box_pooler, "box_head": box_head, "box_predictor": box_predictor, } @torch.no_grad() def label_and_sample_proposals(self, proposals, targets): """ Prepare some proposals to be used to train the RROI heads. It performs box matching between `proposals` and `targets`, and assigns training labels to the proposals. It returns `self.batch_size_per_image` random samples from proposals and groundtruth boxes, with a fraction of positives that is no larger than `self.positive_sample_fraction. Args: See :meth:`StandardROIHeads.forward` Returns: list[Instances]: length `N` list of `Instances`s containing the proposals sampled for training. Each `Instances` has the following fields: - proposal_boxes: the rotated proposal boxes - gt_boxes: the ground-truth rotated boxes that the proposal is assigned to (this is only meaningful if the proposal has a label > 0; if label = 0 then the ground-truth box is random) - gt_classes: the ground-truth classification lable for each proposal """ if self.proposal_append_gt: proposals = add_ground_truth_to_proposals(targets, proposals) proposals_with_gt = [] num_fg_samples = [] num_bg_samples = [] for proposals_per_image, targets_per_image in zip(proposals, targets): has_gt = len(targets_per_image) > 0 match_quality_matrix = pairwise_iou_rotated( targets_per_image.gt_boxes, proposals_per_image.proposal_boxes ) matched_idxs, matched_labels = self.proposal_matcher(match_quality_matrix) sampled_idxs, gt_classes = self._sample_proposals( matched_idxs, matched_labels, targets_per_image.gt_classes ) proposals_per_image = proposals_per_image[sampled_idxs] proposals_per_image.gt_classes = gt_classes if has_gt: sampled_targets = matched_idxs[sampled_idxs] proposals_per_image.gt_boxes = targets_per_image.gt_boxes[sampled_targets] num_bg_samples.append((gt_classes == self.num_classes).sum().item()) num_fg_samples.append(gt_classes.numel() - num_bg_samples[-1]) proposals_with_gt.append(proposals_per_image) # Log the number of fg/bg samples that are selected for training ROI heads storage = get_event_storage() storage.put_scalar("roi_head/num_fg_samples", np.mean(num_fg_samples)) storage.put_scalar("roi_head/num_bg_samples", np.mean(num_bg_samples)) return proposals_with_gt ================================================ FILE: detectron2/detectron2/modeling/sampling.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch from detectron2.layers import nonzero_tuple __all__ = ["subsample_labels"] def subsample_labels( labels: torch.Tensor, num_samples: int, positive_fraction: float, bg_label: int ): """ Return `num_samples` (or fewer, if not enough found) random samples from `labels` which is a mixture of positives & negatives. It will try to return as many positives as possible without exceeding `positive_fraction * num_samples`, and then try to fill the remaining slots with negatives. Args: labels (Tensor): (N, ) label vector with values: * -1: ignore * bg_label: background ("negative") class * otherwise: one or more foreground ("positive") classes num_samples (int): The total number of labels with value >= 0 to return. Values that are not sampled will be filled with -1 (ignore). positive_fraction (float): The number of subsampled labels with values > 0 is `min(num_positives, int(positive_fraction * num_samples))`. The number of negatives sampled is `min(num_negatives, num_samples - num_positives_sampled)`. In order words, if there are not enough positives, the sample is filled with negatives. If there are also not enough negatives, then as many elements are sampled as is possible. bg_label (int): label index of background ("negative") class. Returns: pos_idx, neg_idx (Tensor): 1D vector of indices. The total length of both is `num_samples` or fewer. """ positive = nonzero_tuple((labels != -1) & (labels != bg_label))[0] negative = nonzero_tuple(labels == bg_label)[0] num_pos = int(num_samples * positive_fraction) # protect against not enough positive examples num_pos = min(positive.numel(), num_pos) num_neg = num_samples - num_pos # protect against not enough negative examples num_neg = min(negative.numel(), num_neg) # randomly select positive and negative examples perm1 = torch.randperm(positive.numel(), device=positive.device)[:num_pos] perm2 = torch.randperm(negative.numel(), device=negative.device)[:num_neg] pos_idx = positive[perm1] neg_idx = negative[perm2] return pos_idx, neg_idx ================================================ FILE: detectron2/detectron2/modeling/test_time_augmentation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import numpy as np from contextlib import contextmanager from itertools import count from typing import List import torch from fvcore.transforms import HFlipTransform, NoOpTransform from torch import nn from torch.nn.parallel import DistributedDataParallel from detectron2.config import configurable from detectron2.data.detection_utils import read_image from detectron2.data.transforms import ( RandomFlip, ResizeShortestEdge, ResizeTransform, apply_augmentations, ) from detectron2.structures import Boxes, Instances from .meta_arch import GeneralizedRCNN from .postprocessing import detector_postprocess from .roi_heads.fast_rcnn import fast_rcnn_inference_single_image __all__ = ["DatasetMapperTTA", "GeneralizedRCNNWithTTA"] class DatasetMapperTTA: """ Implement test-time augmentation for detection data. It is a callable which takes a dataset dict from a detection dataset, and returns a list of dataset dicts where the images are augmented from the input image by the transformations defined in the config. This is used for test-time augmentation. """ @configurable def __init__(self, min_sizes: List[int], max_size: int, flip: bool): """ Args: min_sizes: list of short-edge size to resize the image to max_size: maximum height or width of resized images flip: whether to apply flipping augmentation """ self.min_sizes = min_sizes self.max_size = max_size self.flip = flip @classmethod def from_config(cls, cfg): return { "min_sizes": cfg.TEST.AUG.MIN_SIZES, "max_size": cfg.TEST.AUG.MAX_SIZE, "flip": cfg.TEST.AUG.FLIP, } def __call__(self, dataset_dict): """ Args: dict: a dict in standard model input format. See tutorials for details. Returns: list[dict]: a list of dicts, which contain augmented version of the input image. The total number of dicts is ``len(min_sizes) * (2 if flip else 1)``. Each dict has field "transforms" which is a TransformList, containing the transforms that are used to generate this image. """ numpy_image = dataset_dict["image"].permute(1, 2, 0).numpy() shape = numpy_image.shape orig_shape = (dataset_dict["height"], dataset_dict["width"]) if shape[:2] != orig_shape: # It transforms the "original" image in the dataset to the input image pre_tfm = ResizeTransform(orig_shape[0], orig_shape[1], shape[0], shape[1]) else: pre_tfm = NoOpTransform() # Create all combinations of augmentations to use aug_candidates = [] # each element is a list[Augmentation] for min_size in self.min_sizes: resize = ResizeShortestEdge(min_size, self.max_size) aug_candidates.append([resize]) # resize only if self.flip: flip = RandomFlip(prob=1.0) aug_candidates.append([resize, flip]) # resize + flip # Apply all the augmentations ret = [] for aug in aug_candidates: new_image, tfms = apply_augmentations(aug, np.copy(numpy_image)) torch_image = torch.from_numpy(np.ascontiguousarray(new_image.transpose(2, 0, 1))) dic = copy.deepcopy(dataset_dict) dic["transforms"] = pre_tfm + tfms dic["image"] = torch_image ret.append(dic) return ret class GeneralizedRCNNWithTTA(nn.Module): """ A GeneralizedRCNN with test-time augmentation enabled. Its :meth:`__call__` method has the same interface as :meth:`GeneralizedRCNN.forward`. """ def __init__(self, cfg, model, tta_mapper=None, batch_size=3): """ Args: cfg (CfgNode): model (GeneralizedRCNN): a GeneralizedRCNN to apply TTA on. tta_mapper (callable): takes a dataset dict and returns a list of augmented versions of the dataset dict. Defaults to `DatasetMapperTTA(cfg)`. batch_size (int): batch the augmented images into this batch size for inference. """ super().__init__() if isinstance(model, DistributedDataParallel): model = model.module assert isinstance( model, GeneralizedRCNN ), "TTA is only supported on GeneralizedRCNN. Got a model of type {}".format(type(model)) self.cfg = cfg.clone() assert not self.cfg.MODEL.KEYPOINT_ON, "TTA for keypoint is not supported yet" assert ( not self.cfg.MODEL.LOAD_PROPOSALS ), "TTA for pre-computed proposals is not supported yet" self.model = model if tta_mapper is None: tta_mapper = DatasetMapperTTA(cfg) self.tta_mapper = tta_mapper self.batch_size = batch_size @contextmanager def _turn_off_roi_heads(self, attrs): """ Open a context where some heads in `model.roi_heads` are temporarily turned off. Args: attr (list[str]): the attribute in `model.roi_heads` which can be used to turn off a specific head, e.g., "mask_on", "keypoint_on". """ roi_heads = self.model.roi_heads old = {} for attr in attrs: try: old[attr] = getattr(roi_heads, attr) except AttributeError: # The head may not be implemented in certain ROIHeads pass if len(old.keys()) == 0: yield else: for attr in old.keys(): setattr(roi_heads, attr, False) yield for attr in old.keys(): setattr(roi_heads, attr, old[attr]) def _batch_inference(self, batched_inputs, detected_instances=None): """ Execute inference on a list of inputs, using batch size = self.batch_size, instead of the length of the list. Inputs & outputs have the same format as :meth:`GeneralizedRCNN.inference` """ if detected_instances is None: detected_instances = [None] * len(batched_inputs) outputs = [] inputs, instances = [], [] for idx, input, instance in zip(count(), batched_inputs, detected_instances): inputs.append(input) instances.append(instance) if len(inputs) == self.batch_size or idx == len(batched_inputs) - 1: outputs.extend( self.model.inference( inputs, instances if instances[0] is not None else None, do_postprocess=False, ) ) inputs, instances = [], [] return outputs def __call__(self, batched_inputs): """ Same input/output format as :meth:`GeneralizedRCNN.forward` """ def _maybe_read_image(dataset_dict): ret = copy.copy(dataset_dict) if "image" not in ret: image = read_image(ret.pop("file_name"), self.model.input_format) image = torch.from_numpy(np.ascontiguousarray(image.transpose(2, 0, 1))) # CHW ret["image"] = image if "height" not in ret and "width" not in ret: ret["height"] = image.shape[1] ret["width"] = image.shape[2] return ret return [self._inference_one_image(_maybe_read_image(x)) for x in batched_inputs] def _inference_one_image(self, input): """ Args: input (dict): one dataset dict with "image" field being a CHW tensor Returns: dict: one output dict """ orig_shape = (input["height"], input["width"]) augmented_inputs, tfms = self._get_augmented_inputs(input) # Detect boxes from all augmented versions with self._turn_off_roi_heads(["mask_on", "keypoint_on"]): # temporarily disable roi heads all_boxes, all_scores, all_classes = self._get_augmented_boxes(augmented_inputs, tfms) # merge all detected boxes to obtain final predictions for boxes merged_instances = self._merge_detections(all_boxes, all_scores, all_classes, orig_shape) if self.cfg.MODEL.MASK_ON: # Use the detected boxes to obtain masks augmented_instances = self._rescale_detected_boxes( augmented_inputs, merged_instances, tfms ) # run forward on the detected boxes outputs = self._batch_inference(augmented_inputs, augmented_instances) # Delete now useless variables to avoid being out of memory del augmented_inputs, augmented_instances # average the predictions merged_instances.pred_masks = self._reduce_pred_masks(outputs, tfms) merged_instances = detector_postprocess(merged_instances, *orig_shape) return {"instances": merged_instances} else: return {"instances": merged_instances} def _get_augmented_inputs(self, input): augmented_inputs = self.tta_mapper(input) tfms = [x.pop("transforms") for x in augmented_inputs] return augmented_inputs, tfms def _get_augmented_boxes(self, augmented_inputs, tfms): # 1: forward with all augmented images outputs = self._batch_inference(augmented_inputs) # 2: union the results all_boxes = [] all_scores = [] all_classes = [] for output, tfm in zip(outputs, tfms): # Need to inverse the transforms on boxes, to obtain results on original image pred_boxes = output.pred_boxes.tensor original_pred_boxes = tfm.inverse().apply_box(pred_boxes.cpu().numpy()) all_boxes.append(torch.from_numpy(original_pred_boxes).to(pred_boxes.device)) all_scores.extend(output.scores) all_classes.extend(output.pred_classes) all_boxes = torch.cat(all_boxes, dim=0) return all_boxes, all_scores, all_classes def _merge_detections(self, all_boxes, all_scores, all_classes, shape_hw): # select from the union of all results num_boxes = len(all_boxes) num_classes = self.cfg.MODEL.ROI_HEADS.NUM_CLASSES # +1 because fast_rcnn_inference expects background scores as well all_scores_2d = torch.zeros(num_boxes, num_classes + 1, device=all_boxes.device) for idx, cls, score in zip(count(), all_classes, all_scores): all_scores_2d[idx, cls] = score merged_instances, _ = fast_rcnn_inference_single_image( all_boxes, all_scores_2d, shape_hw, 1e-8, self.cfg.MODEL.ROI_HEADS.NMS_THRESH_TEST, self.cfg.TEST.DETECTIONS_PER_IMAGE, ) return merged_instances def _rescale_detected_boxes(self, augmented_inputs, merged_instances, tfms): augmented_instances = [] for input, tfm in zip(augmented_inputs, tfms): # Transform the target box to the augmented image's coordinate space pred_boxes = merged_instances.pred_boxes.tensor.cpu().numpy() pred_boxes = torch.from_numpy(tfm.apply_box(pred_boxes)) aug_instances = Instances( image_size=input["image"].shape[1:3], pred_boxes=Boxes(pred_boxes), pred_classes=merged_instances.pred_classes, scores=merged_instances.scores, ) augmented_instances.append(aug_instances) return augmented_instances def _reduce_pred_masks(self, outputs, tfms): # Should apply inverse transforms on masks. # We assume only resize & flip are used. pred_masks is a scale-invariant # representation, so we handle flip specially for output, tfm in zip(outputs, tfms): if any(isinstance(t, HFlipTransform) for t in tfm.transforms): output.pred_masks = output.pred_masks.flip(dims=[3]) all_pred_masks = torch.stack([o.pred_masks for o in outputs], dim=0) avg_pred_masks = torch.mean(all_pred_masks, dim=0) return avg_pred_masks ================================================ FILE: detectron2/detectron2/projects/README.md ================================================ Projects live in the [`projects` directory](../../projects) under the root of this repository, but not here. ================================================ FILE: detectron2/detectron2/projects/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import importlib from pathlib import Path _PROJECTS = { "point_rend": "PointRend", "deeplab": "DeepLab", "panoptic_deeplab": "Panoptic-DeepLab", } _PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent / "projects" if _PROJECT_ROOT.is_dir(): # This is true only for in-place installation (pip install -e, setup.py develop), # where setup(package_dir=) does not work: https://github.com/pypa/setuptools/issues/230 class _D2ProjectsFinder(importlib.abc.MetaPathFinder): def find_spec(self, name, path, target=None): if not name.startswith("detectron2.projects."): return project_name = name.split(".")[-1] project_dir = _PROJECTS.get(project_name) if not project_dir: return target_file = _PROJECT_ROOT / f"{project_dir}/{project_name}/__init__.py" if not target_file.is_file(): return return importlib.util.spec_from_file_location(name, target_file) import sys sys.meta_path.append(_D2ProjectsFinder()) ================================================ FILE: detectron2/detectron2/solver/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .build import build_lr_scheduler, build_optimizer, get_default_optimizer_params from .lr_scheduler import WarmupCosineLR, WarmupMultiStepLR, LRMultiplier, WarmupParamScheduler __all__ = [k for k in globals().keys() if not k.startswith("_")] ================================================ FILE: detectron2/detectron2/solver/build.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import itertools import logging from collections import defaultdict from enum import Enum from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Type, Union import torch from fvcore.common.param_scheduler import CosineParamScheduler, MultiStepParamScheduler from detectron2.config import CfgNode from .lr_scheduler import LRMultiplier, WarmupParamScheduler _GradientClipperInput = Union[torch.Tensor, Iterable[torch.Tensor]] _GradientClipper = Callable[[_GradientClipperInput], None] class GradientClipType(Enum): VALUE = "value" NORM = "norm" def _create_gradient_clipper(cfg: CfgNode) -> _GradientClipper: """ Creates gradient clipping closure to clip by value or by norm, according to the provided config. """ cfg = copy.deepcopy(cfg) def clip_grad_norm(p: _GradientClipperInput): torch.nn.utils.clip_grad_norm_(p, cfg.CLIP_VALUE, cfg.NORM_TYPE) def clip_grad_value(p: _GradientClipperInput): torch.nn.utils.clip_grad_value_(p, cfg.CLIP_VALUE) _GRADIENT_CLIP_TYPE_TO_CLIPPER = { GradientClipType.VALUE: clip_grad_value, GradientClipType.NORM: clip_grad_norm, } return _GRADIENT_CLIP_TYPE_TO_CLIPPER[GradientClipType(cfg.CLIP_TYPE)] def _generate_optimizer_class_with_gradient_clipping( optimizer: Type[torch.optim.Optimizer], *, per_param_clipper: Optional[_GradientClipper] = None, global_clipper: Optional[_GradientClipper] = None, ) -> Type[torch.optim.Optimizer]: """ Dynamically creates a new type that inherits the type of a given instance and overrides the `step` method to add gradient clipping """ assert ( per_param_clipper is None or global_clipper is None ), "Not allowed to use both per-parameter clipping and global clipping" def optimizer_wgc_step(self, closure=None): if per_param_clipper is not None: for group in self.param_groups: for p in group["params"]: per_param_clipper(p) else: # global clipper for future use with detr # (https://github.com/facebookresearch/detr/pull/287) all_params = itertools.chain(*[g["params"] for g in self.param_groups]) global_clipper(all_params) super(type(self), self).step(closure) OptimizerWithGradientClip = type( optimizer.__name__ + "WithGradientClip", (optimizer,), {"step": optimizer_wgc_step}, ) return OptimizerWithGradientClip def maybe_add_gradient_clipping( cfg: CfgNode, optimizer: Type[torch.optim.Optimizer] ) -> Type[torch.optim.Optimizer]: """ If gradient clipping is enabled through config options, wraps the existing optimizer type to become a new dynamically created class OptimizerWithGradientClip that inherits the given optimizer and overrides the `step` method to include gradient clipping. Args: cfg: CfgNode, configuration options optimizer: type. A subclass of torch.optim.Optimizer Return: type: either the input `optimizer` (if gradient clipping is disabled), or a subclass of it with gradient clipping included in the `step` method. """ if not cfg.SOLVER.CLIP_GRADIENTS.ENABLED: return optimizer if isinstance(optimizer, torch.optim.Optimizer): optimizer_type = type(optimizer) else: assert issubclass(optimizer, torch.optim.Optimizer), optimizer optimizer_type = optimizer grad_clipper = _create_gradient_clipper(cfg.SOLVER.CLIP_GRADIENTS) OptimizerWithGradientClip = _generate_optimizer_class_with_gradient_clipping( optimizer_type, per_param_clipper=grad_clipper ) if isinstance(optimizer, torch.optim.Optimizer): optimizer.__class__ = OptimizerWithGradientClip # a bit hacky, not recommended return optimizer else: return OptimizerWithGradientClip def build_optimizer(cfg: CfgNode, model: torch.nn.Module) -> torch.optim.Optimizer: """ Build an optimizer from config. """ params = get_default_optimizer_params( model, base_lr=cfg.SOLVER.BASE_LR, weight_decay_norm=cfg.SOLVER.WEIGHT_DECAY_NORM, bias_lr_factor=cfg.SOLVER.BIAS_LR_FACTOR, weight_decay_bias=cfg.SOLVER.WEIGHT_DECAY_BIAS, ) return maybe_add_gradient_clipping(cfg, torch.optim.SGD)( params, lr=cfg.SOLVER.BASE_LR, momentum=cfg.SOLVER.MOMENTUM, nesterov=cfg.SOLVER.NESTEROV, weight_decay=cfg.SOLVER.WEIGHT_DECAY, ) def get_default_optimizer_params( model: torch.nn.Module, base_lr: Optional[float] = None, weight_decay: Optional[float] = None, weight_decay_norm: Optional[float] = None, bias_lr_factor: Optional[float] = 1.0, weight_decay_bias: Optional[float] = None, lr_factor_func: Optional[Callable] = None, overrides: Optional[Dict[str, Dict[str, float]]] = None, ) -> List[Dict[str, Any]]: """ Get default param list for optimizer, with support for a few types of overrides. If no overrides needed, this is equivalent to `model.parameters()`. Args: base_lr: lr for every group by default. Can be omitted to use the one in optimizer. weight_decay: weight decay for every group by default. Can be omitted to use the one in optimizer. weight_decay_norm: override weight decay for params in normalization layers bias_lr_factor: multiplier of lr for bias parameters. weight_decay_bias: override weight decay for bias parameters. lr_factor_func: function to calculate lr decay rate by mapping the parameter names to corresponding lr decay rate. Note that setting this option requires also setting ``base_lr``. overrides: if not `None`, provides values for optimizer hyperparameters (LR, weight decay) for module parameters with a given name; e.g. ``{"embedding": {"lr": 0.01, "weight_decay": 0.1}}`` will set the LR and weight decay values for all module parameters named `embedding`. For common detection models, ``weight_decay_norm`` is the only option needed to be set. ``bias_lr_factor,weight_decay_bias`` are legacy settings from Detectron1 that are not found useful. Example: :: torch.optim.SGD(get_default_optimizer_params(model, weight_decay_norm=0), lr=0.01, weight_decay=1e-4, momentum=0.9) """ if overrides is None: overrides = {} defaults = {} if base_lr is not None: defaults["lr"] = base_lr if weight_decay is not None: defaults["weight_decay"] = weight_decay bias_overrides = {} if bias_lr_factor is not None and bias_lr_factor != 1.0: # NOTE: unlike Detectron v1, we now by default make bias hyperparameters # exactly the same as regular weights. if base_lr is None: raise ValueError("bias_lr_factor requires base_lr") bias_overrides["lr"] = base_lr * bias_lr_factor if weight_decay_bias is not None: bias_overrides["weight_decay"] = weight_decay_bias if len(bias_overrides): if "bias" in overrides: raise ValueError("Conflicting overrides for 'bias'") overrides["bias"] = bias_overrides if lr_factor_func is not None: if base_lr is None: raise ValueError("lr_factor_func requires base_lr") norm_module_types = ( torch.nn.BatchNorm1d, torch.nn.BatchNorm2d, torch.nn.BatchNorm3d, torch.nn.SyncBatchNorm, # NaiveSyncBatchNorm inherits from BatchNorm2d torch.nn.GroupNorm, torch.nn.InstanceNorm1d, torch.nn.InstanceNorm2d, torch.nn.InstanceNorm3d, torch.nn.LayerNorm, torch.nn.LocalResponseNorm, ) params: List[Dict[str, Any]] = [] memo: Set[torch.nn.parameter.Parameter] = set() for module_name, module in model.named_modules(): for module_param_name, value in module.named_parameters(recurse=False): if not value.requires_grad: continue # Avoid duplicating parameters if value in memo: continue memo.add(value) hyperparams = copy.copy(defaults) if isinstance(module, norm_module_types) and weight_decay_norm is not None: hyperparams["weight_decay"] = weight_decay_norm if lr_factor_func is not None: hyperparams["lr"] *= lr_factor_func(f"{module_name}.{module_param_name}") hyperparams.update(overrides.get(module_param_name, {})) params.append({"params": [value], **hyperparams}) return reduce_param_groups(params) def _expand_param_groups(params: List[Dict[str, Any]]) -> List[Dict[str, Any]]: # Transform parameter groups into per-parameter structure. # Later items in `params` can overwrite parameters set in previous items. ret = defaultdict(dict) for item in params: assert "params" in item cur_params = {x: y for x, y in item.items() if x != "params"} for param in item["params"]: ret[param].update({"params": [param], **cur_params}) return list(ret.values()) def reduce_param_groups(params: List[Dict[str, Any]]) -> List[Dict[str, Any]]: # Reorganize the parameter groups and merge duplicated groups. # The number of parameter groups needs to be as small as possible in order # to efficiently use the PyTorch multi-tensor optimizer. Therefore instead # of using a parameter_group per single parameter, we reorganize the # parameter groups and merge duplicated groups. This approach speeds # up multi-tensor optimizer significantly. params = _expand_param_groups(params) groups = defaultdict(list) # re-group all parameter groups by their hyperparams for item in params: cur_params = tuple((x, y) for x, y in item.items() if x != "params") groups[cur_params].extend(item["params"]) ret = [] for param_keys, param_values in groups.items(): cur = {kv[0]: kv[1] for kv in param_keys} cur["params"] = param_values ret.append(cur) return ret def build_lr_scheduler( cfg: CfgNode, optimizer: torch.optim.Optimizer ) -> torch.optim.lr_scheduler._LRScheduler: """ Build a LR scheduler from config. """ name = cfg.SOLVER.LR_SCHEDULER_NAME if name == "WarmupMultiStepLR": steps = [x for x in cfg.SOLVER.STEPS if x <= cfg.SOLVER.MAX_ITER] if len(steps) != len(cfg.SOLVER.STEPS): logger = logging.getLogger(__name__) logger.warning( "SOLVER.STEPS contains values larger than SOLVER.MAX_ITER. " "These values will be ignored." ) sched = MultiStepParamScheduler( values=[cfg.SOLVER.GAMMA**k for k in range(len(steps) + 1)], milestones=steps, num_updates=cfg.SOLVER.MAX_ITER, ) elif name == "WarmupCosineLR": end_value = cfg.SOLVER.BASE_LR_END / cfg.SOLVER.BASE_LR assert end_value >= 0.0 and end_value <= 1.0, end_value sched = CosineParamScheduler(1, end_value) else: raise ValueError("Unknown LR scheduler: {}".format(name)) sched = WarmupParamScheduler( sched, cfg.SOLVER.WARMUP_FACTOR, min(cfg.SOLVER.WARMUP_ITERS / cfg.SOLVER.MAX_ITER, 1.0), cfg.SOLVER.WARMUP_METHOD, ) return LRMultiplier(optimizer, multiplier=sched, max_iter=cfg.SOLVER.MAX_ITER) ================================================ FILE: detectron2/detectron2/solver/lr_scheduler.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import math from bisect import bisect_right from typing import List import torch from fvcore.common.param_scheduler import ( CompositeParamScheduler, ConstantParamScheduler, LinearParamScheduler, ParamScheduler, ) logger = logging.getLogger(__name__) class WarmupParamScheduler(CompositeParamScheduler): """ Add an initial warmup stage to another scheduler. """ def __init__( self, scheduler: ParamScheduler, warmup_factor: float, warmup_length: float, warmup_method: str = "linear", ): """ Args: scheduler: warmup will be added at the beginning of this scheduler warmup_factor: the factor w.r.t the initial value of ``scheduler``, e.g. 0.001 warmup_length: the relative length (in [0, 1]) of warmup steps w.r.t the entire training, e.g. 0.01 warmup_method: one of "linear" or "constant" """ end_value = scheduler(warmup_length) # the value to reach when warmup ends start_value = warmup_factor * scheduler(0.0) if warmup_method == "constant": warmup = ConstantParamScheduler(start_value) elif warmup_method == "linear": warmup = LinearParamScheduler(start_value, end_value) else: raise ValueError("Unknown warmup method: {}".format(warmup_method)) super().__init__( [warmup, scheduler], interval_scaling=["rescaled", "fixed"], lengths=[warmup_length, 1 - warmup_length], ) class LRMultiplier(torch.optim.lr_scheduler._LRScheduler): """ A LRScheduler which uses fvcore :class:`ParamScheduler` to multiply the learning rate of each param in the optimizer. Every step, the learning rate of each parameter becomes its initial value multiplied by the output of the given :class:`ParamScheduler`. The absolute learning rate value of each parameter can be different. This scheduler can be used as long as the relative scale among them do not change during training. Examples: :: LRMultiplier( opt, WarmupParamScheduler( MultiStepParamScheduler( [1, 0.1, 0.01], milestones=[60000, 80000], num_updates=90000, ), 0.001, 100 / 90000 ), max_iter=90000 ) """ # NOTES: in the most general case, every LR can use its own scheduler. # Supporting this requires interaction with the optimizer when its parameter # group is initialized. For example, classyvision implements its own optimizer # that allows different schedulers for every parameter group. # To avoid this complexity, we use this class to support the most common cases # where the relative scale among all LRs stay unchanged during training. In this # case we only need a total of one scheduler that defines the relative LR multiplier. def __init__( self, optimizer: torch.optim.Optimizer, multiplier: ParamScheduler, max_iter: int, last_iter: int = -1, ): """ Args: optimizer, last_iter: See ``torch.optim.lr_scheduler._LRScheduler``. ``last_iter`` is the same as ``last_epoch``. multiplier: a fvcore ParamScheduler that defines the multiplier on every LR of the optimizer max_iter: the total number of training iterations """ if not isinstance(multiplier, ParamScheduler): raise ValueError( "_LRMultiplier(multiplier=) must be an instance of fvcore " f"ParamScheduler. Got {multiplier} instead." ) self._multiplier = multiplier self._max_iter = max_iter super().__init__(optimizer, last_epoch=last_iter) def state_dict(self): # fvcore schedulers are stateless. Only keep pytorch scheduler states return {"base_lrs": self.base_lrs, "last_epoch": self.last_epoch} def get_lr(self) -> List[float]: multiplier = self._multiplier(self.last_epoch / self._max_iter) return [base_lr * multiplier for base_lr in self.base_lrs] """ Content below is no longer needed! """ # NOTE: PyTorch's LR scheduler interface uses names that assume the LR changes # only on epoch boundaries. We typically use iteration based schedules instead. # As a result, "epoch" (e.g., as in self.last_epoch) should be understood to mean # "iteration" instead. # FIXME: ideally this would be achieved with a CombinedLRScheduler, separating # MultiStepLR with WarmupLR but the current LRScheduler design doesn't allow it. class WarmupMultiStepLR(torch.optim.lr_scheduler._LRScheduler): def __init__( self, optimizer: torch.optim.Optimizer, milestones: List[int], gamma: float = 0.1, warmup_factor: float = 0.001, warmup_iters: int = 1000, warmup_method: str = "linear", last_epoch: int = -1, ): logger.warning( "WarmupMultiStepLR is deprecated! Use LRMultipilier with fvcore ParamScheduler instead!" ) if not list(milestones) == sorted(milestones): raise ValueError( "Milestones should be a list of" " increasing integers. Got {}", milestones ) self.milestones = milestones self.gamma = gamma self.warmup_factor = warmup_factor self.warmup_iters = warmup_iters self.warmup_method = warmup_method super().__init__(optimizer, last_epoch) def get_lr(self) -> List[float]: warmup_factor = _get_warmup_factor_at_iter( self.warmup_method, self.last_epoch, self.warmup_iters, self.warmup_factor ) return [ base_lr * warmup_factor * self.gamma ** bisect_right(self.milestones, self.last_epoch) for base_lr in self.base_lrs ] def _compute_values(self) -> List[float]: # The new interface return self.get_lr() class WarmupCosineLR(torch.optim.lr_scheduler._LRScheduler): def __init__( self, optimizer: torch.optim.Optimizer, max_iters: int, warmup_factor: float = 0.001, warmup_iters: int = 1000, warmup_method: str = "linear", last_epoch: int = -1, ): logger.warning( "WarmupCosineLR is deprecated! Use LRMultipilier with fvcore ParamScheduler instead!" ) self.max_iters = max_iters self.warmup_factor = warmup_factor self.warmup_iters = warmup_iters self.warmup_method = warmup_method super().__init__(optimizer, last_epoch) def get_lr(self) -> List[float]: warmup_factor = _get_warmup_factor_at_iter( self.warmup_method, self.last_epoch, self.warmup_iters, self.warmup_factor ) # Different definitions of half-cosine with warmup are possible. For # simplicity we multiply the standard half-cosine schedule by the warmup # factor. An alternative is to start the period of the cosine at warmup_iters # instead of at 0. In the case that warmup_iters << max_iters the two are # very close to each other. return [ base_lr * warmup_factor * 0.5 * (1.0 + math.cos(math.pi * self.last_epoch / self.max_iters)) for base_lr in self.base_lrs ] def _compute_values(self) -> List[float]: # The new interface return self.get_lr() def _get_warmup_factor_at_iter( method: str, iter: int, warmup_iters: int, warmup_factor: float ) -> float: """ Return the learning rate warmup factor at a specific iteration. See :paper:`ImageNet in 1h` for more details. Args: method (str): warmup method; either "constant" or "linear". iter (int): iteration at which to calculate the warmup factor. warmup_iters (int): the number of warmup iterations. warmup_factor (float): the base warmup factor (the meaning changes according to the method used). Returns: float: the effective warmup factor at the given iteration. """ if iter >= warmup_iters: return 1.0 if method == "constant": return warmup_factor elif method == "linear": alpha = iter / warmup_iters return warmup_factor * (1 - alpha) + alpha else: raise ValueError("Unknown warmup method: {}".format(method)) ================================================ FILE: detectron2/detectron2/structures/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .boxes import Boxes, BoxMode, pairwise_iou, pairwise_ioa, pairwise_point_box_distance from .image_list import ImageList from .instances import Instances from .keypoints import Keypoints, heatmaps_to_keypoints from .masks import BitMasks, PolygonMasks, polygons_to_bitmask, ROIMasks from .rotated_boxes import RotatedBoxes from .rotated_boxes import pairwise_iou as pairwise_iou_rotated __all__ = [k for k in globals().keys() if not k.startswith("_")] from detectron2.utils.env import fixup_module_metadata fixup_module_metadata(__name__, globals(), __all__) del fixup_module_metadata ================================================ FILE: detectron2/detectron2/structures/boxes.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import math import numpy as np from enum import IntEnum, unique from typing import List, Tuple, Union import torch from torch import device _RawBoxType = Union[List[float], Tuple[float, ...], torch.Tensor, np.ndarray] @unique class BoxMode(IntEnum): """ Enum of different ways to represent a box. """ XYXY_ABS = 0 """ (x0, y0, x1, y1) in absolute floating points coordinates. The coordinates in range [0, width or height]. """ XYWH_ABS = 1 """ (x0, y0, w, h) in absolute floating points coordinates. """ XYXY_REL = 2 """ Not yet supported! (x0, y0, x1, y1) in range [0, 1]. They are relative to the size of the image. """ XYWH_REL = 3 """ Not yet supported! (x0, y0, w, h) in range [0, 1]. They are relative to the size of the image. """ XYWHA_ABS = 4 """ (xc, yc, w, h, a) in absolute floating points coordinates. (xc, yc) is the center of the rotated box, and the angle a is in degrees ccw. """ @staticmethod def convert(box: _RawBoxType, from_mode: "BoxMode", to_mode: "BoxMode") -> _RawBoxType: """ Args: box: can be a k-tuple, k-list or an Nxk array/tensor, where k = 4 or 5 from_mode, to_mode (BoxMode) Returns: The converted box of the same type. """ if from_mode == to_mode: return box original_type = type(box) is_numpy = isinstance(box, np.ndarray) single_box = isinstance(box, (list, tuple)) if single_box: assert len(box) == 4 or len(box) == 5, ( "BoxMode.convert takes either a k-tuple/list or an Nxk array/tensor," " where k == 4 or 5" ) arr = torch.tensor(box)[None, :] else: # avoid modifying the input box if is_numpy: arr = torch.from_numpy(np.asarray(box)).clone() else: arr = box.clone() assert to_mode not in [BoxMode.XYXY_REL, BoxMode.XYWH_REL] and from_mode not in [ BoxMode.XYXY_REL, BoxMode.XYWH_REL, ], "Relative mode not yet supported!" if from_mode == BoxMode.XYWHA_ABS and to_mode == BoxMode.XYXY_ABS: assert ( arr.shape[-1] == 5 ), "The last dimension of input shape must be 5 for XYWHA format" original_dtype = arr.dtype arr = arr.double() w = arr[:, 2] h = arr[:, 3] a = arr[:, 4] c = torch.abs(torch.cos(a * math.pi / 180.0)) s = torch.abs(torch.sin(a * math.pi / 180.0)) # This basically computes the horizontal bounding rectangle of the rotated box new_w = c * w + s * h new_h = c * h + s * w # convert center to top-left corner arr[:, 0] -= new_w / 2.0 arr[:, 1] -= new_h / 2.0 # bottom-right corner arr[:, 2] = arr[:, 0] + new_w arr[:, 3] = arr[:, 1] + new_h arr = arr[:, :4].to(dtype=original_dtype) elif from_mode == BoxMode.XYWH_ABS and to_mode == BoxMode.XYWHA_ABS: original_dtype = arr.dtype arr = arr.double() arr[:, 0] += arr[:, 2] / 2.0 arr[:, 1] += arr[:, 3] / 2.0 angles = torch.zeros((arr.shape[0], 1), dtype=arr.dtype) arr = torch.cat((arr, angles), axis=1).to(dtype=original_dtype) else: if to_mode == BoxMode.XYXY_ABS and from_mode == BoxMode.XYWH_ABS: arr[:, 2] += arr[:, 0] arr[:, 3] += arr[:, 1] elif from_mode == BoxMode.XYXY_ABS and to_mode == BoxMode.XYWH_ABS: arr[:, 2] -= arr[:, 0] arr[:, 3] -= arr[:, 1] else: raise NotImplementedError( "Conversion from BoxMode {} to {} is not supported yet".format( from_mode, to_mode ) ) if single_box: return original_type(arr.flatten().tolist()) if is_numpy: return arr.numpy() else: return arr class Boxes: """ This structure stores a list of boxes as a Nx4 torch.Tensor. It supports some common methods about boxes (`area`, `clip`, `nonempty`, etc), and also behaves like a Tensor (support indexing, `to(device)`, `.device`, and iteration over all boxes) Attributes: tensor (torch.Tensor): float matrix of Nx4. Each row is (x1, y1, x2, y2). """ def __init__(self, tensor: torch.Tensor): """ Args: tensor (Tensor[float]): a Nx4 matrix. Each row is (x1, y1, x2, y2). """ if not isinstance(tensor, torch.Tensor): tensor = torch.as_tensor(tensor, dtype=torch.float32, device=torch.device("cpu")) else: tensor = tensor.to(torch.float32) if tensor.numel() == 0: # Use reshape, so we don't end up creating a new tensor that does not depend on # the inputs (and consequently confuses jit) tensor = tensor.reshape((-1, 4)).to(dtype=torch.float32) assert tensor.dim() == 2 and tensor.size(-1) == 4, tensor.size() self.tensor = tensor def clone(self) -> "Boxes": """ Clone the Boxes. Returns: Boxes """ return Boxes(self.tensor.clone()) def to(self, device: torch.device): # Boxes are assumed float32 and does not support to(dtype) return Boxes(self.tensor.to(device=device)) def area(self) -> torch.Tensor: """ Computes the area of all the boxes. Returns: torch.Tensor: a vector with areas of each box. """ box = self.tensor area = (box[:, 2] - box[:, 0]) * (box[:, 3] - box[:, 1]) return area def clip(self, box_size: Tuple[int, int]) -> None: """ Clip (in place) the boxes by limiting x coordinates to the range [0, width] and y coordinates to the range [0, height]. Args: box_size (height, width): The clipping box's size. """ assert torch.isfinite(self.tensor).all(), "Box tensor contains infinite or NaN!" h, w = box_size x1 = self.tensor[:, 0].clamp(min=0, max=w) y1 = self.tensor[:, 1].clamp(min=0, max=h) x2 = self.tensor[:, 2].clamp(min=0, max=w) y2 = self.tensor[:, 3].clamp(min=0, max=h) self.tensor = torch.stack((x1, y1, x2, y2), dim=-1) def nonempty(self, threshold: float = 0.0) -> torch.Tensor: """ Find boxes that are non-empty. A box is considered empty, if either of its side is no larger than threshold. Returns: Tensor: a binary vector which represents whether each box is empty (False) or non-empty (True). """ box = self.tensor widths = box[:, 2] - box[:, 0] heights = box[:, 3] - box[:, 1] keep = (widths > threshold) & (heights > threshold) return keep def __getitem__(self, item) -> "Boxes": """ Args: item: int, slice, or a BoolTensor Returns: Boxes: Create a new :class:`Boxes` by indexing. The following usage are allowed: 1. `new_boxes = boxes[3]`: return a `Boxes` which contains only one box. 2. `new_boxes = boxes[2:10]`: return a slice of boxes. 3. `new_boxes = boxes[vector]`, where vector is a torch.BoolTensor with `length = len(boxes)`. Nonzero elements in the vector will be selected. Note that the returned Boxes might share storage with this Boxes, subject to Pytorch's indexing semantics. """ if isinstance(item, int): return Boxes(self.tensor[item].view(1, -1)) b = self.tensor[item] assert b.dim() == 2, "Indexing on Boxes with {} failed to return a matrix!".format(item) return Boxes(b) def __len__(self) -> int: return self.tensor.shape[0] def __repr__(self) -> str: return "Boxes(" + str(self.tensor) + ")" def inside_box(self, box_size: Tuple[int, int], boundary_threshold: int = 0) -> torch.Tensor: """ Args: box_size (height, width): Size of the reference box. boundary_threshold (int): Boxes that extend beyond the reference box boundary by more than boundary_threshold are considered "outside". Returns: a binary vector, indicating whether each box is inside the reference box. """ height, width = box_size inds_inside = ( (self.tensor[..., 0] >= -boundary_threshold) & (self.tensor[..., 1] >= -boundary_threshold) & (self.tensor[..., 2] < width + boundary_threshold) & (self.tensor[..., 3] < height + boundary_threshold) ) return inds_inside def get_centers(self) -> torch.Tensor: """ Returns: The box centers in a Nx2 array of (x, y). """ return (self.tensor[:, :2] + self.tensor[:, 2:]) / 2 def scale(self, scale_x: float, scale_y: float) -> None: """ Scale the box with horizontal and vertical scaling factors """ self.tensor[:, 0::2] *= scale_x self.tensor[:, 1::2] *= scale_y @classmethod def cat(cls, boxes_list: List["Boxes"]) -> "Boxes": """ Concatenates a list of Boxes into a single Boxes Arguments: boxes_list (list[Boxes]) Returns: Boxes: the concatenated Boxes """ assert isinstance(boxes_list, (list, tuple)) if len(boxes_list) == 0: return cls(torch.empty(0)) assert all([isinstance(box, Boxes) for box in boxes_list]) # use torch.cat (v.s. layers.cat) so the returned boxes never share storage with input cat_boxes = cls(torch.cat([b.tensor for b in boxes_list], dim=0)) return cat_boxes @property def device(self) -> device: return self.tensor.device # type "Iterator[torch.Tensor]", yield, and iter() not supported by torchscript # https://github.com/pytorch/pytorch/issues/18627 @torch.jit.unused def __iter__(self): """ Yield a box as a Tensor of shape (4,) at a time. """ yield from self.tensor def pairwise_intersection(boxes1: Boxes, boxes2: Boxes) -> torch.Tensor: """ Given two lists of boxes of size N and M, compute the intersection area between __all__ N x M pairs of boxes. The box order must be (xmin, ymin, xmax, ymax) Args: boxes1,boxes2 (Boxes): two `Boxes`. Contains N & M boxes, respectively. Returns: Tensor: intersection, sized [N,M]. """ boxes1, boxes2 = boxes1.tensor, boxes2.tensor width_height = torch.min(boxes1[:, None, 2:], boxes2[:, 2:]) - torch.max( boxes1[:, None, :2], boxes2[:, :2] ) # [N,M,2] width_height.clamp_(min=0) # [N,M,2] intersection = width_height.prod(dim=2) # [N,M] return intersection # implementation from https://github.com/kuangliu/torchcv/blob/master/torchcv/utils/box.py # with slight modifications def pairwise_iou(boxes1: Boxes, boxes2: Boxes) -> torch.Tensor: """ Given two lists of boxes of size N and M, compute the IoU (intersection over union) between **all** N x M pairs of boxes. The box order must be (xmin, ymin, xmax, ymax). Args: boxes1,boxes2 (Boxes): two `Boxes`. Contains N & M boxes, respectively. Returns: Tensor: IoU, sized [N,M]. """ area1 = boxes1.area() # [N] area2 = boxes2.area() # [M] inter = pairwise_intersection(boxes1, boxes2) # handle empty boxes iou = torch.where( inter > 0, inter / (area1[:, None] + area2 - inter), torch.zeros(1, dtype=inter.dtype, device=inter.device), ) return iou def pairwise_ioa(boxes1: Boxes, boxes2: Boxes) -> torch.Tensor: """ Similar to :func:`pariwise_iou` but compute the IoA (intersection over boxes2 area). Args: boxes1,boxes2 (Boxes): two `Boxes`. Contains N & M boxes, respectively. Returns: Tensor: IoA, sized [N,M]. """ area2 = boxes2.area() # [M] inter = pairwise_intersection(boxes1, boxes2) # handle empty boxes ioa = torch.where( inter > 0, inter / area2, torch.zeros(1, dtype=inter.dtype, device=inter.device) ) return ioa def pairwise_point_box_distance(points: torch.Tensor, boxes: Boxes): """ Pairwise distance between N points and M boxes. The distance between a point and a box is represented by the distance from the point to 4 edges of the box. Distances are all positive when the point is inside the box. Args: points: Nx2 coordinates. Each row is (x, y) boxes: M boxes Returns: Tensor: distances of size (N, M, 4). The 4 values are distances from the point to the left, top, right, bottom of the box. """ x, y = points.unsqueeze(dim=2).unbind(dim=1) # (N, 1) x0, y0, x1, y1 = boxes.tensor.unsqueeze(dim=0).unbind(dim=2) # (1, M) return torch.stack([x - x0, y - y0, x1 - x, y1 - y], dim=2) def matched_pairwise_iou(boxes1: Boxes, boxes2: Boxes) -> torch.Tensor: """ Compute pairwise intersection over union (IOU) of two sets of matched boxes that have the same number of boxes. Similar to :func:`pairwise_iou`, but computes only diagonal elements of the matrix. Args: boxes1 (Boxes): bounding boxes, sized [N,4]. boxes2 (Boxes): same length as boxes1 Returns: Tensor: iou, sized [N]. """ assert len(boxes1) == len( boxes2 ), "boxlists should have the same" "number of entries, got {}, {}".format( len(boxes1), len(boxes2) ) area1 = boxes1.area() # [N] area2 = boxes2.area() # [N] box1, box2 = boxes1.tensor, boxes2.tensor lt = torch.max(box1[:, :2], box2[:, :2]) # [N,2] rb = torch.min(box1[:, 2:], box2[:, 2:]) # [N,2] wh = (rb - lt).clamp(min=0) # [N,2] inter = wh[:, 0] * wh[:, 1] # [N] iou = inter / (area1 + area2 - inter) # [N] return iou ================================================ FILE: detectron2/detectron2/structures/image_list.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from __future__ import division from typing import Any, Dict, List, Optional, Tuple import torch from torch import device from torch.nn import functional as F from detectron2.layers.wrappers import move_device_like, shapes_to_tensor class ImageList(object): """ Structure that holds a list of images (of possibly varying sizes) as a single tensor. This works by padding the images to the same size. The original sizes of each image is stored in `image_sizes`. Attributes: image_sizes (list[tuple[int, int]]): each tuple is (h, w). During tracing, it becomes list[Tensor] instead. """ def __init__(self, tensor: torch.Tensor, image_sizes: List[Tuple[int, int]]): """ Arguments: tensor (Tensor): of shape (N, H, W) or (N, C_1, ..., C_K, H, W) where K >= 1 image_sizes (list[tuple[int, int]]): Each tuple is (h, w). It can be smaller than (H, W) due to padding. """ self.tensor = tensor self.image_sizes = image_sizes def __len__(self) -> int: return len(self.image_sizes) def __getitem__(self, idx) -> torch.Tensor: """ Access the individual image in its original size. Args: idx: int or slice Returns: Tensor: an image of shape (H, W) or (C_1, ..., C_K, H, W) where K >= 1 """ size = self.image_sizes[idx] return self.tensor[idx, ..., : size[0], : size[1]] @torch.jit.unused def to(self, *args: Any, **kwargs: Any) -> "ImageList": cast_tensor = self.tensor.to(*args, **kwargs) return ImageList(cast_tensor, self.image_sizes) @property def device(self) -> device: return self.tensor.device @staticmethod def from_tensors( tensors: List[torch.Tensor], size_divisibility: int = 0, pad_value: float = 0.0, padding_constraints: Optional[Dict[str, int]] = None, ) -> "ImageList": """ Args: tensors: a tuple or list of `torch.Tensor`, each of shape (Hi, Wi) or (C_1, ..., C_K, Hi, Wi) where K >= 1. The Tensors will be padded to the same shape with `pad_value`. size_divisibility (int): If `size_divisibility > 0`, add padding to ensure the common height and width is divisible by `size_divisibility`. This depends on the model and many models need a divisibility of 32. pad_value (float): value to pad. padding_constraints (optional[Dict]): If given, it would follow the format as {"size_divisibility": int, "square_size": int}, where `size_divisibility` will overwrite the above one if presented and `square_size` indicates the square padding size if `square_size` > 0. Returns: an `ImageList`. """ assert len(tensors) > 0 assert isinstance(tensors, (tuple, list)) for t in tensors: assert isinstance(t, torch.Tensor), type(t) assert t.shape[:-2] == tensors[0].shape[:-2], t.shape image_sizes = [(im.shape[-2], im.shape[-1]) for im in tensors] image_sizes_tensor = [shapes_to_tensor(x) for x in image_sizes] max_size = torch.stack(image_sizes_tensor).max(0).values if padding_constraints is not None: square_size = padding_constraints.get("square_size", 0) if square_size > 0: # pad to square. max_size[0] = max_size[1] = square_size if "size_divisibility" in padding_constraints: size_divisibility = padding_constraints["size_divisibility"] if size_divisibility > 1: stride = size_divisibility # the last two dims are H,W, both subject to divisibility requirement max_size = (max_size + (stride - 1)).div(stride, rounding_mode="floor") * stride # handle weirdness of scripting and tracing ... if torch.jit.is_scripting(): max_size: List[int] = max_size.to(dtype=torch.long).tolist() else: if torch.jit.is_tracing(): image_sizes = image_sizes_tensor if len(tensors) == 1: # This seems slightly (2%) faster. # TODO: check whether it's faster for multiple images as well image_size = image_sizes[0] padding_size = [0, max_size[-1] - image_size[1], 0, max_size[-2] - image_size[0]] batched_imgs = F.pad(tensors[0], padding_size, value=pad_value).unsqueeze_(0) else: # max_size can be a tensor in tracing mode, therefore convert to list batch_shape = [len(tensors)] + list(tensors[0].shape[:-2]) + list(max_size) device = ( None if torch.jit.is_scripting() else ("cpu" if torch.jit.is_tracing() else None) ) batched_imgs = tensors[0].new_full(batch_shape, pad_value, device=device) batched_imgs = move_device_like(batched_imgs, tensors[0]) for i, img in enumerate(tensors): # Use `batched_imgs` directly instead of `img, pad_img = zip(tensors, batched_imgs)` # Tracing mode cannot capture `copy_()` of temporary locals batched_imgs[i, ..., : img.shape[-2], : img.shape[-1]].copy_(img) return ImageList(batched_imgs.contiguous(), image_sizes) ================================================ FILE: detectron2/detectron2/structures/instances.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import itertools import warnings from typing import Any, Dict, List, Tuple, Union import torch class Instances: """ This class represents a list of instances in an image. It stores the attributes of instances (e.g., boxes, masks, labels, scores) as "fields". All fields must have the same ``__len__`` which is the number of instances. All other (non-field) attributes of this class are considered private: they must start with '_' and are not modifiable by a user. Some basic usage: 1. Set/get/check a field: .. code-block:: python instances.gt_boxes = Boxes(...) print(instances.pred_masks) # a tensor of shape (N, H, W) print('gt_masks' in instances) 2. ``len(instances)`` returns the number of instances 3. Indexing: ``instances[indices]`` will apply the indexing on all the fields and returns a new :class:`Instances`. Typically, ``indices`` is a integer vector of indices, or a binary mask of length ``num_instances`` .. code-block:: python category_3_detections = instances[instances.pred_classes == 3] confident_detections = instances[instances.scores > 0.9] """ def __init__(self, image_size: Tuple[int, int], **kwargs: Any): """ Args: image_size (height, width): the spatial size of the image. kwargs: fields to add to this `Instances`. """ self._image_size = image_size self._fields: Dict[str, Any] = {} for k, v in kwargs.items(): self.set(k, v) @property def image_size(self) -> Tuple[int, int]: """ Returns: tuple: height, width """ return self._image_size def __setattr__(self, name: str, val: Any) -> None: if name.startswith("_"): super().__setattr__(name, val) else: self.set(name, val) def __getattr__(self, name: str) -> Any: if name == "_fields" or name not in self._fields: raise AttributeError("Cannot find field '{}' in the given Instances!".format(name)) return self._fields[name] def set(self, name: str, value: Any) -> None: """ Set the field named `name` to `value`. The length of `value` must be the number of instances, and must agree with other existing fields in this object. """ with warnings.catch_warnings(record=True): data_len = len(value) if len(self._fields): assert ( len(self) == data_len ), "Adding a field of length {} to a Instances of length {}".format(data_len, len(self)) self._fields[name] = value def has(self, name: str) -> bool: """ Returns: bool: whether the field called `name` exists. """ return name in self._fields def remove(self, name: str) -> None: """ Remove the field called `name`. """ del self._fields[name] def get(self, name: str) -> Any: """ Returns the field called `name`. """ return self._fields[name] def get_fields(self) -> Dict[str, Any]: """ Returns: dict: a dict which maps names (str) to data of the fields Modifying the returned dict will modify this instance. """ return self._fields # Tensor-like methods def to(self, *args: Any, **kwargs: Any) -> "Instances": """ Returns: Instances: all fields are called with a `to(device)`, if the field has this method. """ ret = Instances(self._image_size) for k, v in self._fields.items(): if hasattr(v, "to"): v = v.to(*args, **kwargs) ret.set(k, v) return ret def __getitem__(self, item: Union[int, slice, torch.BoolTensor]) -> "Instances": """ Args: item: an index-like object and will be used to index all the fields. Returns: If `item` is a string, return the data in the corresponding field. Otherwise, returns an `Instances` where all fields are indexed by `item`. """ if type(item) == int: if item >= len(self) or item < -len(self): raise IndexError("Instances index out of range!") else: item = slice(item, None, len(self)) ret = Instances(self._image_size) for k, v in self._fields.items(): ret.set(k, v[item]) return ret def __len__(self) -> int: for v in self._fields.values(): # use __len__ because len() has to be int and is not friendly to tracing return v.__len__() raise NotImplementedError("Empty Instances does not support __len__!") def __iter__(self): raise NotImplementedError("`Instances` object is not iterable!") @staticmethod def cat(instance_lists: List["Instances"]) -> "Instances": """ Args: instance_lists (list[Instances]) Returns: Instances """ assert all(isinstance(i, Instances) for i in instance_lists) assert len(instance_lists) > 0 if len(instance_lists) == 1: return instance_lists[0] image_size = instance_lists[0].image_size if not isinstance(image_size, torch.Tensor): # could be a tensor in tracing for i in instance_lists[1:]: assert i.image_size == image_size ret = Instances(image_size) for k in instance_lists[0]._fields.keys(): values = [i.get(k) for i in instance_lists] v0 = values[0] if isinstance(v0, torch.Tensor): values = torch.cat(values, dim=0) elif isinstance(v0, list): values = list(itertools.chain(*values)) elif hasattr(type(v0), "cat"): values = type(v0).cat(values) else: raise ValueError("Unsupported type {} for concatenation".format(type(v0))) ret.set(k, values) return ret def __str__(self) -> str: s = self.__class__.__name__ + "(" s += "num_instances={}, ".format(len(self)) s += "image_height={}, ".format(self._image_size[0]) s += "image_width={}, ".format(self._image_size[1]) s += "fields=[{}])".format(", ".join((f"{k}: {v}" for k, v in self._fields.items()))) return s __repr__ = __str__ ================================================ FILE: detectron2/detectron2/structures/keypoints.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from typing import Any, List, Tuple, Union import torch from torch.nn import functional as F class Keypoints: """ Stores keypoint **annotation** data. GT Instances have a `gt_keypoints` property containing the x,y location and visibility flag of each keypoint. This tensor has shape (N, K, 3) where N is the number of instances and K is the number of keypoints per instance. The visibility flag follows the COCO format and must be one of three integers: * v=0: not labeled (in which case x=y=0) * v=1: labeled but not visible * v=2: labeled and visible """ def __init__(self, keypoints: Union[torch.Tensor, np.ndarray, List[List[float]]]): """ Arguments: keypoints: A Tensor, numpy array, or list of the x, y, and visibility of each keypoint. The shape should be (N, K, 3) where N is the number of instances, and K is the number of keypoints per instance. """ device = keypoints.device if isinstance(keypoints, torch.Tensor) else torch.device("cpu") keypoints = torch.as_tensor(keypoints, dtype=torch.float32, device=device) assert keypoints.dim() == 3 and keypoints.shape[2] == 3, keypoints.shape self.tensor = keypoints def __len__(self) -> int: return self.tensor.size(0) def to(self, *args: Any, **kwargs: Any) -> "Keypoints": return type(self)(self.tensor.to(*args, **kwargs)) @property def device(self) -> torch.device: return self.tensor.device def to_heatmap(self, boxes: torch.Tensor, heatmap_size: int) -> torch.Tensor: """ Convert keypoint annotations to a heatmap of one-hot labels for training, as described in :paper:`Mask R-CNN`. Arguments: boxes: Nx4 tensor, the boxes to draw the keypoints to Returns: heatmaps: A tensor of shape (N, K), each element is integer spatial label in the range [0, heatmap_size**2 - 1] for each keypoint in the input. valid: A tensor of shape (N, K) containing whether each keypoint is in the roi or not. """ return _keypoints_to_heatmap(self.tensor, boxes, heatmap_size) def __getitem__(self, item: Union[int, slice, torch.BoolTensor]) -> "Keypoints": """ Create a new `Keypoints` by indexing on this `Keypoints`. The following usage are allowed: 1. `new_kpts = kpts[3]`: return a `Keypoints` which contains only one instance. 2. `new_kpts = kpts[2:10]`: return a slice of key points. 3. `new_kpts = kpts[vector]`, where vector is a torch.ByteTensor with `length = len(kpts)`. Nonzero elements in the vector will be selected. Note that the returned Keypoints might share storage with this Keypoints, subject to Pytorch's indexing semantics. """ if isinstance(item, int): return Keypoints([self.tensor[item]]) return Keypoints(self.tensor[item]) def __repr__(self) -> str: s = self.__class__.__name__ + "(" s += "num_instances={})".format(len(self.tensor)) return s @staticmethod def cat(keypoints_list: List["Keypoints"]) -> "Keypoints": """ Concatenates a list of Keypoints into a single Keypoints Arguments: keypoints_list (list[Keypoints]) Returns: Keypoints: the concatenated Keypoints """ assert isinstance(keypoints_list, (list, tuple)) assert len(keypoints_list) > 0 assert all(isinstance(keypoints, Keypoints) for keypoints in keypoints_list) cat_kpts = type(keypoints_list[0])( torch.cat([kpts.tensor for kpts in keypoints_list], dim=0) ) return cat_kpts # TODO make this nicer, this is a direct translation from C2 (but removing the inner loop) def _keypoints_to_heatmap( keypoints: torch.Tensor, rois: torch.Tensor, heatmap_size: int ) -> Tuple[torch.Tensor, torch.Tensor]: """ Encode keypoint locations into a target heatmap for use in SoftmaxWithLoss across space. Maps keypoints from the half-open interval [x1, x2) on continuous image coordinates to the closed interval [0, heatmap_size - 1] on discrete image coordinates. We use the continuous-discrete conversion from Heckbert 1990 ("What is the coordinate of a pixel?"): d = floor(c) and c = d + 0.5, where d is a discrete coordinate and c is a continuous coordinate. Arguments: keypoints: tensor of keypoint locations in of shape (N, K, 3). rois: Nx4 tensor of rois in xyxy format heatmap_size: integer side length of square heatmap. Returns: heatmaps: A tensor of shape (N, K) containing an integer spatial label in the range [0, heatmap_size**2 - 1] for each keypoint in the input. valid: A tensor of shape (N, K) containing whether each keypoint is in the roi or not. """ if rois.numel() == 0: return rois.new().long(), rois.new().long() offset_x = rois[:, 0] offset_y = rois[:, 1] scale_x = heatmap_size / (rois[:, 2] - rois[:, 0]) scale_y = heatmap_size / (rois[:, 3] - rois[:, 1]) offset_x = offset_x[:, None] offset_y = offset_y[:, None] scale_x = scale_x[:, None] scale_y = scale_y[:, None] x = keypoints[..., 0] y = keypoints[..., 1] x_boundary_inds = x == rois[:, 2][:, None] y_boundary_inds = y == rois[:, 3][:, None] x = (x - offset_x) * scale_x x = x.floor().long() y = (y - offset_y) * scale_y y = y.floor().long() x[x_boundary_inds] = heatmap_size - 1 y[y_boundary_inds] = heatmap_size - 1 valid_loc = (x >= 0) & (y >= 0) & (x < heatmap_size) & (y < heatmap_size) vis = keypoints[..., 2] > 0 valid = (valid_loc & vis).long() lin_ind = y * heatmap_size + x heatmaps = lin_ind * valid return heatmaps, valid @torch.jit.script_if_tracing def heatmaps_to_keypoints(maps: torch.Tensor, rois: torch.Tensor) -> torch.Tensor: """ Extract predicted keypoint locations from heatmaps. Args: maps (Tensor): (#ROIs, #keypoints, POOL_H, POOL_W). The predicted heatmap of logits for each ROI and each keypoint. rois (Tensor): (#ROIs, 4). The box of each ROI. Returns: Tensor of shape (#ROIs, #keypoints, 4) with the last dimension corresponding to (x, y, logit, score) for each keypoint. When converting discrete pixel indices in an NxN image to a continuous keypoint coordinate, we maintain consistency with :meth:`Keypoints.to_heatmap` by using the conversion from Heckbert 1990: c = d + 0.5, where d is a discrete coordinate and c is a continuous coordinate. """ # The decorator use of torch.no_grad() was not supported by torchscript. # https://github.com/pytorch/pytorch/issues/44768 maps = maps.detach() rois = rois.detach() offset_x = rois[:, 0] offset_y = rois[:, 1] widths = (rois[:, 2] - rois[:, 0]).clamp(min=1) heights = (rois[:, 3] - rois[:, 1]).clamp(min=1) widths_ceil = widths.ceil() heights_ceil = heights.ceil() num_rois, num_keypoints = maps.shape[:2] xy_preds = maps.new_zeros(rois.shape[0], num_keypoints, 4) width_corrections = widths / widths_ceil height_corrections = heights / heights_ceil keypoints_idx = torch.arange(num_keypoints, device=maps.device) for i in range(num_rois): outsize = (int(heights_ceil[i]), int(widths_ceil[i])) roi_map = F.interpolate( maps[[i]], size=outsize, mode="bicubic", align_corners=False ).squeeze( 0 ) # #keypoints x H x W # softmax over the spatial region max_score, _ = roi_map.view(num_keypoints, -1).max(1) max_score = max_score.view(num_keypoints, 1, 1) tmp_full_resolution = (roi_map - max_score).exp_() tmp_pool_resolution = (maps[i] - max_score).exp_() # Produce scores over the region H x W, but normalize with POOL_H x POOL_W, # so that the scores of objects of different absolute sizes will be more comparable roi_map_scores = tmp_full_resolution / tmp_pool_resolution.sum((1, 2), keepdim=True) w = roi_map.shape[2] pos = roi_map.view(num_keypoints, -1).argmax(1) x_int = pos % w y_int = (pos - x_int) // w assert ( roi_map_scores[keypoints_idx, y_int, x_int] == roi_map_scores.view(num_keypoints, -1).max(1)[0] ).all() x = (x_int.float() + 0.5) * width_corrections[i] y = (y_int.float() + 0.5) * height_corrections[i] xy_preds[i, :, 0] = x + offset_x[i] xy_preds[i, :, 1] = y + offset_y[i] xy_preds[i, :, 2] = roi_map[keypoints_idx, y_int, x_int] xy_preds[i, :, 3] = roi_map_scores[keypoints_idx, y_int, x_int] return xy_preds ================================================ FILE: detectron2/detectron2/structures/masks.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import itertools import numpy as np from typing import Any, Iterator, List, Union import pycocotools.mask as mask_util import torch from torch import device from detectron2.layers.roi_align import ROIAlign from detectron2.utils.memory import retry_if_cuda_oom from .boxes import Boxes def polygon_area(x, y): # Using the shoelace formula # https://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates return 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1))) def polygons_to_bitmask(polygons: List[np.ndarray], height: int, width: int) -> np.ndarray: """ Args: polygons (list[ndarray]): each array has shape (Nx2,) height, width (int) Returns: ndarray: a bool mask of shape (height, width) """ if len(polygons) == 0: # COCOAPI does not support empty polygons return np.zeros((height, width)).astype(np.bool) rles = mask_util.frPyObjects(polygons, height, width) rle = mask_util.merge(rles) return mask_util.decode(rle).astype(np.bool) def rasterize_polygons_within_box( polygons: List[np.ndarray], box: np.ndarray, mask_size: int ) -> torch.Tensor: """ Rasterize the polygons into a mask image and crop the mask content in the given box. The cropped mask is resized to (mask_size, mask_size). This function is used when generating training targets for mask head in Mask R-CNN. Given original ground-truth masks for an image, new ground-truth mask training targets in the size of `mask_size x mask_size` must be provided for each predicted box. This function will be called to produce such targets. Args: polygons (list[ndarray[float]]): a list of polygons, which represents an instance. box: 4-element numpy array mask_size (int): Returns: Tensor: BoolTensor of shape (mask_size, mask_size) """ # 1. Shift the polygons w.r.t the boxes w, h = box[2] - box[0], box[3] - box[1] polygons = copy.deepcopy(polygons) for p in polygons: p[0::2] = p[0::2] - box[0] p[1::2] = p[1::2] - box[1] # 2. Rescale the polygons to the new box size # max() to avoid division by small number ratio_h = mask_size / max(h, 0.1) ratio_w = mask_size / max(w, 0.1) if ratio_h == ratio_w: for p in polygons: p *= ratio_h else: for p in polygons: p[0::2] *= ratio_w p[1::2] *= ratio_h # 3. Rasterize the polygons with coco api mask = polygons_to_bitmask(polygons, mask_size, mask_size) mask = torch.from_numpy(mask) return mask class BitMasks: """ This class stores the segmentation masks for all objects in one image, in the form of bitmaps. Attributes: tensor: bool Tensor of N,H,W, representing N instances in the image. """ def __init__(self, tensor: Union[torch.Tensor, np.ndarray]): """ Args: tensor: bool Tensor of N,H,W, representing N instances in the image. """ if isinstance(tensor, torch.Tensor): tensor = tensor.to(torch.bool) else: tensor = torch.as_tensor(tensor, dtype=torch.bool, device=torch.device("cpu")) assert tensor.dim() == 3, tensor.size() self.image_size = tensor.shape[1:] self.tensor = tensor @torch.jit.unused def to(self, *args: Any, **kwargs: Any) -> "BitMasks": return BitMasks(self.tensor.to(*args, **kwargs)) @property def device(self) -> torch.device: return self.tensor.device @torch.jit.unused def __getitem__(self, item: Union[int, slice, torch.BoolTensor]) -> "BitMasks": """ Returns: BitMasks: Create a new :class:`BitMasks` by indexing. The following usage are allowed: 1. `new_masks = masks[3]`: return a `BitMasks` which contains only one mask. 2. `new_masks = masks[2:10]`: return a slice of masks. 3. `new_masks = masks[vector]`, where vector is a torch.BoolTensor with `length = len(masks)`. Nonzero elements in the vector will be selected. Note that the returned object might share storage with this object, subject to Pytorch's indexing semantics. """ if isinstance(item, int): return BitMasks(self.tensor[item].unsqueeze(0)) m = self.tensor[item] assert m.dim() == 3, "Indexing on BitMasks with {} returns a tensor with shape {}!".format( item, m.shape ) return BitMasks(m) @torch.jit.unused def __iter__(self) -> torch.Tensor: yield from self.tensor @torch.jit.unused def __repr__(self) -> str: s = self.__class__.__name__ + "(" s += "num_instances={})".format(len(self.tensor)) return s def __len__(self) -> int: return self.tensor.shape[0] def nonempty(self) -> torch.Tensor: """ Find masks that are non-empty. Returns: Tensor: a BoolTensor which represents whether each mask is empty (False) or non-empty (True). """ return self.tensor.flatten(1).any(dim=1) @staticmethod def from_polygon_masks( polygon_masks: Union["PolygonMasks", List[List[np.ndarray]]], height: int, width: int ) -> "BitMasks": """ Args: polygon_masks (list[list[ndarray]] or PolygonMasks) height, width (int) """ if isinstance(polygon_masks, PolygonMasks): polygon_masks = polygon_masks.polygons masks = [polygons_to_bitmask(p, height, width) for p in polygon_masks] if len(masks): return BitMasks(torch.stack([torch.from_numpy(x) for x in masks])) else: return BitMasks(torch.empty(0, height, width, dtype=torch.bool)) @staticmethod def from_roi_masks(roi_masks: "ROIMasks", height: int, width: int) -> "BitMasks": """ Args: roi_masks: height, width (int): """ return roi_masks.to_bitmasks(height, width) def crop_and_resize(self, boxes: torch.Tensor, mask_size: int) -> torch.Tensor: """ Crop each bitmask by the given box, and resize results to (mask_size, mask_size). This can be used to prepare training targets for Mask R-CNN. It has less reconstruction error compared to rasterization with polygons. However we observe no difference in accuracy, but BitMasks requires more memory to store all the masks. Args: boxes (Tensor): Nx4 tensor storing the boxes for each mask mask_size (int): the size of the rasterized mask. Returns: Tensor: A bool tensor of shape (N, mask_size, mask_size), where N is the number of predicted boxes for this image. """ assert len(boxes) == len(self), "{} != {}".format(len(boxes), len(self)) device = self.tensor.device batch_inds = torch.arange(len(boxes), device=device).to(dtype=boxes.dtype)[:, None] rois = torch.cat([batch_inds, boxes], dim=1) # Nx5 bit_masks = self.tensor.to(dtype=torch.float32) rois = rois.to(device=device) output = ( ROIAlign((mask_size, mask_size), 1.0, 0, aligned=True) .forward(bit_masks[:, None, :, :], rois) .squeeze(1) ) output = output >= 0.5 return output def get_bounding_boxes(self) -> Boxes: """ Returns: Boxes: tight bounding boxes around bitmasks. If a mask is empty, it's bounding box will be all zero. """ boxes = torch.zeros(self.tensor.shape[0], 4, dtype=torch.float32) x_any = torch.any(self.tensor, dim=1) y_any = torch.any(self.tensor, dim=2) for idx in range(self.tensor.shape[0]): x = torch.where(x_any[idx, :])[0] y = torch.where(y_any[idx, :])[0] if len(x) > 0 and len(y) > 0: boxes[idx, :] = torch.as_tensor( [x[0], y[0], x[-1] + 1, y[-1] + 1], dtype=torch.float32 ) return Boxes(boxes) @staticmethod def cat(bitmasks_list: List["BitMasks"]) -> "BitMasks": """ Concatenates a list of BitMasks into a single BitMasks Arguments: bitmasks_list (list[BitMasks]) Returns: BitMasks: the concatenated BitMasks """ assert isinstance(bitmasks_list, (list, tuple)) assert len(bitmasks_list) > 0 assert all(isinstance(bitmask, BitMasks) for bitmask in bitmasks_list) cat_bitmasks = type(bitmasks_list[0])(torch.cat([bm.tensor for bm in bitmasks_list], dim=0)) return cat_bitmasks class PolygonMasks: """ This class stores the segmentation masks for all objects in one image, in the form of polygons. Attributes: polygons: list[list[ndarray]]. Each ndarray is a float64 vector representing a polygon. """ def __init__(self, polygons: List[List[Union[torch.Tensor, np.ndarray]]]): """ Arguments: polygons (list[list[np.ndarray]]): The first level of the list correspond to individual instances, the second level to all the polygons that compose the instance, and the third level to the polygon coordinates. The third level array should have the format of [x0, y0, x1, y1, ..., xn, yn] (n >= 3). """ if not isinstance(polygons, list): raise ValueError( "Cannot create PolygonMasks: Expect a list of list of polygons per image. " "Got '{}' instead.".format(type(polygons)) ) def _make_array(t: Union[torch.Tensor, np.ndarray]) -> np.ndarray: # Use float64 for higher precision, because why not? # Always put polygons on CPU (self.to is a no-op) since they # are supposed to be small tensors. # May need to change this assumption if GPU placement becomes useful if isinstance(t, torch.Tensor): t = t.cpu().numpy() return np.asarray(t).astype("float64") def process_polygons( polygons_per_instance: List[Union[torch.Tensor, np.ndarray]] ) -> List[np.ndarray]: if not isinstance(polygons_per_instance, list): raise ValueError( "Cannot create polygons: Expect a list of polygons per instance. " "Got '{}' instead.".format(type(polygons_per_instance)) ) # transform each polygon to a numpy array polygons_per_instance = [_make_array(p) for p in polygons_per_instance] for polygon in polygons_per_instance: if len(polygon) % 2 != 0 or len(polygon) < 6: raise ValueError(f"Cannot create a polygon from {len(polygon)} coordinates.") return polygons_per_instance self.polygons: List[List[np.ndarray]] = [ process_polygons(polygons_per_instance) for polygons_per_instance in polygons ] def to(self, *args: Any, **kwargs: Any) -> "PolygonMasks": return self @property def device(self) -> torch.device: return torch.device("cpu") def get_bounding_boxes(self) -> Boxes: """ Returns: Boxes: tight bounding boxes around polygon masks. """ boxes = torch.zeros(len(self.polygons), 4, dtype=torch.float32) for idx, polygons_per_instance in enumerate(self.polygons): minxy = torch.as_tensor([float("inf"), float("inf")], dtype=torch.float32) maxxy = torch.zeros(2, dtype=torch.float32) for polygon in polygons_per_instance: coords = torch.from_numpy(polygon).view(-1, 2).to(dtype=torch.float32) minxy = torch.min(minxy, torch.min(coords, dim=0).values) maxxy = torch.max(maxxy, torch.max(coords, dim=0).values) boxes[idx, :2] = minxy boxes[idx, 2:] = maxxy return Boxes(boxes) def nonempty(self) -> torch.Tensor: """ Find masks that are non-empty. Returns: Tensor: a BoolTensor which represents whether each mask is empty (False) or not (True). """ keep = [1 if len(polygon) > 0 else 0 for polygon in self.polygons] return torch.from_numpy(np.asarray(keep, dtype=np.bool)) def __getitem__(self, item: Union[int, slice, List[int], torch.BoolTensor]) -> "PolygonMasks": """ Support indexing over the instances and return a `PolygonMasks` object. `item` can be: 1. An integer. It will return an object with only one instance. 2. A slice. It will return an object with the selected instances. 3. A list[int]. It will return an object with the selected instances, correpsonding to the indices in the list. 4. A vector mask of type BoolTensor, whose length is num_instances. It will return an object with the instances whose mask is nonzero. """ if isinstance(item, int): selected_polygons = [self.polygons[item]] elif isinstance(item, slice): selected_polygons = self.polygons[item] elif isinstance(item, list): selected_polygons = [self.polygons[i] for i in item] elif isinstance(item, torch.Tensor): # Polygons is a list, so we have to move the indices back to CPU. if item.dtype == torch.bool: assert item.dim() == 1, item.shape item = item.nonzero().squeeze(1).cpu().numpy().tolist() elif item.dtype in [torch.int32, torch.int64]: item = item.cpu().numpy().tolist() else: raise ValueError("Unsupported tensor dtype={} for indexing!".format(item.dtype)) selected_polygons = [self.polygons[i] for i in item] return PolygonMasks(selected_polygons) def __iter__(self) -> Iterator[List[np.ndarray]]: """ Yields: list[ndarray]: the polygons for one instance. Each Tensor is a float64 vector representing a polygon. """ return iter(self.polygons) def __repr__(self) -> str: s = self.__class__.__name__ + "(" s += "num_instances={})".format(len(self.polygons)) return s def __len__(self) -> int: return len(self.polygons) def crop_and_resize(self, boxes: torch.Tensor, mask_size: int) -> torch.Tensor: """ Crop each mask by the given box, and resize results to (mask_size, mask_size). This can be used to prepare training targets for Mask R-CNN. Args: boxes (Tensor): Nx4 tensor storing the boxes for each mask mask_size (int): the size of the rasterized mask. Returns: Tensor: A bool tensor of shape (N, mask_size, mask_size), where N is the number of predicted boxes for this image. """ assert len(boxes) == len(self), "{} != {}".format(len(boxes), len(self)) device = boxes.device # Put boxes on the CPU, as the polygon representation is not efficient GPU-wise # (several small tensors for representing a single instance mask) boxes = boxes.to(torch.device("cpu")) results = [ rasterize_polygons_within_box(poly, box.numpy(), mask_size) for poly, box in zip(self.polygons, boxes) ] """ poly: list[list[float]], the polygons for one instance box: a tensor of shape (4,) """ if len(results) == 0: return torch.empty(0, mask_size, mask_size, dtype=torch.bool, device=device) return torch.stack(results, dim=0).to(device=device) def area(self): """ Computes area of the mask. Only works with Polygons, using the shoelace formula: https://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates Returns: Tensor: a vector, area for each instance """ area = [] for polygons_per_instance in self.polygons: area_per_instance = 0 for p in polygons_per_instance: area_per_instance += polygon_area(p[0::2], p[1::2]) area.append(area_per_instance) return torch.tensor(area) @staticmethod def cat(polymasks_list: List["PolygonMasks"]) -> "PolygonMasks": """ Concatenates a list of PolygonMasks into a single PolygonMasks Arguments: polymasks_list (list[PolygonMasks]) Returns: PolygonMasks: the concatenated PolygonMasks """ assert isinstance(polymasks_list, (list, tuple)) assert len(polymasks_list) > 0 assert all(isinstance(polymask, PolygonMasks) for polymask in polymasks_list) cat_polymasks = type(polymasks_list[0])( list(itertools.chain.from_iterable(pm.polygons for pm in polymasks_list)) ) return cat_polymasks class ROIMasks: """ Represent masks by N smaller masks defined in some ROIs. Once ROI boxes are given, full-image bitmask can be obtained by "pasting" the mask on the region defined by the corresponding ROI box. """ def __init__(self, tensor: torch.Tensor): """ Args: tensor: (N, M, M) mask tensor that defines the mask within each ROI. """ if tensor.dim() != 3: raise ValueError("ROIMasks must take a masks of 3 dimension.") self.tensor = tensor def to(self, device: torch.device) -> "ROIMasks": return ROIMasks(self.tensor.to(device)) @property def device(self) -> device: return self.tensor.device def __len__(self): return self.tensor.shape[0] def __getitem__(self, item) -> "ROIMasks": """ Returns: ROIMasks: Create a new :class:`ROIMasks` by indexing. The following usage are allowed: 1. `new_masks = masks[2:10]`: return a slice of masks. 2. `new_masks = masks[vector]`, where vector is a torch.BoolTensor with `length = len(masks)`. Nonzero elements in the vector will be selected. Note that the returned object might share storage with this object, subject to Pytorch's indexing semantics. """ t = self.tensor[item] if t.dim() != 3: raise ValueError( f"Indexing on ROIMasks with {item} returns a tensor with shape {t.shape}!" ) return ROIMasks(t) @torch.jit.unused def __repr__(self) -> str: s = self.__class__.__name__ + "(" s += "num_instances={})".format(len(self.tensor)) return s @torch.jit.unused def to_bitmasks(self, boxes: torch.Tensor, height, width, threshold=0.5): """ Args: see documentation of :func:`paste_masks_in_image`. """ from detectron2.layers.mask_ops import paste_masks_in_image, _paste_masks_tensor_shape if torch.jit.is_tracing(): if isinstance(height, torch.Tensor): paste_func = _paste_masks_tensor_shape else: paste_func = paste_masks_in_image else: paste_func = retry_if_cuda_oom(paste_masks_in_image) bitmasks = paste_func(self.tensor, boxes.tensor, (height, width), threshold=threshold) return BitMasks(bitmasks) ================================================ FILE: detectron2/detectron2/structures/rotated_boxes.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import math from typing import List, Tuple import torch from detectron2.layers.rotated_boxes import pairwise_iou_rotated from .boxes import Boxes class RotatedBoxes(Boxes): """ This structure stores a list of rotated boxes as a Nx5 torch.Tensor. It supports some common methods about boxes (`area`, `clip`, `nonempty`, etc), and also behaves like a Tensor (support indexing, `to(device)`, `.device`, and iteration over all boxes) """ def __init__(self, tensor: torch.Tensor): """ Args: tensor (Tensor[float]): a Nx5 matrix. Each row is (x_center, y_center, width, height, angle), in which angle is represented in degrees. While there's no strict range restriction for it, the recommended principal range is between [-180, 180) degrees. Assume we have a horizontal box B = (x_center, y_center, width, height), where width is along the x-axis and height is along the y-axis. The rotated box B_rot (x_center, y_center, width, height, angle) can be seen as: 1. When angle == 0: B_rot == B 2. When angle > 0: B_rot is obtained by rotating B w.r.t its center by :math:`|angle|` degrees CCW; 3. When angle < 0: B_rot is obtained by rotating B w.r.t its center by :math:`|angle|` degrees CW. Mathematically, since the right-handed coordinate system for image space is (y, x), where y is top->down and x is left->right, the 4 vertices of the rotated rectangle :math:`(yr_i, xr_i)` (i = 1, 2, 3, 4) can be obtained from the vertices of the horizontal rectangle :math:`(y_i, x_i)` (i = 1, 2, 3, 4) in the following way (:math:`\\theta = angle*\\pi/180` is the angle in radians, :math:`(y_c, x_c)` is the center of the rectangle): .. math:: yr_i = \\cos(\\theta) (y_i - y_c) - \\sin(\\theta) (x_i - x_c) + y_c, xr_i = \\sin(\\theta) (y_i - y_c) + \\cos(\\theta) (x_i - x_c) + x_c, which is the standard rigid-body rotation transformation. Intuitively, the angle is (1) the rotation angle from y-axis in image space to the height vector (top->down in the box's local coordinate system) of the box in CCW, and (2) the rotation angle from x-axis in image space to the width vector (left->right in the box's local coordinate system) of the box in CCW. More intuitively, consider the following horizontal box ABCD represented in (x1, y1, x2, y2): (3, 2, 7, 4), covering the [3, 7] x [2, 4] region of the continuous coordinate system which looks like this: .. code:: none O--------> x | | A---B | | | | D---C | v y Note that each capital letter represents one 0-dimensional geometric point instead of a 'square pixel' here. In the example above, using (x, y) to represent a point we have: .. math:: O = (0, 0), A = (3, 2), B = (7, 2), C = (7, 4), D = (3, 4) We name vector AB = vector DC as the width vector in box's local coordinate system, and vector AD = vector BC as the height vector in box's local coordinate system. Initially, when angle = 0 degree, they're aligned with the positive directions of x-axis and y-axis in the image space, respectively. For better illustration, we denote the center of the box as E, .. code:: none O--------> x | | A---B | | E | | D---C | v y where the center E = ((3+7)/2, (2+4)/2) = (5, 3). Also, .. math:: width = |AB| = |CD| = 7 - 3 = 4, height = |AD| = |BC| = 4 - 2 = 2. Therefore, the corresponding representation for the same shape in rotated box in (x_center, y_center, width, height, angle) format is: (5, 3, 4, 2, 0), Now, let's consider (5, 3, 4, 2, 90), which is rotated by 90 degrees CCW (counter-clockwise) by definition. It looks like this: .. code:: none O--------> x | B-C | | | | |E| | | | | A-D v y The center E is still located at the same point (5, 3), while the vertices ABCD are rotated by 90 degrees CCW with regard to E: A = (4, 5), B = (4, 1), C = (6, 1), D = (6, 5) Here, 90 degrees can be seen as the CCW angle to rotate from y-axis to vector AD or vector BC (the top->down height vector in box's local coordinate system), or the CCW angle to rotate from x-axis to vector AB or vector DC (the left->right width vector in box's local coordinate system). .. math:: width = |AB| = |CD| = 5 - 1 = 4, height = |AD| = |BC| = 6 - 4 = 2. Next, how about (5, 3, 4, 2, -90), which is rotated by 90 degrees CW (clockwise) by definition? It looks like this: .. code:: none O--------> x | D-A | | | | |E| | | | | C-B v y The center E is still located at the same point (5, 3), while the vertices ABCD are rotated by 90 degrees CW with regard to E: A = (6, 1), B = (6, 5), C = (4, 5), D = (4, 1) .. math:: width = |AB| = |CD| = 5 - 1 = 4, height = |AD| = |BC| = 6 - 4 = 2. This covers exactly the same region as (5, 3, 4, 2, 90) does, and their IoU will be 1. However, these two will generate different RoI Pooling results and should not be treated as an identical box. On the other hand, it's easy to see that (X, Y, W, H, A) is identical to (X, Y, W, H, A+360N), for any integer N. For example (5, 3, 4, 2, 270) would be identical to (5, 3, 4, 2, -90), because rotating the shape 270 degrees CCW is equivalent to rotating the same shape 90 degrees CW. We could rotate further to get (5, 3, 4, 2, 180), or (5, 3, 4, 2, -180): .. code:: none O--------> x | | C---D | | E | | B---A | v y .. math:: A = (7, 4), B = (3, 4), C = (3, 2), D = (7, 2), width = |AB| = |CD| = 7 - 3 = 4, height = |AD| = |BC| = 4 - 2 = 2. Finally, this is a very inaccurate (heavily quantized) illustration of how (5, 3, 4, 2, 60) looks like in case anyone wonders: .. code:: none O--------> x | B\ | / C | /E / | A / | `D v y It's still a rectangle with center of (5, 3), width of 4 and height of 2, but its angle (and thus orientation) is somewhere between (5, 3, 4, 2, 0) and (5, 3, 4, 2, 90). """ device = tensor.device if isinstance(tensor, torch.Tensor) else torch.device("cpu") tensor = torch.as_tensor(tensor, dtype=torch.float32, device=device) if tensor.numel() == 0: # Use reshape, so we don't end up creating a new tensor that does not depend on # the inputs (and consequently confuses jit) tensor = tensor.reshape((0, 5)).to(dtype=torch.float32, device=device) assert tensor.dim() == 2 and tensor.size(-1) == 5, tensor.size() self.tensor = tensor def clone(self) -> "RotatedBoxes": """ Clone the RotatedBoxes. Returns: RotatedBoxes """ return RotatedBoxes(self.tensor.clone()) def to(self, device: torch.device): # Boxes are assumed float32 and does not support to(dtype) return RotatedBoxes(self.tensor.to(device=device)) def area(self) -> torch.Tensor: """ Computes the area of all the boxes. Returns: torch.Tensor: a vector with areas of each box. """ box = self.tensor area = box[:, 2] * box[:, 3] return area def normalize_angles(self) -> None: """ Restrict angles to the range of [-180, 180) degrees """ self.tensor[:, 4] = (self.tensor[:, 4] + 180.0) % 360.0 - 180.0 def clip(self, box_size: Tuple[int, int], clip_angle_threshold: float = 1.0) -> None: """ Clip (in place) the boxes by limiting x coordinates to the range [0, width] and y coordinates to the range [0, height]. For RRPN: Only clip boxes that are almost horizontal with a tolerance of clip_angle_threshold to maintain backward compatibility. Rotated boxes beyond this threshold are not clipped for two reasons: 1. There are potentially multiple ways to clip a rotated box to make it fit within the image. 2. It's tricky to make the entire rectangular box fit within the image and still be able to not leave out pixels of interest. Therefore we rely on ops like RoIAlignRotated to safely handle this. Args: box_size (height, width): The clipping box's size. clip_angle_threshold: Iff. abs(normalized(angle)) <= clip_angle_threshold (in degrees), we do the clipping as horizontal boxes. """ h, w = box_size # normalize angles to be within (-180, 180] degrees self.normalize_angles() idx = torch.where(torch.abs(self.tensor[:, 4]) <= clip_angle_threshold)[0] # convert to (x1, y1, x2, y2) x1 = self.tensor[idx, 0] - self.tensor[idx, 2] / 2.0 y1 = self.tensor[idx, 1] - self.tensor[idx, 3] / 2.0 x2 = self.tensor[idx, 0] + self.tensor[idx, 2] / 2.0 y2 = self.tensor[idx, 1] + self.tensor[idx, 3] / 2.0 # clip x1.clamp_(min=0, max=w) y1.clamp_(min=0, max=h) x2.clamp_(min=0, max=w) y2.clamp_(min=0, max=h) # convert back to (xc, yc, w, h) self.tensor[idx, 0] = (x1 + x2) / 2.0 self.tensor[idx, 1] = (y1 + y2) / 2.0 # make sure widths and heights do not increase due to numerical errors self.tensor[idx, 2] = torch.min(self.tensor[idx, 2], x2 - x1) self.tensor[idx, 3] = torch.min(self.tensor[idx, 3], y2 - y1) def nonempty(self, threshold: float = 0.0) -> torch.Tensor: """ Find boxes that are non-empty. A box is considered empty, if either of its side is no larger than threshold. Returns: Tensor: a binary vector which represents whether each box is empty (False) or non-empty (True). """ box = self.tensor widths = box[:, 2] heights = box[:, 3] keep = (widths > threshold) & (heights > threshold) return keep def __getitem__(self, item) -> "RotatedBoxes": """ Returns: RotatedBoxes: Create a new :class:`RotatedBoxes` by indexing. The following usage are allowed: 1. `new_boxes = boxes[3]`: return a `RotatedBoxes` which contains only one box. 2. `new_boxes = boxes[2:10]`: return a slice of boxes. 3. `new_boxes = boxes[vector]`, where vector is a torch.ByteTensor with `length = len(boxes)`. Nonzero elements in the vector will be selected. Note that the returned RotatedBoxes might share storage with this RotatedBoxes, subject to Pytorch's indexing semantics. """ if isinstance(item, int): return RotatedBoxes(self.tensor[item].view(1, -1)) b = self.tensor[item] assert b.dim() == 2, "Indexing on RotatedBoxes with {} failed to return a matrix!".format( item ) return RotatedBoxes(b) def __len__(self) -> int: return self.tensor.shape[0] def __repr__(self) -> str: return "RotatedBoxes(" + str(self.tensor) + ")" def inside_box(self, box_size: Tuple[int, int], boundary_threshold: int = 0) -> torch.Tensor: """ Args: box_size (height, width): Size of the reference box covering [0, width] x [0, height] boundary_threshold (int): Boxes that extend beyond the reference box boundary by more than boundary_threshold are considered "outside". For RRPN, it might not be necessary to call this function since it's common for rotated box to extend to outside of the image boundaries (the clip function only clips the near-horizontal boxes) Returns: a binary vector, indicating whether each box is inside the reference box. """ height, width = box_size cnt_x = self.tensor[..., 0] cnt_y = self.tensor[..., 1] half_w = self.tensor[..., 2] / 2.0 half_h = self.tensor[..., 3] / 2.0 a = self.tensor[..., 4] c = torch.abs(torch.cos(a * math.pi / 180.0)) s = torch.abs(torch.sin(a * math.pi / 180.0)) # This basically computes the horizontal bounding rectangle of the rotated box max_rect_dx = c * half_w + s * half_h max_rect_dy = c * half_h + s * half_w inds_inside = ( (cnt_x - max_rect_dx >= -boundary_threshold) & (cnt_y - max_rect_dy >= -boundary_threshold) & (cnt_x + max_rect_dx < width + boundary_threshold) & (cnt_y + max_rect_dy < height + boundary_threshold) ) return inds_inside def get_centers(self) -> torch.Tensor: """ Returns: The box centers in a Nx2 array of (x, y). """ return self.tensor[:, :2] def scale(self, scale_x: float, scale_y: float) -> None: """ Scale the rotated box with horizontal and vertical scaling factors Note: when scale_factor_x != scale_factor_y, the rotated box does not preserve the rectangular shape when the angle is not a multiple of 90 degrees under resize transformation. Instead, the shape is a parallelogram (that has skew) Here we make an approximation by fitting a rotated rectangle to the parallelogram. """ self.tensor[:, 0] *= scale_x self.tensor[:, 1] *= scale_y theta = self.tensor[:, 4] * math.pi / 180.0 c = torch.cos(theta) s = torch.sin(theta) # In image space, y is top->down and x is left->right # Consider the local coordintate system for the rotated box, # where the box center is located at (0, 0), and the four vertices ABCD are # A(-w / 2, -h / 2), B(w / 2, -h / 2), C(w / 2, h / 2), D(-w / 2, h / 2) # the midpoint of the left edge AD of the rotated box E is: # E = (A+D)/2 = (-w / 2, 0) # the midpoint of the top edge AB of the rotated box F is: # F(0, -h / 2) # To get the old coordinates in the global system, apply the rotation transformation # (Note: the right-handed coordinate system for image space is yOx): # (old_x, old_y) = (s * y + c * x, c * y - s * x) # E(old) = (s * 0 + c * (-w/2), c * 0 - s * (-w/2)) = (-c * w / 2, s * w / 2) # F(old) = (s * (-h / 2) + c * 0, c * (-h / 2) - s * 0) = (-s * h / 2, -c * h / 2) # After applying the scaling factor (sfx, sfy): # E(new) = (-sfx * c * w / 2, sfy * s * w / 2) # F(new) = (-sfx * s * h / 2, -sfy * c * h / 2) # The new width after scaling tranformation becomes: # w(new) = |E(new) - O| * 2 # = sqrt[(sfx * c * w / 2)^2 + (sfy * s * w / 2)^2] * 2 # = sqrt[(sfx * c)^2 + (sfy * s)^2] * w # i.e., scale_factor_w = sqrt[(sfx * c)^2 + (sfy * s)^2] # # For example, # when angle = 0 or 180, |c| = 1, s = 0, scale_factor_w == scale_factor_x; # when |angle| = 90, c = 0, |s| = 1, scale_factor_w == scale_factor_y self.tensor[:, 2] *= torch.sqrt((scale_x * c) ** 2 + (scale_y * s) ** 2) # h(new) = |F(new) - O| * 2 # = sqrt[(sfx * s * h / 2)^2 + (sfy * c * h / 2)^2] * 2 # = sqrt[(sfx * s)^2 + (sfy * c)^2] * h # i.e., scale_factor_h = sqrt[(sfx * s)^2 + (sfy * c)^2] # # For example, # when angle = 0 or 180, |c| = 1, s = 0, scale_factor_h == scale_factor_y; # when |angle| = 90, c = 0, |s| = 1, scale_factor_h == scale_factor_x self.tensor[:, 3] *= torch.sqrt((scale_x * s) ** 2 + (scale_y * c) ** 2) # The angle is the rotation angle from y-axis in image space to the height # vector (top->down in the box's local coordinate system) of the box in CCW. # # angle(new) = angle_yOx(O - F(new)) # = angle_yOx( (sfx * s * h / 2, sfy * c * h / 2) ) # = atan2(sfx * s * h / 2, sfy * c * h / 2) # = atan2(sfx * s, sfy * c) # # For example, # when sfx == sfy, angle(new) == atan2(s, c) == angle(old) self.tensor[:, 4] = torch.atan2(scale_x * s, scale_y * c) * 180 / math.pi @classmethod def cat(cls, boxes_list: List["RotatedBoxes"]) -> "RotatedBoxes": """ Concatenates a list of RotatedBoxes into a single RotatedBoxes Arguments: boxes_list (list[RotatedBoxes]) Returns: RotatedBoxes: the concatenated RotatedBoxes """ assert isinstance(boxes_list, (list, tuple)) if len(boxes_list) == 0: return cls(torch.empty(0)) assert all([isinstance(box, RotatedBoxes) for box in boxes_list]) # use torch.cat (v.s. layers.cat) so the returned boxes never share storage with input cat_boxes = cls(torch.cat([b.tensor for b in boxes_list], dim=0)) return cat_boxes @property def device(self) -> torch.device: return self.tensor.device @torch.jit.unused def __iter__(self): """ Yield a box as a Tensor of shape (5,) at a time. """ yield from self.tensor def pairwise_iou(boxes1: RotatedBoxes, boxes2: RotatedBoxes) -> None: """ Given two lists of rotated boxes of size N and M, compute the IoU (intersection over union) between **all** N x M pairs of boxes. The box order must be (x_center, y_center, width, height, angle). Args: boxes1, boxes2 (RotatedBoxes): two `RotatedBoxes`. Contains N & M rotated boxes, respectively. Returns: Tensor: IoU, sized [N,M]. """ return pairwise_iou_rotated(boxes1.tensor, boxes2.tensor) ================================================ FILE: detectron2/detectron2/tracking/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .base_tracker import ( # noqa BaseTracker, build_tracker_head, TRACKER_HEADS_REGISTRY, ) from .bbox_iou_tracker import BBoxIOUTracker # noqa from .hungarian_tracker import BaseHungarianTracker # noqa from .iou_weighted_hungarian_bbox_iou_tracker import ( # noqa IOUWeightedHungarianBBoxIOUTracker, ) from .utils import create_prediction_pairs # noqa from .vanilla_hungarian_bbox_iou_tracker import VanillaHungarianBBoxIOUTracker # noqa __all__ = [k for k in globals().keys() if not k.startswith("_")] ================================================ FILE: detectron2/detectron2/tracking/base_tracker.py ================================================ #!/usr/bin/env python3 # Copyright 2004-present Facebook. All Rights Reserved. from detectron2.config import configurable from detectron2.utils.registry import Registry from ..config.config import CfgNode as CfgNode_ from ..structures import Instances TRACKER_HEADS_REGISTRY = Registry("TRACKER_HEADS") TRACKER_HEADS_REGISTRY.__doc__ = """ Registry for tracking classes. """ class BaseTracker(object): """ A parent class for all trackers """ @configurable def __init__(self, **kwargs): self._prev_instances = None # (D2)instances for previous frame self._matched_idx = set() # indices in prev_instances found matching self._matched_ID = set() # idendities in prev_instances found matching self._untracked_prev_idx = set() # indices in prev_instances not found matching self._id_count = 0 # used to assign new id @classmethod def from_config(cls, cfg: CfgNode_): raise NotImplementedError("Calling BaseTracker::from_config") def update(self, predictions: Instances) -> Instances: """ Args: predictions: D2 Instances for predictions of the current frame Return: D2 Instances for predictions of the current frame with ID assigned _prev_instances and instances will have the following fields: .pred_boxes (shape=[N, 4]) .scores (shape=[N,]) .pred_classes (shape=[N,]) .pred_keypoints (shape=[N, M, 3], Optional) .pred_masks (shape=List[2D_MASK], Optional) 2D_MASK: shape=[H, W] .ID (shape=[N,]) N: # of detected bboxes H and W: height and width of 2D mask """ raise NotImplementedError("Calling BaseTracker::update") def build_tracker_head(cfg: CfgNode_) -> BaseTracker: """ Build a tracker head from `cfg.TRACKER_HEADS.TRACKER_NAME`. Args: cfg: D2 CfgNode, config file with tracker information Return: tracker object """ name = cfg.TRACKER_HEADS.TRACKER_NAME tracker_class = TRACKER_HEADS_REGISTRY.get(name) return tracker_class(cfg) ================================================ FILE: detectron2/detectron2/tracking/bbox_iou_tracker.py ================================================ #!/usr/bin/env python3 # Copyright 2004-present Facebook. All Rights Reserved. import copy import numpy as np from typing import List import torch from detectron2.config import configurable from detectron2.structures import Boxes, Instances from detectron2.structures.boxes import pairwise_iou from ..config.config import CfgNode as CfgNode_ from .base_tracker import TRACKER_HEADS_REGISTRY, BaseTracker @TRACKER_HEADS_REGISTRY.register() class BBoxIOUTracker(BaseTracker): """ A bounding box tracker to assign ID based on IoU between current and previous instances """ @configurable def __init__( self, *, video_height: int, video_width: int, max_num_instances: int = 200, max_lost_frame_count: int = 0, min_box_rel_dim: float = 0.02, min_instance_period: int = 1, track_iou_threshold: float = 0.5, **kwargs, ): """ Args: video_height: height the video frame video_width: width of the video frame max_num_instances: maximum number of id allowed to be tracked max_lost_frame_count: maximum number of frame an id can lost tracking exceed this number, an id is considered as lost forever min_box_rel_dim: a percentage, smaller than this dimension, a bbox is removed from tracking min_instance_period: an instance will be shown after this number of period since its first showing up in the video track_iou_threshold: iou threshold, below this number a bbox pair is removed from tracking """ super().__init__(**kwargs) self._video_height = video_height self._video_width = video_width self._max_num_instances = max_num_instances self._max_lost_frame_count = max_lost_frame_count self._min_box_rel_dim = min_box_rel_dim self._min_instance_period = min_instance_period self._track_iou_threshold = track_iou_threshold @classmethod def from_config(cls, cfg: CfgNode_): """ Old style initialization using CfgNode Args: cfg: D2 CfgNode, config file Return: dictionary storing arguments for __init__ method """ assert "VIDEO_HEIGHT" in cfg.TRACKER_HEADS assert "VIDEO_WIDTH" in cfg.TRACKER_HEADS video_height = cfg.TRACKER_HEADS.get("VIDEO_HEIGHT") video_width = cfg.TRACKER_HEADS.get("VIDEO_WIDTH") max_num_instances = cfg.TRACKER_HEADS.get("MAX_NUM_INSTANCES", 200) max_lost_frame_count = cfg.TRACKER_HEADS.get("MAX_LOST_FRAME_COUNT", 0) min_box_rel_dim = cfg.TRACKER_HEADS.get("MIN_BOX_REL_DIM", 0.02) min_instance_period = cfg.TRACKER_HEADS.get("MIN_INSTANCE_PERIOD", 1) track_iou_threshold = cfg.TRACKER_HEADS.get("TRACK_IOU_THRESHOLD", 0.5) return { "_target_": "detectron2.tracking.bbox_iou_tracker.BBoxIOUTracker", "video_height": video_height, "video_width": video_width, "max_num_instances": max_num_instances, "max_lost_frame_count": max_lost_frame_count, "min_box_rel_dim": min_box_rel_dim, "min_instance_period": min_instance_period, "track_iou_threshold": track_iou_threshold, } def update(self, instances: Instances) -> Instances: """ See BaseTracker description """ instances = self._initialize_extra_fields(instances) if self._prev_instances is not None: # calculate IoU of all bbox pairs iou_all = pairwise_iou( boxes1=instances.pred_boxes, boxes2=self._prev_instances.pred_boxes, ) # sort IoU in descending order bbox_pairs = self._create_prediction_pairs(instances, iou_all) # assign previous ID to current bbox if IoU > track_iou_threshold self._reset_fields() for bbox_pair in bbox_pairs: idx = bbox_pair["idx"] prev_id = bbox_pair["prev_id"] if ( idx in self._matched_idx or prev_id in self._matched_ID or bbox_pair["IoU"] < self._track_iou_threshold ): continue instances.ID[idx] = prev_id instances.ID_period[idx] = bbox_pair["prev_period"] + 1 instances.lost_frame_count[idx] = 0 self._matched_idx.add(idx) self._matched_ID.add(prev_id) self._untracked_prev_idx.remove(bbox_pair["prev_idx"]) instances = self._assign_new_id(instances) instances = self._merge_untracked_instances(instances) self._prev_instances = copy.deepcopy(instances) return instances def _create_prediction_pairs(self, instances: Instances, iou_all: np.ndarray) -> List: """ For all instances in previous and current frames, create pairs. For each pair, store index of the instance in current frame predcitions, index in previous predictions, ID in previous predictions, IoU of the bboxes in this pair, period in previous predictions. Args: instances: D2 Instances, for predictions of the current frame iou_all: IoU for all bboxes pairs Return: A list of IoU for all pairs """ bbox_pairs = [] for i in range(len(instances)): for j in range(len(self._prev_instances)): bbox_pairs.append( { "idx": i, "prev_idx": j, "prev_id": self._prev_instances.ID[j], "IoU": iou_all[i, j], "prev_period": self._prev_instances.ID_period[j], } ) return bbox_pairs def _initialize_extra_fields(self, instances: Instances) -> Instances: """ If input instances don't have ID, ID_period, lost_frame_count fields, this method is used to initialize these fields. Args: instances: D2 Instances, for predictions of the current frame Return: D2 Instances with extra fields added """ if not instances.has("ID"): instances.set("ID", [None] * len(instances)) if not instances.has("ID_period"): instances.set("ID_period", [None] * len(instances)) if not instances.has("lost_frame_count"): instances.set("lost_frame_count", [None] * len(instances)) if self._prev_instances is None: instances.ID = list(range(len(instances))) self._id_count += len(instances) instances.ID_period = [1] * len(instances) instances.lost_frame_count = [0] * len(instances) return instances def _reset_fields(self): """ Before each uodate call, reset fields first """ self._matched_idx = set() self._matched_ID = set() self._untracked_prev_idx = set(range(len(self._prev_instances))) def _assign_new_id(self, instances: Instances) -> Instances: """ For each untracked instance, assign a new id Args: instances: D2 Instances, for predictions of the current frame Return: D2 Instances with new ID assigned """ untracked_idx = set(range(len(instances))).difference(self._matched_idx) for idx in untracked_idx: instances.ID[idx] = self._id_count self._id_count += 1 instances.ID_period[idx] = 1 instances.lost_frame_count[idx] = 0 return instances def _merge_untracked_instances(self, instances: Instances) -> Instances: """ For untracked previous instances, under certain condition, still keep them in tracking and merge with the current instances. Args: instances: D2 Instances, for predictions of the current frame Return: D2 Instances merging current instances and instances from previous frame decided to keep tracking """ untracked_instances = Instances( image_size=instances.image_size, pred_boxes=[], pred_classes=[], scores=[], ID=[], ID_period=[], lost_frame_count=[], ) prev_bboxes = list(self._prev_instances.pred_boxes) prev_classes = list(self._prev_instances.pred_classes) prev_scores = list(self._prev_instances.scores) prev_ID_period = self._prev_instances.ID_period if instances.has("pred_masks"): untracked_instances.set("pred_masks", []) prev_masks = list(self._prev_instances.pred_masks) if instances.has("pred_keypoints"): untracked_instances.set("pred_keypoints", []) prev_keypoints = list(self._prev_instances.pred_keypoints) if instances.has("pred_keypoint_heatmaps"): untracked_instances.set("pred_keypoint_heatmaps", []) prev_keypoint_heatmaps = list(self._prev_instances.pred_keypoint_heatmaps) for idx in self._untracked_prev_idx: x_left, y_top, x_right, y_bot = prev_bboxes[idx] if ( (1.0 * (x_right - x_left) / self._video_width < self._min_box_rel_dim) or (1.0 * (y_bot - y_top) / self._video_height < self._min_box_rel_dim) or self._prev_instances.lost_frame_count[idx] >= self._max_lost_frame_count or prev_ID_period[idx] <= self._min_instance_period ): continue untracked_instances.pred_boxes.append(list(prev_bboxes[idx].numpy())) untracked_instances.pred_classes.append(int(prev_classes[idx])) untracked_instances.scores.append(float(prev_scores[idx])) untracked_instances.ID.append(self._prev_instances.ID[idx]) untracked_instances.ID_period.append(self._prev_instances.ID_period[idx]) untracked_instances.lost_frame_count.append( self._prev_instances.lost_frame_count[idx] + 1 ) if instances.has("pred_masks"): untracked_instances.pred_masks.append(prev_masks[idx].numpy().astype(np.uint8)) if instances.has("pred_keypoints"): untracked_instances.pred_keypoints.append( prev_keypoints[idx].numpy().astype(np.uint8) ) if instances.has("pred_keypoint_heatmaps"): untracked_instances.pred_keypoint_heatmaps.append( prev_keypoint_heatmaps[idx].numpy().astype(np.float32) ) untracked_instances.pred_boxes = Boxes(torch.FloatTensor(untracked_instances.pred_boxes)) untracked_instances.pred_classes = torch.IntTensor(untracked_instances.pred_classes) untracked_instances.scores = torch.FloatTensor(untracked_instances.scores) if instances.has("pred_masks"): untracked_instances.pred_masks = torch.IntTensor(untracked_instances.pred_masks) if instances.has("pred_keypoints"): untracked_instances.pred_keypoints = torch.IntTensor(untracked_instances.pred_keypoints) if instances.has("pred_keypoint_heatmaps"): untracked_instances.pred_keypoint_heatmaps = torch.FloatTensor( untracked_instances.pred_keypoint_heatmaps ) return Instances.cat( [ instances, untracked_instances, ] ) ================================================ FILE: detectron2/detectron2/tracking/hungarian_tracker.py ================================================ #!/usr/bin/env python3 # Copyright 2004-present Facebook. All Rights Reserved. import copy import numpy as np from typing import Dict import torch from scipy.optimize import linear_sum_assignment from detectron2.config import configurable from detectron2.structures import Boxes, Instances from ..config.config import CfgNode as CfgNode_ from .base_tracker import BaseTracker class BaseHungarianTracker(BaseTracker): """ A base class for all Hungarian trackers """ @configurable def __init__( self, video_height: int, video_width: int, max_num_instances: int = 200, max_lost_frame_count: int = 0, min_box_rel_dim: float = 0.02, min_instance_period: int = 1, **kwargs ): """ Args: video_height: height the video frame video_width: width of the video frame max_num_instances: maximum number of id allowed to be tracked max_lost_frame_count: maximum number of frame an id can lost tracking exceed this number, an id is considered as lost forever min_box_rel_dim: a percentage, smaller than this dimension, a bbox is removed from tracking min_instance_period: an instance will be shown after this number of period since its first showing up in the video """ super().__init__(**kwargs) self._video_height = video_height self._video_width = video_width self._max_num_instances = max_num_instances self._max_lost_frame_count = max_lost_frame_count self._min_box_rel_dim = min_box_rel_dim self._min_instance_period = min_instance_period @classmethod def from_config(cls, cfg: CfgNode_) -> Dict: raise NotImplementedError("Calling HungarianTracker::from_config") def build_cost_matrix(self, instances: Instances, prev_instances: Instances) -> np.ndarray: raise NotImplementedError("Calling HungarianTracker::build_matrix") def update(self, instances: Instances) -> Instances: if instances.has("pred_keypoints"): raise NotImplementedError("Need to add support for keypoints") instances = self._initialize_extra_fields(instances) if self._prev_instances is not None: self._untracked_prev_idx = set(range(len(self._prev_instances))) cost_matrix = self.build_cost_matrix(instances, self._prev_instances) matched_idx, matched_prev_idx = linear_sum_assignment(cost_matrix) instances = self._process_matched_idx(instances, matched_idx, matched_prev_idx) instances = self._process_unmatched_idx(instances, matched_idx) instances = self._process_unmatched_prev_idx(instances, matched_prev_idx) self._prev_instances = copy.deepcopy(instances) return instances def _initialize_extra_fields(self, instances: Instances) -> Instances: """ If input instances don't have ID, ID_period, lost_frame_count fields, this method is used to initialize these fields. Args: instances: D2 Instances, for predictions of the current frame Return: D2 Instances with extra fields added """ if not instances.has("ID"): instances.set("ID", [None] * len(instances)) if not instances.has("ID_period"): instances.set("ID_period", [None] * len(instances)) if not instances.has("lost_frame_count"): instances.set("lost_frame_count", [None] * len(instances)) if self._prev_instances is None: instances.ID = list(range(len(instances))) self._id_count += len(instances) instances.ID_period = [1] * len(instances) instances.lost_frame_count = [0] * len(instances) return instances def _process_matched_idx( self, instances: Instances, matched_idx: np.ndarray, matched_prev_idx: np.ndarray ) -> Instances: assert matched_idx.size == matched_prev_idx.size for i in range(matched_idx.size): instances.ID[matched_idx[i]] = self._prev_instances.ID[matched_prev_idx[i]] instances.ID_period[matched_idx[i]] = ( self._prev_instances.ID_period[matched_prev_idx[i]] + 1 ) instances.lost_frame_count[matched_idx[i]] = 0 return instances def _process_unmatched_idx(self, instances: Instances, matched_idx: np.ndarray) -> Instances: untracked_idx = set(range(len(instances))).difference(set(matched_idx)) for idx in untracked_idx: instances.ID[idx] = self._id_count self._id_count += 1 instances.ID_period[idx] = 1 instances.lost_frame_count[idx] = 0 return instances def _process_unmatched_prev_idx( self, instances: Instances, matched_prev_idx: np.ndarray ) -> Instances: untracked_instances = Instances( image_size=instances.image_size, pred_boxes=[], pred_masks=[], pred_classes=[], scores=[], ID=[], ID_period=[], lost_frame_count=[], ) prev_bboxes = list(self._prev_instances.pred_boxes) prev_classes = list(self._prev_instances.pred_classes) prev_scores = list(self._prev_instances.scores) prev_ID_period = self._prev_instances.ID_period if instances.has("pred_masks"): prev_masks = list(self._prev_instances.pred_masks) untracked_prev_idx = set(range(len(self._prev_instances))).difference(set(matched_prev_idx)) for idx in untracked_prev_idx: x_left, y_top, x_right, y_bot = prev_bboxes[idx] if ( (1.0 * (x_right - x_left) / self._video_width < self._min_box_rel_dim) or (1.0 * (y_bot - y_top) / self._video_height < self._min_box_rel_dim) or self._prev_instances.lost_frame_count[idx] >= self._max_lost_frame_count or prev_ID_period[idx] <= self._min_instance_period ): continue untracked_instances.pred_boxes.append(list(prev_bboxes[idx].numpy())) untracked_instances.pred_classes.append(int(prev_classes[idx])) untracked_instances.scores.append(float(prev_scores[idx])) untracked_instances.ID.append(self._prev_instances.ID[idx]) untracked_instances.ID_period.append(self._prev_instances.ID_period[idx]) untracked_instances.lost_frame_count.append( self._prev_instances.lost_frame_count[idx] + 1 ) if instances.has("pred_masks"): untracked_instances.pred_masks.append(prev_masks[idx].numpy().astype(np.uint8)) untracked_instances.pred_boxes = Boxes(torch.FloatTensor(untracked_instances.pred_boxes)) untracked_instances.pred_classes = torch.IntTensor(untracked_instances.pred_classes) untracked_instances.scores = torch.FloatTensor(untracked_instances.scores) if instances.has("pred_masks"): untracked_instances.pred_masks = torch.IntTensor(untracked_instances.pred_masks) else: untracked_instances.remove("pred_masks") return Instances.cat( [ instances, untracked_instances, ] ) ================================================ FILE: detectron2/detectron2/tracking/iou_weighted_hungarian_bbox_iou_tracker.py ================================================ #!/usr/bin/env python3 # Copyright 2004-present Facebook. All Rights Reserved. import numpy as np from typing import List from detectron2.config import CfgNode as CfgNode_ from detectron2.config import configurable from .base_tracker import TRACKER_HEADS_REGISTRY from .vanilla_hungarian_bbox_iou_tracker import VanillaHungarianBBoxIOUTracker @TRACKER_HEADS_REGISTRY.register() class IOUWeightedHungarianBBoxIOUTracker(VanillaHungarianBBoxIOUTracker): """ A tracker using IoU as weight in Hungarian algorithm, also known as Munkres or Kuhn-Munkres algorithm """ @configurable def __init__( self, *, video_height: int, video_width: int, max_num_instances: int = 200, max_lost_frame_count: int = 0, min_box_rel_dim: float = 0.02, min_instance_period: int = 1, track_iou_threshold: float = 0.5, **kwargs, ): """ Args: video_height: height the video frame video_width: width of the video frame max_num_instances: maximum number of id allowed to be tracked max_lost_frame_count: maximum number of frame an id can lost tracking exceed this number, an id is considered as lost forever min_box_rel_dim: a percentage, smaller than this dimension, a bbox is removed from tracking min_instance_period: an instance will be shown after this number of period since its first showing up in the video track_iou_threshold: iou threshold, below this number a bbox pair is removed from tracking """ super().__init__( video_height=video_height, video_width=video_width, max_num_instances=max_num_instances, max_lost_frame_count=max_lost_frame_count, min_box_rel_dim=min_box_rel_dim, min_instance_period=min_instance_period, track_iou_threshold=track_iou_threshold, ) @classmethod def from_config(cls, cfg: CfgNode_): """ Old style initialization using CfgNode Args: cfg: D2 CfgNode, config file Return: dictionary storing arguments for __init__ method """ assert "VIDEO_HEIGHT" in cfg.TRACKER_HEADS assert "VIDEO_WIDTH" in cfg.TRACKER_HEADS video_height = cfg.TRACKER_HEADS.get("VIDEO_HEIGHT") video_width = cfg.TRACKER_HEADS.get("VIDEO_WIDTH") max_num_instances = cfg.TRACKER_HEADS.get("MAX_NUM_INSTANCES", 200) max_lost_frame_count = cfg.TRACKER_HEADS.get("MAX_LOST_FRAME_COUNT", 0) min_box_rel_dim = cfg.TRACKER_HEADS.get("MIN_BOX_REL_DIM", 0.02) min_instance_period = cfg.TRACKER_HEADS.get("MIN_INSTANCE_PERIOD", 1) track_iou_threshold = cfg.TRACKER_HEADS.get("TRACK_IOU_THRESHOLD", 0.5) return { "_target_": "detectron2.tracking.iou_weighted_hungarian_bbox_iou_tracker.IOUWeightedHungarianBBoxIOUTracker", # noqa "video_height": video_height, "video_width": video_width, "max_num_instances": max_num_instances, "max_lost_frame_count": max_lost_frame_count, "min_box_rel_dim": min_box_rel_dim, "min_instance_period": min_instance_period, "track_iou_threshold": track_iou_threshold, } def assign_cost_matrix_values(self, cost_matrix: np.ndarray, bbox_pairs: List) -> np.ndarray: """ Based on IoU for each pair of bbox, assign the associated value in cost matrix Args: cost_matrix: np.ndarray, initialized 2D array with target dimensions bbox_pairs: list of bbox pair, in each pair, iou value is stored Return: np.ndarray, cost_matrix with assigned values """ for pair in bbox_pairs: # assign (-1 * IoU) for above threshold pairs, algorithms will minimize cost cost_matrix[pair["idx"]][pair["prev_idx"]] = -1 * pair["IoU"] return cost_matrix ================================================ FILE: detectron2/detectron2/tracking/utils.py ================================================ #!/usr/bin/env python3 import numpy as np from typing import List from detectron2.structures import Instances def create_prediction_pairs( instances: Instances, prev_instances: Instances, iou_all: np.ndarray, threshold: float = 0.5, ) -> List: """ Args: instances: predictions from current frame prev_instances: predictions from previous frame iou_all: 2D numpy array containing iou for each bbox pair threshold: below the threshold, doesn't consider the pair of bbox is valid Return: List of bbox pairs """ bbox_pairs = [] for i in range(len(instances)): for j in range(len(prev_instances)): if iou_all[i, j] < threshold: continue bbox_pairs.append( { "idx": i, "prev_idx": j, "prev_id": prev_instances.ID[j], "IoU": iou_all[i, j], "prev_period": prev_instances.ID_period[j], } ) return bbox_pairs LARGE_COST_VALUE = 100000 ================================================ FILE: detectron2/detectron2/tracking/vanilla_hungarian_bbox_iou_tracker.py ================================================ #!/usr/bin/env python3 # Copyright 2004-present Facebook. All Rights Reserved. import numpy as np from typing import List from detectron2.config import CfgNode as CfgNode_ from detectron2.config import configurable from detectron2.structures import Instances from detectron2.structures.boxes import pairwise_iou from detectron2.tracking.utils import LARGE_COST_VALUE, create_prediction_pairs from .base_tracker import TRACKER_HEADS_REGISTRY from .hungarian_tracker import BaseHungarianTracker @TRACKER_HEADS_REGISTRY.register() class VanillaHungarianBBoxIOUTracker(BaseHungarianTracker): """ Hungarian algo based tracker using bbox iou as metric """ @configurable def __init__( self, *, video_height: int, video_width: int, max_num_instances: int = 200, max_lost_frame_count: int = 0, min_box_rel_dim: float = 0.02, min_instance_period: int = 1, track_iou_threshold: float = 0.5, **kwargs, ): """ Args: video_height: height the video frame video_width: width of the video frame max_num_instances: maximum number of id allowed to be tracked max_lost_frame_count: maximum number of frame an id can lost tracking exceed this number, an id is considered as lost forever min_box_rel_dim: a percentage, smaller than this dimension, a bbox is removed from tracking min_instance_period: an instance will be shown after this number of period since its first showing up in the video track_iou_threshold: iou threshold, below this number a bbox pair is removed from tracking """ super().__init__( video_height=video_height, video_width=video_width, max_num_instances=max_num_instances, max_lost_frame_count=max_lost_frame_count, min_box_rel_dim=min_box_rel_dim, min_instance_period=min_instance_period, ) self._track_iou_threshold = track_iou_threshold @classmethod def from_config(cls, cfg: CfgNode_): """ Old style initialization using CfgNode Args: cfg: D2 CfgNode, config file Return: dictionary storing arguments for __init__ method """ assert "VIDEO_HEIGHT" in cfg.TRACKER_HEADS assert "VIDEO_WIDTH" in cfg.TRACKER_HEADS video_height = cfg.TRACKER_HEADS.get("VIDEO_HEIGHT") video_width = cfg.TRACKER_HEADS.get("VIDEO_WIDTH") max_num_instances = cfg.TRACKER_HEADS.get("MAX_NUM_INSTANCES", 200) max_lost_frame_count = cfg.TRACKER_HEADS.get("MAX_LOST_FRAME_COUNT", 0) min_box_rel_dim = cfg.TRACKER_HEADS.get("MIN_BOX_REL_DIM", 0.02) min_instance_period = cfg.TRACKER_HEADS.get("MIN_INSTANCE_PERIOD", 1) track_iou_threshold = cfg.TRACKER_HEADS.get("TRACK_IOU_THRESHOLD", 0.5) return { "_target_": "detectron2.tracking.vanilla_hungarian_bbox_iou_tracker.VanillaHungarianBBoxIOUTracker", # noqa "video_height": video_height, "video_width": video_width, "max_num_instances": max_num_instances, "max_lost_frame_count": max_lost_frame_count, "min_box_rel_dim": min_box_rel_dim, "min_instance_period": min_instance_period, "track_iou_threshold": track_iou_threshold, } def build_cost_matrix(self, instances: Instances, prev_instances: Instances) -> np.ndarray: """ Build the cost matrix for assignment problem (https://en.wikipedia.org/wiki/Assignment_problem) Args: instances: D2 Instances, for current frame predictions prev_instances: D2 Instances, for previous frame predictions Return: the cost matrix in numpy array """ assert instances is not None and prev_instances is not None # calculate IoU of all bbox pairs iou_all = pairwise_iou( boxes1=instances.pred_boxes, boxes2=self._prev_instances.pred_boxes, ) bbox_pairs = create_prediction_pairs( instances, self._prev_instances, iou_all, threshold=self._track_iou_threshold ) # assign large cost value to make sure pair below IoU threshold won't be matched cost_matrix = np.full((len(instances), len(prev_instances)), LARGE_COST_VALUE) return self.assign_cost_matrix_values(cost_matrix, bbox_pairs) def assign_cost_matrix_values(self, cost_matrix: np.ndarray, bbox_pairs: List) -> np.ndarray: """ Based on IoU for each pair of bbox, assign the associated value in cost matrix Args: cost_matrix: np.ndarray, initialized 2D array with target dimensions bbox_pairs: list of bbox pair, in each pair, iou value is stored Return: np.ndarray, cost_matrix with assigned values """ for pair in bbox_pairs: # assign -1 for IoU above threshold pairs, algorithms will minimize cost cost_matrix[pair["idx"]][pair["prev_idx"]] = -1 return cost_matrix ================================================ FILE: detectron2/detectron2/utils/README.md ================================================ # Utility functions This folder contain utility functions that are not used in the core library, but are useful for building models or training code using the config system. ================================================ FILE: detectron2/detectron2/utils/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. ================================================ FILE: detectron2/detectron2/utils/analysis.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # -*- coding: utf-8 -*- import typing from typing import Any, List import fvcore from fvcore.nn import activation_count, flop_count, parameter_count, parameter_count_table from torch import nn from detectron2.export import TracingAdapter __all__ = [ "activation_count_operators", "flop_count_operators", "parameter_count_table", "parameter_count", "FlopCountAnalysis", ] FLOPS_MODE = "flops" ACTIVATIONS_MODE = "activations" # Some extra ops to ignore from counting, including elementwise and reduction ops _IGNORED_OPS = { "aten::add", "aten::add_", "aten::argmax", "aten::argsort", "aten::batch_norm", "aten::constant_pad_nd", "aten::div", "aten::div_", "aten::exp", "aten::log2", "aten::max_pool2d", "aten::meshgrid", "aten::mul", "aten::mul_", "aten::neg", "aten::nonzero_numpy", "aten::reciprocal", "aten::repeat_interleave", "aten::rsub", "aten::sigmoid", "aten::sigmoid_", "aten::softmax", "aten::sort", "aten::sqrt", "aten::sub", "torchvision::nms", # TODO estimate flop for nms } class FlopCountAnalysis(fvcore.nn.FlopCountAnalysis): """ Same as :class:`fvcore.nn.FlopCountAnalysis`, but supports detectron2 models. """ def __init__(self, model, inputs): """ Args: model (nn.Module): inputs (Any): inputs of the given model. Does not have to be tuple of tensors. """ wrapper = TracingAdapter(model, inputs, allow_non_tensor=True) super().__init__(wrapper, wrapper.flattened_inputs) self.set_op_handle(**{k: None for k in _IGNORED_OPS}) def flop_count_operators(model: nn.Module, inputs: list) -> typing.DefaultDict[str, float]: """ Implement operator-level flops counting using jit. This is a wrapper of :func:`fvcore.nn.flop_count` and adds supports for standard detection models in detectron2. Please use :class:`FlopCountAnalysis` for more advanced functionalities. Note: The function runs the input through the model to compute flops. The flops of a detection model is often input-dependent, for example, the flops of box & mask head depends on the number of proposals & the number of detected objects. Therefore, the flops counting using a single input may not accurately reflect the computation cost of a model. It's recommended to average across a number of inputs. Args: model: a detectron2 model that takes `list[dict]` as input. inputs (list[dict]): inputs to model, in detectron2's standard format. Only "image" key will be used. supported_ops (dict[str, Handle]): see documentation of :func:`fvcore.nn.flop_count` Returns: Counter: Gflop count per operator """ old_train = model.training model.eval() ret = FlopCountAnalysis(model, inputs).by_operator() model.train(old_train) return {k: v / 1e9 for k, v in ret.items()} def activation_count_operators( model: nn.Module, inputs: list, **kwargs ) -> typing.DefaultDict[str, float]: """ Implement operator-level activations counting using jit. This is a wrapper of fvcore.nn.activation_count, that supports standard detection models in detectron2. Note: The function runs the input through the model to compute activations. The activations of a detection model is often input-dependent, for example, the activations of box & mask head depends on the number of proposals & the number of detected objects. Args: model: a detectron2 model that takes `list[dict]` as input. inputs (list[dict]): inputs to model, in detectron2's standard format. Only "image" key will be used. Returns: Counter: activation count per operator """ return _wrapper_count_operators(model=model, inputs=inputs, mode=ACTIVATIONS_MODE, **kwargs) def _wrapper_count_operators( model: nn.Module, inputs: list, mode: str, **kwargs ) -> typing.DefaultDict[str, float]: # ignore some ops supported_ops = {k: lambda *args, **kwargs: {} for k in _IGNORED_OPS} supported_ops.update(kwargs.pop("supported_ops", {})) kwargs["supported_ops"] = supported_ops assert len(inputs) == 1, "Please use batch size=1" tensor_input = inputs[0]["image"] inputs = [{"image": tensor_input}] # remove other keys, in case there are any old_train = model.training if isinstance(model, (nn.parallel.distributed.DistributedDataParallel, nn.DataParallel)): model = model.module wrapper = TracingAdapter(model, inputs) wrapper.eval() if mode == FLOPS_MODE: ret = flop_count(wrapper, (tensor_input,), **kwargs) elif mode == ACTIVATIONS_MODE: ret = activation_count(wrapper, (tensor_input,), **kwargs) else: raise NotImplementedError("Count for mode {} is not supported yet.".format(mode)) # compatible with change in fvcore if isinstance(ret, tuple): ret = ret[0] model.train(old_train) return ret def find_unused_parameters(model: nn.Module, inputs: Any) -> List[str]: """ Given a model, find parameters that do not contribute to the loss. Args: model: a model in training mode that returns losses inputs: argument or a tuple of arguments. Inputs of the model Returns: list[str]: the name of unused parameters """ assert model.training for _, prm in model.named_parameters(): prm.grad = None if isinstance(inputs, tuple): losses = model(*inputs) else: losses = model(inputs) if isinstance(losses, dict): losses = sum(losses.values()) losses.backward() unused: List[str] = [] for name, prm in model.named_parameters(): if prm.grad is None: unused.append(name) prm.grad = None return unused ================================================ FILE: detectron2/detectron2/utils/collect_env.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import importlib import numpy as np import os import re import subprocess import sys from collections import defaultdict import PIL import torch import torchvision from tabulate import tabulate __all__ = ["collect_env_info"] def collect_torch_env(): try: import torch.__config__ return torch.__config__.show() except ImportError: # compatible with older versions of pytorch from torch.utils.collect_env import get_pretty_env_info return get_pretty_env_info() def get_env_module(): var_name = "DETECTRON2_ENV_MODULE" return var_name, os.environ.get(var_name, "") def detect_compute_compatibility(CUDA_HOME, so_file): try: cuobjdump = os.path.join(CUDA_HOME, "bin", "cuobjdump") if os.path.isfile(cuobjdump): output = subprocess.check_output( "'{}' --list-elf '{}'".format(cuobjdump, so_file), shell=True ) output = output.decode("utf-8").strip().split("\n") arch = [] for line in output: line = re.findall(r"\.sm_([0-9]*)\.", line)[0] arch.append(".".join(line)) arch = sorted(set(arch)) return ", ".join(arch) else: return so_file + "; cannot find cuobjdump" except Exception: # unhandled failure return so_file def collect_env_info(): has_gpu = torch.cuda.is_available() # true for both CUDA & ROCM torch_version = torch.__version__ # NOTE that CUDA_HOME/ROCM_HOME could be None even when CUDA runtime libs are functional from torch.utils.cpp_extension import CUDA_HOME, ROCM_HOME has_rocm = False if (getattr(torch.version, "hip", None) is not None) and (ROCM_HOME is not None): has_rocm = True has_cuda = has_gpu and (not has_rocm) data = [] data.append(("sys.platform", sys.platform)) # check-template.yml depends on it data.append(("Python", sys.version.replace("\n", ""))) data.append(("numpy", np.__version__)) try: import detectron2 # noqa data.append( ("detectron2", detectron2.__version__ + " @" + os.path.dirname(detectron2.__file__)) ) except ImportError: data.append(("detectron2", "failed to import")) except AttributeError: data.append(("detectron2", "imported a wrong installation")) try: import detectron2._C as _C except ImportError as e: data.append(("detectron2._C", f"not built correctly: {e}")) # print system compilers when extension fails to build if sys.platform != "win32": # don't know what to do for windows try: # this is how torch/utils/cpp_extensions.py choose compiler cxx = os.environ.get("CXX", "c++") cxx = subprocess.check_output("'{}' --version".format(cxx), shell=True) cxx = cxx.decode("utf-8").strip().split("\n")[0] except subprocess.SubprocessError: cxx = "Not found" data.append(("Compiler ($CXX)", cxx)) if has_cuda and CUDA_HOME is not None: try: nvcc = os.path.join(CUDA_HOME, "bin", "nvcc") nvcc = subprocess.check_output("'{}' -V".format(nvcc), shell=True) nvcc = nvcc.decode("utf-8").strip().split("\n")[-1] except subprocess.SubprocessError: nvcc = "Not found" data.append(("CUDA compiler", nvcc)) if has_cuda and sys.platform != "win32": try: so_file = importlib.util.find_spec("detectron2._C").origin except (ImportError, AttributeError): pass else: data.append( ("detectron2 arch flags", detect_compute_compatibility(CUDA_HOME, so_file)) ) else: # print compilers that are used to build extension data.append(("Compiler", _C.get_compiler_version())) data.append(("CUDA compiler", _C.get_cuda_version())) # cuda or hip if has_cuda and getattr(_C, "has_cuda", lambda: True)(): data.append( ("detectron2 arch flags", detect_compute_compatibility(CUDA_HOME, _C.__file__)) ) data.append(get_env_module()) data.append(("PyTorch", torch_version + " @" + os.path.dirname(torch.__file__))) data.append(("PyTorch debug build", torch.version.debug)) if not has_gpu: has_gpu_text = "No: torch.cuda.is_available() == False" else: has_gpu_text = "Yes" data.append(("GPU available", has_gpu_text)) if has_gpu: devices = defaultdict(list) for k in range(torch.cuda.device_count()): cap = ".".join((str(x) for x in torch.cuda.get_device_capability(k))) name = torch.cuda.get_device_name(k) + f" (arch={cap})" devices[name].append(str(k)) for name, devids in devices.items(): data.append(("GPU " + ",".join(devids), name)) if has_rocm: msg = " - invalid!" if not (ROCM_HOME and os.path.isdir(ROCM_HOME)) else "" data.append(("ROCM_HOME", str(ROCM_HOME) + msg)) else: try: from torch.utils.collect_env import get_nvidia_driver_version, run as _run data.append(("Driver version", get_nvidia_driver_version(_run))) except Exception: pass msg = " - invalid!" if not (CUDA_HOME and os.path.isdir(CUDA_HOME)) else "" data.append(("CUDA_HOME", str(CUDA_HOME) + msg)) cuda_arch_list = os.environ.get("TORCH_CUDA_ARCH_LIST", None) if cuda_arch_list: data.append(("TORCH_CUDA_ARCH_LIST", cuda_arch_list)) data.append(("Pillow", PIL.__version__)) try: data.append( ( "torchvision", str(torchvision.__version__) + " @" + os.path.dirname(torchvision.__file__), ) ) if has_cuda: try: torchvision_C = importlib.util.find_spec("torchvision._C").origin msg = detect_compute_compatibility(CUDA_HOME, torchvision_C) data.append(("torchvision arch flags", msg)) except (ImportError, AttributeError): data.append(("torchvision._C", "Not found")) except AttributeError: data.append(("torchvision", "unknown")) try: import fvcore data.append(("fvcore", fvcore.__version__)) except (ImportError, AttributeError): pass try: import iopath data.append(("iopath", iopath.__version__)) except (ImportError, AttributeError): pass try: import cv2 data.append(("cv2", cv2.__version__)) except (ImportError, AttributeError): data.append(("cv2", "Not found")) env_str = tabulate(data) + "\n" env_str += collect_torch_env() return env_str def test_nccl_ops(): num_gpu = torch.cuda.device_count() if os.access("/tmp", os.W_OK): import torch.multiprocessing as mp dist_url = "file:///tmp/nccl_tmp_file" print("Testing NCCL connectivity ... this should not hang.") mp.spawn(_test_nccl_worker, nprocs=num_gpu, args=(num_gpu, dist_url), daemon=False) print("NCCL succeeded.") def _test_nccl_worker(rank, num_gpu, dist_url): import torch.distributed as dist dist.init_process_group(backend="NCCL", init_method=dist_url, rank=rank, world_size=num_gpu) dist.barrier(device_ids=[rank]) if __name__ == "__main__": try: from detectron2.utils.collect_env import collect_env_info as f print(f()) except ImportError: print(collect_env_info()) if torch.cuda.is_available(): num_gpu = torch.cuda.device_count() for k in range(num_gpu): device = f"cuda:{k}" try: x = torch.tensor([1, 2.0], dtype=torch.float32) x = x.to(device) except Exception as e: print( f"Unable to copy tensor to device={device}: {e}. " "Your CUDA environment is broken." ) if num_gpu > 1: test_nccl_ops() ================================================ FILE: detectron2/detectron2/utils/colormap.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. """ An awesome colormap for really neat visualizations. Copied from Detectron, and removed gray colors. """ import numpy as np import random __all__ = ["colormap", "random_color", "random_colors"] # fmt: off # RGB: _COLORS = np.array( [ 0.000, 0.447, 0.741, 0.850, 0.325, 0.098, 0.929, 0.694, 0.125, 0.494, 0.184, 0.556, 0.466, 0.674, 0.188, 0.301, 0.745, 0.933, 0.635, 0.078, 0.184, 0.300, 0.300, 0.300, 0.600, 0.600, 0.600, 1.000, 0.000, 0.000, 1.000, 0.500, 0.000, 0.749, 0.749, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 1.000, 0.667, 0.000, 1.000, 0.333, 0.333, 0.000, 0.333, 0.667, 0.000, 0.333, 1.000, 0.000, 0.667, 0.333, 0.000, 0.667, 0.667, 0.000, 0.667, 1.000, 0.000, 1.000, 0.333, 0.000, 1.000, 0.667, 0.000, 1.000, 1.000, 0.000, 0.000, 0.333, 0.500, 0.000, 0.667, 0.500, 0.000, 1.000, 0.500, 0.333, 0.000, 0.500, 0.333, 0.333, 0.500, 0.333, 0.667, 0.500, 0.333, 1.000, 0.500, 0.667, 0.000, 0.500, 0.667, 0.333, 0.500, 0.667, 0.667, 0.500, 0.667, 1.000, 0.500, 1.000, 0.000, 0.500, 1.000, 0.333, 0.500, 1.000, 0.667, 0.500, 1.000, 1.000, 0.500, 0.000, 0.333, 1.000, 0.000, 0.667, 1.000, 0.000, 1.000, 1.000, 0.333, 0.000, 1.000, 0.333, 0.333, 1.000, 0.333, 0.667, 1.000, 0.333, 1.000, 1.000, 0.667, 0.000, 1.000, 0.667, 0.333, 1.000, 0.667, 0.667, 1.000, 0.667, 1.000, 1.000, 1.000, 0.000, 1.000, 1.000, 0.333, 1.000, 1.000, 0.667, 1.000, 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, 0.000, 0.833, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.167, 0.000, 0.000, 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, 0.000, 0.833, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.167, 0.000, 0.000, 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, 0.000, 0.833, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.143, 0.143, 0.143, 0.857, 0.857, 0.857, 1.000, 1.000, 1.000 ] ).astype(np.float32).reshape(-1, 3) # fmt: on def colormap(rgb=False, maximum=255): """ Args: rgb (bool): whether to return RGB colors or BGR colors. maximum (int): either 255 or 1 Returns: ndarray: a float32 array of Nx3 colors, in range [0, 255] or [0, 1] """ assert maximum in [255, 1], maximum c = _COLORS * maximum if not rgb: c = c[:, ::-1] return c def random_color(rgb=False, maximum=255): """ Args: rgb (bool): whether to return RGB colors or BGR colors. maximum (int): either 255 or 1 Returns: ndarray: a vector of 3 numbers """ idx = np.random.randint(0, len(_COLORS)) ret = _COLORS[idx] * maximum if not rgb: ret = ret[::-1] return ret def random_colors(N, rgb=False, maximum=255): """ Args: N (int): number of unique colors needed rgb (bool): whether to return RGB colors or BGR colors. maximum (int): either 255 or 1 Returns: ndarray: a list of random_color """ indices = random.sample(range(len(_COLORS)), N) ret = [_COLORS[i] * maximum for i in indices] if not rgb: ret = [x[::-1] for x in ret] return ret if __name__ == "__main__": import cv2 size = 100 H, W = 10, 10 canvas = np.random.rand(H * size, W * size, 3).astype("float32") for h in range(H): for w in range(W): idx = h * W + w if idx >= len(_COLORS): break canvas[h * size : (h + 1) * size, w * size : (w + 1) * size] = _COLORS[idx] cv2.imshow("a", canvas) cv2.waitKey(0) ================================================ FILE: detectron2/detectron2/utils/comm.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. """ This file contains primitives for multi-gpu communication. This is useful when doing distributed training. """ import functools import numpy as np import torch import torch.distributed as dist _LOCAL_PROCESS_GROUP = None """ A torch process group which only includes processes that on the same machine as the current process. This variable is set when processes are spawned by `launch()` in "engine/launch.py". """ def get_world_size() -> int: if not dist.is_available(): return 1 if not dist.is_initialized(): return 1 return dist.get_world_size() def get_rank() -> int: if not dist.is_available(): return 0 if not dist.is_initialized(): return 0 return dist.get_rank() def get_local_rank() -> int: """ Returns: The rank of the current process within the local (per-machine) process group. """ if not dist.is_available(): return 0 if not dist.is_initialized(): return 0 assert ( _LOCAL_PROCESS_GROUP is not None ), "Local process group is not created! Please use launch() to spawn processes!" return dist.get_rank(group=_LOCAL_PROCESS_GROUP) def get_local_size() -> int: """ Returns: The size of the per-machine process group, i.e. the number of processes per machine. """ if not dist.is_available(): return 1 if not dist.is_initialized(): return 1 return dist.get_world_size(group=_LOCAL_PROCESS_GROUP) def is_main_process() -> bool: return get_rank() == 0 def synchronize(): """ Helper function to synchronize (barrier) among all processes when using distributed training """ if not dist.is_available(): return if not dist.is_initialized(): return world_size = dist.get_world_size() if world_size == 1: return if dist.get_backend() == dist.Backend.NCCL: # This argument is needed to avoid warnings. # It's valid only for NCCL backend. dist.barrier(device_ids=[torch.cuda.current_device()]) else: dist.barrier() @functools.lru_cache() def _get_global_gloo_group(): """ Return a process group based on gloo backend, containing all the ranks The result is cached. """ if dist.get_backend() == "nccl": return dist.new_group(backend="gloo") else: return dist.group.WORLD def all_gather(data, group=None): """ Run all_gather on arbitrary picklable data (not necessarily tensors). Args: data: any picklable object group: a torch process group. By default, will use a group which contains all ranks on gloo backend. Returns: list[data]: list of data gathered from each rank """ if get_world_size() == 1: return [data] if group is None: group = _get_global_gloo_group() # use CPU group by default, to reduce GPU RAM usage. world_size = dist.get_world_size(group) if world_size == 1: return [data] output = [None for _ in range(world_size)] dist.all_gather_object(output, data, group=group) return output def gather(data, dst=0, group=None): """ Run gather on arbitrary picklable data (not necessarily tensors). Args: data: any picklable object dst (int): destination rank group: a torch process group. By default, will use a group which contains all ranks on gloo backend. Returns: list[data]: on dst, a list of data gathered from each rank. Otherwise, an empty list. """ if get_world_size() == 1: return [data] if group is None: group = _get_global_gloo_group() world_size = dist.get_world_size(group=group) if world_size == 1: return [data] rank = dist.get_rank(group=group) if rank == dst: output = [None for _ in range(world_size)] dist.gather_object(data, output, dst=dst, group=group) return output else: dist.gather_object(data, None, dst=dst, group=group) return [] def shared_random_seed(): """ Returns: int: a random number that is the same across all workers. If workers need a shared RNG, they can use this shared seed to create one. All workers must call this function, otherwise it will deadlock. """ ints = np.random.randint(2**31) all_ints = all_gather(ints) return all_ints[0] def reduce_dict(input_dict, average=True): """ Reduce the values in the dictionary from all processes so that process with rank 0 has the reduced results. Args: input_dict (dict): inputs to be reduced. All the values must be scalar CUDA Tensor. average (bool): whether to do average or sum Returns: a dict with the same keys as input_dict, after reduction. """ world_size = get_world_size() if world_size < 2: return input_dict with torch.no_grad(): names = [] values = [] # sort the keys so that they are consistent across processes for k in sorted(input_dict.keys()): names.append(k) values.append(input_dict[k]) values = torch.stack(values, dim=0) dist.reduce(values, dst=0) if dist.get_rank() == 0 and average: # only main process gets accumulated, so only divide by # world_size in this case values /= world_size reduced_dict = {k: v for k, v in zip(names, values)} return reduced_dict ================================================ FILE: detectron2/detectron2/utils/develop.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. """ Utilities for developers only. These are not visible to users (not automatically imported). And should not appeared in docs.""" # adapted from https://github.com/tensorpack/tensorpack/blob/master/tensorpack/utils/develop.py def create_dummy_class(klass, dependency, message=""): """ When a dependency of a class is not available, create a dummy class which throws ImportError when used. Args: klass (str): name of the class. dependency (str): name of the dependency. message: extra message to print Returns: class: a class object """ err = "Cannot import '{}', therefore '{}' is not available.".format(dependency, klass) if message: err = err + " " + message class _DummyMetaClass(type): # throw error on class attribute access def __getattr__(_, __): # noqa: B902 raise ImportError(err) class _Dummy(object, metaclass=_DummyMetaClass): # throw error on constructor def __init__(self, *args, **kwargs): raise ImportError(err) return _Dummy def create_dummy_func(func, dependency, message=""): """ When a dependency of a function is not available, create a dummy function which throws ImportError when used. Args: func (str): name of the function. dependency (str or list[str]): name(s) of the dependency. message: extra message to print Returns: function: a function object """ err = "Cannot import '{}', therefore '{}' is not available.".format(dependency, func) if message: err = err + " " + message if isinstance(dependency, (list, tuple)): dependency = ",".join(dependency) def _dummy(*args, **kwargs): raise ImportError(err) return _dummy ================================================ FILE: detectron2/detectron2/utils/env.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import importlib import importlib.util import logging import numpy as np import os import random import sys from datetime import datetime import torch __all__ = ["seed_all_rng"] TORCH_VERSION = tuple(int(x) for x in torch.__version__.split(".")[:2]) """ PyTorch version as a tuple of 2 ints. Useful for comparison. """ DOC_BUILDING = os.getenv("_DOC_BUILDING", False) # set in docs/conf.py """ Whether we're building documentation. """ def seed_all_rng(seed=None): """ Set the random seed for the RNG in torch, numpy and python. Args: seed (int): if None, will use a strong random seed. """ if seed is None: seed = ( os.getpid() + int(datetime.now().strftime("%S%f")) + int.from_bytes(os.urandom(2), "big") ) logger = logging.getLogger(__name__) logger.info("Using a generated random seed {}".format(seed)) np.random.seed(seed) torch.manual_seed(seed) random.seed(seed) os.environ["PYTHONHASHSEED"] = str(seed) # from https://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path def _import_file(module_name, file_path, make_importable=False): spec = importlib.util.spec_from_file_location(module_name, file_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) if make_importable: sys.modules[module_name] = module return module def _configure_libraries(): """ Configurations for some libraries. """ # An environment option to disable `import cv2` globally, # in case it leads to negative performance impact disable_cv2 = int(os.environ.get("DETECTRON2_DISABLE_CV2", False)) if disable_cv2: sys.modules["cv2"] = None else: # Disable opencl in opencv since its interaction with cuda often has negative effects # This envvar is supported after OpenCV 3.4.0 os.environ["OPENCV_OPENCL_RUNTIME"] = "disabled" try: import cv2 if int(cv2.__version__.split(".")[0]) >= 3: cv2.ocl.setUseOpenCL(False) except ModuleNotFoundError: # Other types of ImportError, if happened, should not be ignored. # Because a failed opencv import could mess up address space # https://github.com/skvark/opencv-python/issues/381 pass def get_version(module, digit=2): return tuple(map(int, module.__version__.split(".")[:digit])) # fmt: off assert get_version(torch) >= (1, 4), "Requires torch>=1.4" import fvcore assert get_version(fvcore, 3) >= (0, 1, 2), "Requires fvcore>=0.1.2" import yaml assert get_version(yaml) >= (5, 1), "Requires pyyaml>=5.1" # fmt: on _ENV_SETUP_DONE = False def setup_environment(): """Perform environment setup work. The default setup is a no-op, but this function allows the user to specify a Python source file or a module in the $DETECTRON2_ENV_MODULE environment variable, that performs custom setup work that may be necessary to their computing environment. """ global _ENV_SETUP_DONE if _ENV_SETUP_DONE: return _ENV_SETUP_DONE = True _configure_libraries() custom_module_path = os.environ.get("DETECTRON2_ENV_MODULE") if custom_module_path: setup_custom_environment(custom_module_path) else: # The default setup is a no-op pass def setup_custom_environment(custom_module): """ Load custom environment setup by importing a Python source file or a module, and run the setup function. """ if custom_module.endswith(".py"): module = _import_file("detectron2.utils.env.custom_module", custom_module) else: module = importlib.import_module(custom_module) assert hasattr(module, "setup_environment") and callable(module.setup_environment), ( "Custom environment module defined in {} does not have the " "required callable attribute 'setup_environment'." ).format(custom_module) module.setup_environment() def fixup_module_metadata(module_name, namespace, keys=None): """ Fix the __qualname__ of module members to be their exported api name, so when they are referenced in docs, sphinx can find them. Reference: https://github.com/python-trio/trio/blob/6754c74eacfad9cc5c92d5c24727a2f3b620624e/trio/_util.py#L216-L241 """ if not DOC_BUILDING: return seen_ids = set() def fix_one(qualname, name, obj): # avoid infinite recursion (relevant when using # typing.Generic, for example) if id(obj) in seen_ids: return seen_ids.add(id(obj)) mod = getattr(obj, "__module__", None) if mod is not None and (mod.startswith(module_name) or mod.startswith("fvcore.")): obj.__module__ = module_name # Modules, unlike everything else in Python, put fully-qualitied # names into their __name__ attribute. We check for "." to avoid # rewriting these. if hasattr(obj, "__name__") and "." not in obj.__name__: obj.__name__ = name obj.__qualname__ = qualname if isinstance(obj, type): for attr_name, attr_value in obj.__dict__.items(): fix_one(objname + "." + attr_name, attr_name, attr_value) if keys is None: keys = namespace.keys() for objname in keys: if not objname.startswith("_"): obj = namespace[objname] fix_one(objname, objname, obj) ================================================ FILE: detectron2/detectron2/utils/events.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import datetime import json import logging import os import time from collections import defaultdict from contextlib import contextmanager from typing import Optional import torch from fvcore.common.history_buffer import HistoryBuffer from detectron2.utils.file_io import PathManager __all__ = [ "get_event_storage", "JSONWriter", "TensorboardXWriter", "CommonMetricPrinter", "EventStorage", ] _CURRENT_STORAGE_STACK = [] def get_event_storage(): """ Returns: The :class:`EventStorage` object that's currently being used. Throws an error if no :class:`EventStorage` is currently enabled. """ assert len( _CURRENT_STORAGE_STACK ), "get_event_storage() has to be called inside a 'with EventStorage(...)' context!" return _CURRENT_STORAGE_STACK[-1] class EventWriter: """ Base class for writers that obtain events from :class:`EventStorage` and process them. """ def write(self): raise NotImplementedError def close(self): pass class JSONWriter(EventWriter): """ Write scalars to a json file. It saves scalars as one json per line (instead of a big json) for easy parsing. Examples parsing such a json file: :: $ cat metrics.json | jq -s '.[0:2]' [ { "data_time": 0.008433341979980469, "iteration": 19, "loss": 1.9228371381759644, "loss_box_reg": 0.050025828182697296, "loss_classifier": 0.5316952466964722, "loss_mask": 0.7236229181289673, "loss_rpn_box": 0.0856662318110466, "loss_rpn_cls": 0.48198649287223816, "lr": 0.007173333333333333, "time": 0.25401854515075684 }, { "data_time": 0.007216215133666992, "iteration": 39, "loss": 1.282649278640747, "loss_box_reg": 0.06222952902317047, "loss_classifier": 0.30682939291000366, "loss_mask": 0.6970193982124329, "loss_rpn_box": 0.038663312792778015, "loss_rpn_cls": 0.1471673548221588, "lr": 0.007706666666666667, "time": 0.2490077018737793 } ] $ cat metrics.json | jq '.loss_mask' 0.7126231789588928 0.689423680305481 0.6776131987571716 ... """ def __init__(self, json_file, window_size=20): """ Args: json_file (str): path to the json file. New data will be appended if the file exists. window_size (int): the window size of median smoothing for the scalars whose `smoothing_hint` are True. """ self._file_handle = PathManager.open(json_file, "a") self._window_size = window_size self._last_write = -1 def write(self): storage = get_event_storage() to_save = defaultdict(dict) for k, (v, iter) in storage.latest_with_smoothing_hint(self._window_size).items(): # keep scalars that have not been written if iter <= self._last_write: continue to_save[iter][k] = v if len(to_save): all_iters = sorted(to_save.keys()) self._last_write = max(all_iters) for itr, scalars_per_iter in to_save.items(): scalars_per_iter["iteration"] = itr self._file_handle.write(json.dumps(scalars_per_iter, sort_keys=True) + "\n") self._file_handle.flush() try: os.fsync(self._file_handle.fileno()) except AttributeError: pass def close(self): self._file_handle.close() class TensorboardXWriter(EventWriter): """ Write all scalars to a tensorboard file. """ def __init__(self, log_dir: str, window_size: int = 20, **kwargs): """ Args: log_dir (str): the directory to save the output events window_size (int): the scalars will be median-smoothed by this window size kwargs: other arguments passed to `torch.utils.tensorboard.SummaryWriter(...)` """ self._window_size = window_size from torch.utils.tensorboard import SummaryWriter self._writer = SummaryWriter(log_dir, **kwargs) self._last_write = -1 def write(self): storage = get_event_storage() new_last_write = self._last_write for k, (v, iter) in storage.latest_with_smoothing_hint(self._window_size).items(): if iter > self._last_write: self._writer.add_scalar(k, v, iter) new_last_write = max(new_last_write, iter) self._last_write = new_last_write # storage.put_{image,histogram} is only meant to be used by # tensorboard writer. So we access its internal fields directly from here. if len(storage._vis_data) >= 1: for img_name, img, step_num in storage._vis_data: self._writer.add_image(img_name, img, step_num) # Storage stores all image data and rely on this writer to clear them. # As a result it assumes only one writer will use its image data. # An alternative design is to let storage store limited recent # data (e.g. only the most recent image) that all writers can access. # In that case a writer may not see all image data if its period is long. storage.clear_images() if len(storage._histograms) >= 1: for params in storage._histograms: self._writer.add_histogram_raw(**params) storage.clear_histograms() def close(self): if hasattr(self, "_writer"): # doesn't exist when the code fails at import self._writer.close() class CommonMetricPrinter(EventWriter): """ Print **common** metrics to the terminal, including iteration time, ETA, memory, all losses, and the learning rate. It also applies smoothing using a window of 20 elements. It's meant to print common metrics in common ways. To print something in more customized ways, please implement a similar printer by yourself. """ def __init__(self, max_iter: Optional[int] = None, window_size: int = 20): """ Args: max_iter: the maximum number of iterations to train. Used to compute ETA. If not given, ETA will not be printed. window_size (int): the losses will be median-smoothed by this window size """ self.logger = logging.getLogger(__name__) self._max_iter = max_iter self._window_size = window_size self._last_write = None # (step, time) of last call to write(). Used to compute ETA def _get_eta(self, storage) -> Optional[str]: if self._max_iter is None: return "" iteration = storage.iter try: eta_seconds = storage.history("time").median(1000) * (self._max_iter - iteration - 1) storage.put_scalar("eta_seconds", eta_seconds, smoothing_hint=False) return str(datetime.timedelta(seconds=int(eta_seconds))) except KeyError: # estimate eta on our own - more noisy eta_string = None if self._last_write is not None: estimate_iter_time = (time.perf_counter() - self._last_write[1]) / ( iteration - self._last_write[0] ) eta_seconds = estimate_iter_time * (self._max_iter - iteration - 1) eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) self._last_write = (iteration, time.perf_counter()) return eta_string def write(self): storage = get_event_storage() iteration = storage.iter if iteration == self._max_iter: # This hook only reports training progress (loss, ETA, etc) but not other data, # therefore do not write anything after training succeeds, even if this method # is called. return try: data_time = storage.history("data_time").avg(20) except KeyError: # they may not exist in the first few iterations (due to warmup) # or when SimpleTrainer is not used data_time = None try: iter_time = storage.history("time").global_avg() except KeyError: iter_time = None try: lr = "{:.5g}".format(storage.history("lr").latest()) except KeyError: lr = "N/A" eta_string = self._get_eta(storage) if torch.cuda.is_available(): max_mem_mb = torch.cuda.max_memory_allocated() / 1024.0 / 1024.0 else: max_mem_mb = None # NOTE: max_mem is parsed by grep in "dev/parse_results.sh" self.logger.info( " {eta}iter: {iter} {losses} {time}{data_time}lr: {lr} {memory}".format( eta=f"eta: {eta_string} " if eta_string else "", iter=iteration, losses=" ".join( [ "{}: {:.4g}".format(k, v.median(self._window_size)) for k, v in storage.histories().items() if "loss" in k ] ), time="time: {:.4f} ".format(iter_time) if iter_time is not None else "", data_time="data_time: {:.4f} ".format(data_time) if data_time is not None else "", lr=lr, memory="max_mem: {:.0f}M".format(max_mem_mb) if max_mem_mb is not None else "", ) ) class EventStorage: """ The user-facing class that provides metric storage functionalities. In the future we may add support for storing / logging other types of data if needed. """ def __init__(self, start_iter=0): """ Args: start_iter (int): the iteration number to start with """ self._history = defaultdict(HistoryBuffer) self._smoothing_hints = {} self._latest_scalars = {} self._iter = start_iter self._current_prefix = "" self._vis_data = [] self._histograms = [] def put_image(self, img_name, img_tensor): """ Add an `img_tensor` associated with `img_name`, to be shown on tensorboard. Args: img_name (str): The name of the image to put into tensorboard. img_tensor (torch.Tensor or numpy.array): An `uint8` or `float` Tensor of shape `[channel, height, width]` where `channel` is 3. The image format should be RGB. The elements in img_tensor can either have values in [0, 1] (float32) or [0, 255] (uint8). The `img_tensor` will be visualized in tensorboard. """ self._vis_data.append((img_name, img_tensor, self._iter)) def put_scalar(self, name, value, smoothing_hint=True): """ Add a scalar `value` to the `HistoryBuffer` associated with `name`. Args: smoothing_hint (bool): a 'hint' on whether this scalar is noisy and should be smoothed when logged. The hint will be accessible through :meth:`EventStorage.smoothing_hints`. A writer may ignore the hint and apply custom smoothing rule. It defaults to True because most scalars we save need to be smoothed to provide any useful signal. """ name = self._current_prefix + name history = self._history[name] value = float(value) history.update(value, self._iter) self._latest_scalars[name] = (value, self._iter) existing_hint = self._smoothing_hints.get(name) if existing_hint is not None: assert ( existing_hint == smoothing_hint ), "Scalar {} was put with a different smoothing_hint!".format(name) else: self._smoothing_hints[name] = smoothing_hint def put_scalars(self, *, smoothing_hint=True, **kwargs): """ Put multiple scalars from keyword arguments. Examples: storage.put_scalars(loss=my_loss, accuracy=my_accuracy, smoothing_hint=True) """ for k, v in kwargs.items(): self.put_scalar(k, v, smoothing_hint=smoothing_hint) def put_histogram(self, hist_name, hist_tensor, bins=1000): """ Create a histogram from a tensor. Args: hist_name (str): The name of the histogram to put into tensorboard. hist_tensor (torch.Tensor): A Tensor of arbitrary shape to be converted into a histogram. bins (int): Number of histogram bins. """ ht_min, ht_max = hist_tensor.min().item(), hist_tensor.max().item() # Create a histogram with PyTorch hist_counts = torch.histc(hist_tensor, bins=bins) hist_edges = torch.linspace(start=ht_min, end=ht_max, steps=bins + 1, dtype=torch.float32) # Parameter for the add_histogram_raw function of SummaryWriter hist_params = dict( tag=hist_name, min=ht_min, max=ht_max, num=len(hist_tensor), sum=float(hist_tensor.sum()), sum_squares=float(torch.sum(hist_tensor**2)), bucket_limits=hist_edges[1:].tolist(), bucket_counts=hist_counts.tolist(), global_step=self._iter, ) self._histograms.append(hist_params) def history(self, name): """ Returns: HistoryBuffer: the scalar history for name """ ret = self._history.get(name, None) if ret is None: raise KeyError("No history metric available for {}!".format(name)) return ret def histories(self): """ Returns: dict[name -> HistoryBuffer]: the HistoryBuffer for all scalars """ return self._history def latest(self): """ Returns: dict[str -> (float, int)]: mapping from the name of each scalar to the most recent value and the iteration number its added. """ return self._latest_scalars def latest_with_smoothing_hint(self, window_size=20): """ Similar to :meth:`latest`, but the returned values are either the un-smoothed original latest value, or a median of the given window_size, depend on whether the smoothing_hint is True. This provides a default behavior that other writers can use. """ result = {} for k, (v, itr) in self._latest_scalars.items(): result[k] = ( self._history[k].median(window_size) if self._smoothing_hints[k] else v, itr, ) return result def smoothing_hints(self): """ Returns: dict[name -> bool]: the user-provided hint on whether the scalar is noisy and needs smoothing. """ return self._smoothing_hints def step(self): """ User should either: (1) Call this function to increment storage.iter when needed. Or (2) Set `storage.iter` to the correct iteration number before each iteration. The storage will then be able to associate the new data with an iteration number. """ self._iter += 1 @property def iter(self): """ Returns: int: The current iteration number. When used together with a trainer, this is ensured to be the same as trainer.iter. """ return self._iter @iter.setter def iter(self, val): self._iter = int(val) @property def iteration(self): # for backward compatibility return self._iter def __enter__(self): _CURRENT_STORAGE_STACK.append(self) return self def __exit__(self, exc_type, exc_val, exc_tb): assert _CURRENT_STORAGE_STACK[-1] == self _CURRENT_STORAGE_STACK.pop() @contextmanager def name_scope(self, name): """ Yields: A context within which all the events added to this storage will be prefixed by the name scope. """ old_prefix = self._current_prefix self._current_prefix = name.rstrip("/") + "/" yield self._current_prefix = old_prefix def clear_images(self): """ Delete all the stored images for visualization. This should be called after images are written to tensorboard. """ self._vis_data = [] def clear_histograms(self): """ Delete all the stored histograms for visualization. This should be called after histograms are written to tensorboard. """ self._histograms = [] ================================================ FILE: detectron2/detectron2/utils/file_io.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from iopath.common.file_io import HTTPURLHandler, OneDrivePathHandler, PathHandler from iopath.common.file_io import PathManager as PathManagerBase __all__ = ["PathManager", "PathHandler"] PathManager = PathManagerBase() """ This is a detectron2 project-specific PathManager. We try to stay away from global PathManager in fvcore as it introduces potential conflicts among other libraries. """ class Detectron2Handler(PathHandler): """ Resolve anything that's hosted under detectron2's namespace. """ PREFIX = "detectron2://" S3_DETECTRON2_PREFIX = "https://dl.fbaipublicfiles.com/detectron2/" def _get_supported_prefixes(self): return [self.PREFIX] def _get_local_path(self, path, **kwargs): name = path[len(self.PREFIX) :] return PathManager.get_local_path(self.S3_DETECTRON2_PREFIX + name, **kwargs) def _open(self, path, mode="r", **kwargs): return PathManager.open(self._get_local_path(path), mode, **kwargs) PathManager.register_handler(HTTPURLHandler()) PathManager.register_handler(OneDrivePathHandler()) PathManager.register_handler(Detectron2Handler()) ================================================ FILE: detectron2/detectron2/utils/logger.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import atexit import functools import logging import os import sys import time from collections import Counter import torch from tabulate import tabulate from termcolor import colored from detectron2.utils.file_io import PathManager __all__ = ["setup_logger", "log_first_n", "log_every_n", "log_every_n_seconds"] class _ColorfulFormatter(logging.Formatter): def __init__(self, *args, **kwargs): self._root_name = kwargs.pop("root_name") + "." self._abbrev_name = kwargs.pop("abbrev_name", "") if len(self._abbrev_name): self._abbrev_name = self._abbrev_name + "." super(_ColorfulFormatter, self).__init__(*args, **kwargs) def formatMessage(self, record): record.name = record.name.replace(self._root_name, self._abbrev_name) log = super(_ColorfulFormatter, self).formatMessage(record) if record.levelno == logging.WARNING: prefix = colored("WARNING", "red", attrs=["blink"]) elif record.levelno == logging.ERROR or record.levelno == logging.CRITICAL: prefix = colored("ERROR", "red", attrs=["blink", "underline"]) else: return log return prefix + " " + log @functools.lru_cache() # so that calling setup_logger multiple times won't add many handlers def setup_logger( output=None, distributed_rank=0, *, color=True, name="detectron2", abbrev_name=None ): """ Initialize the detectron2 logger and set its verbosity level to "DEBUG". Args: output (str): a file name or a directory to save log. If None, will not save log file. If ends with ".txt" or ".log", assumed to be a file name. Otherwise, logs will be saved to `output/log.txt`. name (str): the root module name of this logger abbrev_name (str): an abbreviation of the module, to avoid long names in logs. Set to "" to not log the root module in logs. By default, will abbreviate "detectron2" to "d2" and leave other modules unchanged. Returns: logging.Logger: a logger """ logger = logging.getLogger(name) logger.setLevel(logging.DEBUG) logger.propagate = False if abbrev_name is None: abbrev_name = "d2" if name == "detectron2" else name plain_formatter = logging.Formatter( "[%(asctime)s] %(name)s %(levelname)s: %(message)s", datefmt="%m/%d %H:%M:%S" ) # stdout logging: master only if distributed_rank == 0: ch = logging.StreamHandler(stream=sys.stdout) ch.setLevel(logging.DEBUG) if color: formatter = _ColorfulFormatter( colored("[%(asctime)s %(name)s]: ", "green") + "%(message)s", datefmt="%m/%d %H:%M:%S", root_name=name, abbrev_name=str(abbrev_name), ) else: formatter = plain_formatter ch.setFormatter(formatter) logger.addHandler(ch) # file logging: all workers if output is not None: if output.endswith(".txt") or output.endswith(".log"): filename = output else: filename = os.path.join(output, "log.txt") if distributed_rank > 0: filename = filename + ".rank{}".format(distributed_rank) PathManager.mkdirs(os.path.dirname(filename)) fh = logging.StreamHandler(_cached_log_stream(filename)) fh.setLevel(logging.DEBUG) fh.setFormatter(plain_formatter) logger.addHandler(fh) return logger # cache the opened file object, so that different calls to `setup_logger` # with the same file name can safely write to the same file. @functools.lru_cache(maxsize=None) def _cached_log_stream(filename): # use 1K buffer if writing to cloud storage io = PathManager.open(filename, "a", buffering=1024 if "://" in filename else -1) atexit.register(io.close) return io """ Below are some other convenient logging methods. They are mainly adopted from https://github.com/abseil/abseil-py/blob/master/absl/logging/__init__.py """ def _find_caller(): """ Returns: str: module name of the caller tuple: a hashable key to be used to identify different callers """ frame = sys._getframe(2) while frame: code = frame.f_code if os.path.join("utils", "logger.") not in code.co_filename: mod_name = frame.f_globals["__name__"] if mod_name == "__main__": mod_name = "detectron2" return mod_name, (code.co_filename, frame.f_lineno, code.co_name) frame = frame.f_back _LOG_COUNTER = Counter() _LOG_TIMER = {} def log_first_n(lvl, msg, n=1, *, name=None, key="caller"): """ Log only for the first n times. Args: lvl (int): the logging level msg (str): n (int): name (str): name of the logger to use. Will use the caller's module by default. key (str or tuple[str]): the string(s) can be one of "caller" or "message", which defines how to identify duplicated logs. For example, if called with `n=1, key="caller"`, this function will only log the first call from the same caller, regardless of the message content. If called with `n=1, key="message"`, this function will log the same content only once, even if they are called from different places. If called with `n=1, key=("caller", "message")`, this function will not log only if the same caller has logged the same message before. """ if isinstance(key, str): key = (key,) assert len(key) > 0 caller_module, caller_key = _find_caller() hash_key = () if "caller" in key: hash_key = hash_key + caller_key if "message" in key: hash_key = hash_key + (msg,) _LOG_COUNTER[hash_key] += 1 if _LOG_COUNTER[hash_key] <= n: logging.getLogger(name or caller_module).log(lvl, msg) def log_every_n(lvl, msg, n=1, *, name=None): """ Log once per n times. Args: lvl (int): the logging level msg (str): n (int): name (str): name of the logger to use. Will use the caller's module by default. """ caller_module, key = _find_caller() _LOG_COUNTER[key] += 1 if n == 1 or _LOG_COUNTER[key] % n == 1: logging.getLogger(name or caller_module).log(lvl, msg) def log_every_n_seconds(lvl, msg, n=1, *, name=None): """ Log no more than once per n seconds. Args: lvl (int): the logging level msg (str): n (int): name (str): name of the logger to use. Will use the caller's module by default. """ caller_module, key = _find_caller() last_logged = _LOG_TIMER.get(key, None) current_time = time.time() if last_logged is None or current_time - last_logged >= n: logging.getLogger(name or caller_module).log(lvl, msg) _LOG_TIMER[key] = current_time def create_small_table(small_dict): """ Create a small table using the keys of small_dict as headers. This is only suitable for small dictionaries. Args: small_dict (dict): a result dictionary of only a few items. Returns: str: the table as a string. """ keys, values = tuple(zip(*small_dict.items())) table = tabulate( [values], headers=keys, tablefmt="pipe", floatfmt=".3f", stralign="center", numalign="center", ) return table def _log_api_usage(identifier: str): """ Internal function used to log the usage of different detectron2 components inside facebook's infra. """ torch._C._log_api_usage_once("detectron2." + identifier) ================================================ FILE: detectron2/detectron2/utils/memory.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging from contextlib import contextmanager from functools import wraps import torch __all__ = ["retry_if_cuda_oom"] @contextmanager def _ignore_torch_cuda_oom(): """ A context which ignores CUDA OOM exception from pytorch. """ try: yield except RuntimeError as e: # NOTE: the string may change? if "CUDA out of memory. " in str(e): pass else: raise def retry_if_cuda_oom(func): """ Makes a function retry itself after encountering pytorch's CUDA OOM error. It will first retry after calling `torch.cuda.empty_cache()`. If that still fails, it will then retry by trying to convert inputs to CPUs. In this case, it expects the function to dispatch to CPU implementation. The return values may become CPU tensors as well and it's user's responsibility to convert it back to CUDA tensor if needed. Args: func: a stateless callable that takes tensor-like objects as arguments Returns: a callable which retries `func` if OOM is encountered. Examples: :: output = retry_if_cuda_oom(some_torch_function)(input1, input2) # output may be on CPU even if inputs are on GPU Note: 1. When converting inputs to CPU, it will only look at each argument and check if it has `.device` and `.to` for conversion. Nested structures of tensors are not supported. 2. Since the function might be called more than once, it has to be stateless. """ def maybe_to_cpu(x): try: like_gpu_tensor = x.device.type == "cuda" and hasattr(x, "to") except AttributeError: like_gpu_tensor = False if like_gpu_tensor: return x.to(device="cpu") else: return x @wraps(func) def wrapped(*args, **kwargs): with _ignore_torch_cuda_oom(): return func(*args, **kwargs) # Clear cache and retry torch.cuda.empty_cache() with _ignore_torch_cuda_oom(): return func(*args, **kwargs) # Try on CPU. This slows down the code significantly, therefore print a notice. logger = logging.getLogger(__name__) logger.info("Attempting to copy inputs of {} to CPU due to CUDA OOM".format(str(func))) new_args = (maybe_to_cpu(x) for x in args) new_kwargs = {k: maybe_to_cpu(v) for k, v in kwargs.items()} return func(*new_args, **new_kwargs) return wrapped ================================================ FILE: detectron2/detectron2/utils/registry.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any import pydoc from fvcore.common.registry import Registry # for backward compatibility. """ ``Registry`` and `locate` provide ways to map a string (typically found in config files) to callable objects. """ __all__ = ["Registry", "locate"] def _convert_target_to_string(t: Any) -> str: """ Inverse of ``locate()``. Args: t: any object with ``__module__`` and ``__qualname__`` """ module, qualname = t.__module__, t.__qualname__ # Compress the path to this object, e.g. ``module.submodule._impl.class`` # may become ``module.submodule.class``, if the later also resolves to the same # object. This simplifies the string, and also is less affected by moving the # class implementation. module_parts = module.split(".") for k in range(1, len(module_parts)): prefix = ".".join(module_parts[:k]) candidate = f"{prefix}.{qualname}" try: if locate(candidate) is t: return candidate except ImportError: pass return f"{module}.{qualname}" def locate(name: str) -> Any: """ Locate and return an object ``x`` using an input string ``{x.__module__}.{x.__qualname__}``, such as "module.submodule.class_name". Raise Exception if it cannot be found. """ obj = pydoc.locate(name) # Some cases (e.g. torch.optim.sgd.SGD) not handled correctly # by pydoc.locate. Try a private function from hydra. if obj is None: try: # from hydra.utils import get_method - will print many errors from hydra.utils import _locate except ImportError as e: raise ImportError(f"Cannot dynamically locate object {name}!") from e else: obj = _locate(name) # it raises if fails return obj ================================================ FILE: detectron2/detectron2/utils/serialize.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import cloudpickle class PicklableWrapper(object): """ Wrap an object to make it more picklable, note that it uses heavy weight serialization libraries that are slower than pickle. It's best to use it only on closures (which are usually not picklable). This is a simplified version of https://github.com/joblib/joblib/blob/master/joblib/externals/loky/cloudpickle_wrapper.py """ def __init__(self, obj): while isinstance(obj, PicklableWrapper): # Wrapping an object twice is no-op obj = obj._obj self._obj = obj def __reduce__(self): s = cloudpickle.dumps(self._obj) return cloudpickle.loads, (s,) def __call__(self, *args, **kwargs): return self._obj(*args, **kwargs) def __getattr__(self, attr): # Ensure that the wrapped object can be used seamlessly as the previous object. if attr not in ["_obj"]: return getattr(self._obj, attr) return getattr(self, attr) ================================================ FILE: detectron2/detectron2/utils/testing.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import io import numpy as np import os import re import tempfile import unittest from typing import Callable import torch import torch.onnx.symbolic_helper as sym_help from packaging import version from torch._C import ListType from torch.onnx import register_custom_op_symbolic from detectron2 import model_zoo from detectron2.config import CfgNode, LazyConfig, instantiate from detectron2.data import DatasetCatalog from detectron2.data.detection_utils import read_image from detectron2.modeling import build_model from detectron2.structures import Boxes, Instances, ROIMasks from detectron2.utils.file_io import PathManager """ Internal utilities for tests. Don't use except for writing tests. """ def get_model_no_weights(config_path): """ Like model_zoo.get, but do not load any weights (even pretrained) """ cfg = model_zoo.get_config(config_path) if isinstance(cfg, CfgNode): if not torch.cuda.is_available(): cfg.MODEL.DEVICE = "cpu" return build_model(cfg) else: return instantiate(cfg.model) def random_boxes(num_boxes, max_coord=100, device="cpu"): """ Create a random Nx4 boxes tensor, with coordinates < max_coord. """ boxes = torch.rand(num_boxes, 4, device=device) * (max_coord * 0.5) boxes.clamp_(min=1.0) # tiny boxes cause numerical instability in box regression # Note: the implementation of this function in torchvision is: # boxes[:, 2:] += torch.rand(N, 2) * 100 # but it does not guarantee non-negative widths/heights constraints: # boxes[:, 2] >= boxes[:, 0] and boxes[:, 3] >= boxes[:, 1]: boxes[:, 2:] += boxes[:, :2] return boxes def get_sample_coco_image(tensor=True): """ Args: tensor (bool): if True, returns 3xHxW tensor. else, returns a HxWx3 numpy array. Returns: an image, in BGR color. """ try: file_name = DatasetCatalog.get("coco_2017_val_100")[0]["file_name"] if not PathManager.exists(file_name): raise FileNotFoundError() except IOError: # for public CI to run file_name = PathManager.get_local_path( "http://images.cocodataset.org/train2017/000000000009.jpg" ) ret = read_image(file_name, format="BGR") if tensor: ret = torch.from_numpy(np.ascontiguousarray(ret.transpose(2, 0, 1))) return ret def convert_scripted_instances(instances): """ Convert a scripted Instances object to a regular :class:`Instances` object """ assert hasattr( instances, "image_size" ), f"Expect an Instances object, but got {type(instances)}!" ret = Instances(instances.image_size) for name in instances._field_names: val = getattr(instances, "_" + name, None) if val is not None: ret.set(name, val) return ret def assert_instances_allclose(input, other, *, rtol=1e-5, msg="", size_as_tensor=False): """ Args: input, other (Instances): size_as_tensor: compare image_size of the Instances as tensors (instead of tuples). Useful for comparing outputs of tracing. """ if not isinstance(input, Instances): input = convert_scripted_instances(input) if not isinstance(other, Instances): other = convert_scripted_instances(other) if not msg: msg = "Two Instances are different! " else: msg = msg.rstrip() + " " size_error_msg = msg + f"image_size is {input.image_size} vs. {other.image_size}!" if size_as_tensor: assert torch.equal( torch.tensor(input.image_size), torch.tensor(other.image_size) ), size_error_msg else: assert input.image_size == other.image_size, size_error_msg fields = sorted(input.get_fields().keys()) fields_other = sorted(other.get_fields().keys()) assert fields == fields_other, msg + f"Fields are {fields} vs {fields_other}!" for f in fields: val1, val2 = input.get(f), other.get(f) if isinstance(val1, (Boxes, ROIMasks)): # boxes in the range of O(100) and can have a larger tolerance assert torch.allclose(val1.tensor, val2.tensor, atol=100 * rtol), ( msg + f"Field {f} differs too much!" ) elif isinstance(val1, torch.Tensor): if val1.dtype.is_floating_point: mag = torch.abs(val1).max().cpu().item() assert torch.allclose(val1, val2, atol=mag * rtol), ( msg + f"Field {f} differs too much!" ) else: assert torch.equal(val1, val2), msg + f"Field {f} is different!" else: raise ValueError(f"Don't know how to compare type {type(val1)}") def reload_script_model(module): """ Save a jit module and load it back. Similar to the `getExportImportCopy` function in torch/testing/ """ buffer = io.BytesIO() torch.jit.save(module, buffer) buffer.seek(0) return torch.jit.load(buffer) def reload_lazy_config(cfg): """ Save an object by LazyConfig.save and load it back. This is used to test that a config still works the same after serialization/deserialization. """ with tempfile.TemporaryDirectory(prefix="detectron2") as d: fname = os.path.join(d, "d2_cfg_test.yaml") LazyConfig.save(cfg, fname) return LazyConfig.load(fname) def min_torch_version(min_version: str) -> bool: """ Returns True when torch's version is at least `min_version`. """ try: import torch except ImportError: return False installed_version = version.parse(torch.__version__.split("+")[0]) min_version = version.parse(min_version) return installed_version >= min_version def register_custom_op_onnx_export( opname: str, symbolic_fn: Callable, opset_version: int, min_version: str ) -> None: """ Register `symbolic_fn` as PyTorch's symbolic `opname`-`opset_version` for ONNX export. The registration is performed only when current PyTorch's version is < `min_version.` IMPORTANT: symbolic must be manually unregistered after the caller function returns """ if min_torch_version(min_version): return register_custom_op_symbolic(opname, symbolic_fn, opset_version) print(f"_register_custom_op_onnx_export({opname}, {opset_version}) succeeded.") def unregister_custom_op_onnx_export(opname: str, opset_version: int, min_version: str) -> None: """ Unregister PyTorch's symbolic `opname`-`opset_version` for ONNX export. The un-registration is performed only when PyTorch's version is < `min_version` IMPORTANT: The symbolic must have been manually registered by the caller, otherwise the incorrect symbolic may be unregistered instead. """ # TODO: _unregister_custom_op_symbolic is introduced PyTorch>=1.10 # Remove after PyTorch 1.10+ is used by ALL detectron2's CI try: from torch.onnx import unregister_custom_op_symbolic as _unregister_custom_op_symbolic except ImportError: def _unregister_custom_op_symbolic(symbolic_name, opset_version): import torch.onnx.symbolic_registry as sym_registry from torch.onnx.symbolic_helper import _onnx_main_opset, _onnx_stable_opsets def _get_ns_op_name_from_custom_op(symbolic_name): try: from torch.onnx.utils import get_ns_op_name_from_custom_op ns, op_name = get_ns_op_name_from_custom_op(symbolic_name) except ImportError as import_error: if not bool( re.match(r"^[a-zA-Z0-9-_]*::[a-zA-Z-_]+[a-zA-Z0-9-_]*$", symbolic_name) ): raise ValueError( f"Invalid symbolic name {symbolic_name}. Must be `domain::name`" ) from import_error ns, op_name = symbolic_name.split("::") if ns == "onnx": raise ValueError(f"{ns} domain cannot be modified.") from import_error if ns == "aten": ns = "" return ns, op_name def _unregister_op(opname: str, domain: str, version: int): try: sym_registry.unregister_op(op_name, ns, ver) except AttributeError as attribute_error: if sym_registry.is_registered_op(opname, domain, version): del sym_registry._registry[(domain, version)][opname] if not sym_registry._registry[(domain, version)]: del sym_registry._registry[(domain, version)] else: raise RuntimeError( f"The opname {opname} is not registered." ) from attribute_error ns, op_name = _get_ns_op_name_from_custom_op(symbolic_name) for ver in _onnx_stable_opsets + [_onnx_main_opset]: if ver >= opset_version: _unregister_op(op_name, ns, ver) if min_torch_version(min_version): return _unregister_custom_op_symbolic(opname, opset_version) print(f"_unregister_custom_op_onnx_export({opname}, {opset_version}) succeeded.") skipIfOnCPUCI = unittest.skipIf( os.environ.get("CI") and not torch.cuda.is_available(), "The test is too slow on CPUs and will be executed on CircleCI's GPU jobs.", ) def skipIfUnsupportedMinOpsetVersion(min_opset_version, current_opset_version=None): """ Skips tests for ONNX Opset versions older than min_opset_version. """ def skip_dec(func): def wrapper(self): try: opset_version = self.opset_version except AttributeError: opset_version = current_opset_version if opset_version < min_opset_version: raise unittest.SkipTest( f"Unsupported opset_version {opset_version}" f", required is {min_opset_version}" ) return func(self) return wrapper return skip_dec def skipIfUnsupportedMinTorchVersion(min_version): """ Skips tests for PyTorch versions older than min_version. """ reason = f"module 'torch' has __version__ {torch.__version__}" f", required is: {min_version}" return unittest.skipIf(not min_torch_version(min_version), reason) # TODO: Remove after PyTorch 1.11.1+ is used by detectron2's CI def _pytorch1111_symbolic_opset9_to(g, self, *args): """aten::to() symbolic that must be used for testing with PyTorch < 1.11.1.""" def is_aten_to_device_only(args): if len(args) == 4: # aten::to(Tensor, Device, bool, bool, memory_format) return ( args[0].node().kind() == "prim::device" or args[0].type().isSubtypeOf(ListType.ofInts()) or ( sym_help._is_value(args[0]) and args[0].node().kind() == "onnx::Constant" and isinstance(args[0].node()["value"], str) ) ) elif len(args) == 5: # aten::to(Tensor, Device, ScalarType, bool, bool, memory_format) # When dtype is None, this is a aten::to(device) call dtype = sym_help._get_const(args[1], "i", "dtype") return dtype is None elif len(args) in (6, 7): # aten::to(Tensor, ScalarType, Layout, Device, bool, bool, memory_format) # aten::to(Tensor, ScalarType, Layout, Device, bool, bool, bool, memory_format) # When dtype is None, this is a aten::to(device) call dtype = sym_help._get_const(args[0], "i", "dtype") return dtype is None return False # ONNX doesn't have a concept of a device, so we ignore device-only casts if is_aten_to_device_only(args): return self if len(args) == 4: # TestONNXRuntime::test_ones_bool shows args[0] of aten::to can be onnx::Constant[Tensor] # In this case, the constant value is a tensor not int, # so sym_help._maybe_get_const(args[0], 'i') would not work. dtype = args[0] if sym_help._is_value(args[0]) and args[0].node().kind() == "onnx::Constant": tval = args[0].node()["value"] if isinstance(tval, torch.Tensor): if len(tval.shape) == 0: tval = tval.item() dtype = int(tval) else: dtype = tval if sym_help._is_value(dtype) or isinstance(dtype, torch.Tensor): # aten::to(Tensor, Tensor, bool, bool, memory_format) dtype = args[0].type().scalarType() return g.op("Cast", self, to_i=sym_help.cast_pytorch_to_onnx[dtype]) else: # aten::to(Tensor, ScalarType, bool, bool, memory_format) # memory_format is ignored return g.op("Cast", self, to_i=sym_help.scalar_type_to_onnx[dtype]) elif len(args) == 5: # aten::to(Tensor, Device, ScalarType, bool, bool, memory_format) dtype = sym_help._get_const(args[1], "i", "dtype") # memory_format is ignored return g.op("Cast", self, to_i=sym_help.scalar_type_to_onnx[dtype]) elif len(args) == 6: # aten::to(Tensor, ScalarType, Layout, Device, bool, bool, memory_format) dtype = sym_help._get_const(args[0], "i", "dtype") # Layout, device and memory_format are ignored return g.op("Cast", self, to_i=sym_help.scalar_type_to_onnx[dtype]) elif len(args) == 7: # aten::to(Tensor, ScalarType, Layout, Device, bool, bool, bool, memory_format) dtype = sym_help._get_const(args[0], "i", "dtype") # Layout, device and memory_format are ignored return g.op("Cast", self, to_i=sym_help.scalar_type_to_onnx[dtype]) else: return sym_help._onnx_unsupported("Unknown aten::to signature") # TODO: Remove after PyTorch 1.11.1+ is used by detectron2's CI def _pytorch1111_symbolic_opset9_repeat_interleave(g, self, repeats, dim=None, output_size=None): # from torch.onnx.symbolic_helper import ScalarType from torch.onnx.symbolic_opset9 import expand, unsqueeze input = self # if dim is None flatten # By default, use the flattened input array, and return a flat output array if sym_help._is_none(dim): input = sym_help._reshape_helper(g, self, g.op("Constant", value_t=torch.tensor([-1]))) dim = 0 else: dim = sym_help._maybe_get_scalar(dim) repeats_dim = sym_help._get_tensor_rank(repeats) repeats_sizes = sym_help._get_tensor_sizes(repeats) input_sizes = sym_help._get_tensor_sizes(input) if repeats_dim is None: raise RuntimeError( "Unsupported: ONNX export of repeat_interleave for unknown " "repeats rank." ) if repeats_sizes is None: raise RuntimeError( "Unsupported: ONNX export of repeat_interleave for unknown " "repeats size." ) if input_sizes is None: raise RuntimeError( "Unsupported: ONNX export of repeat_interleave for unknown " "input size." ) input_sizes_temp = input_sizes.copy() for idx, input_size in enumerate(input_sizes): if input_size is None: input_sizes[idx], input_sizes_temp[idx] = 0, -1 # Cases where repeats is an int or single value tensor if repeats_dim == 0 or (repeats_dim == 1 and repeats_sizes[0] == 1): if not sym_help._is_tensor(repeats): repeats = g.op("Constant", value_t=torch.LongTensor(repeats)) if input_sizes[dim] == 0: return sym_help._onnx_opset_unsupported_detailed( "repeat_interleave", 9, 13, "Unsupported along dimension with unknown input size", ) else: reps = input_sizes[dim] repeats = expand(g, repeats, g.op("Constant", value_t=torch.tensor([reps])), None) # Cases where repeats is a 1 dim Tensor elif repeats_dim == 1: if input_sizes[dim] == 0: return sym_help._onnx_opset_unsupported_detailed( "repeat_interleave", 9, 13, "Unsupported along dimension with unknown input size", ) if repeats_sizes[0] is None: return sym_help._onnx_opset_unsupported_detailed( "repeat_interleave", 9, 13, "Unsupported for cases with dynamic repeats" ) assert ( repeats_sizes[0] == input_sizes[dim] ), "repeats must have the same size as input along dim" reps = repeats_sizes[0] else: raise RuntimeError("repeats must be 0-dim or 1-dim tensor") final_splits = list() r_splits = sym_help._repeat_interleave_split_helper(g, repeats, reps, 0) if isinstance(r_splits, torch._C.Value): r_splits = [r_splits] i_splits = sym_help._repeat_interleave_split_helper(g, input, reps, dim) if isinstance(i_splits, torch._C.Value): i_splits = [i_splits] input_sizes[dim], input_sizes_temp[dim] = -1, 1 for idx, r_split in enumerate(r_splits): i_split = unsqueeze(g, i_splits[idx], dim + 1) r_concat = [ g.op("Constant", value_t=torch.LongTensor(input_sizes_temp[: dim + 1])), r_split, g.op("Constant", value_t=torch.LongTensor(input_sizes_temp[dim + 1 :])), ] r_concat = g.op("Concat", *r_concat, axis_i=0) i_split = expand(g, i_split, r_concat, None) i_split = sym_help._reshape_helper( g, i_split, g.op("Constant", value_t=torch.LongTensor(input_sizes)), allowzero=0, ) final_splits.append(i_split) return g.op("Concat", *final_splits, axis_i=dim) ================================================ FILE: detectron2/detectron2/utils/video_visualizer.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from typing import List import pycocotools.mask as mask_util from detectron2.structures import Instances from detectron2.utils.visualizer import ( ColorMode, Visualizer, _create_text_labels, _PanopticPrediction, ) from .colormap import random_color, random_colors class _DetectedInstance: """ Used to store data about detected objects in video frame, in order to transfer color to objects in the future frames. Attributes: label (int): bbox (tuple[float]): mask_rle (dict): color (tuple[float]): RGB colors in range (0, 1) ttl (int): time-to-live for the instance. For example, if ttl=2, the instance color can be transferred to objects in the next two frames. """ __slots__ = ["label", "bbox", "mask_rle", "color", "ttl"] def __init__(self, label, bbox, mask_rle, color, ttl): self.label = label self.bbox = bbox self.mask_rle = mask_rle self.color = color self.ttl = ttl class VideoVisualizer: def __init__(self, metadata, instance_mode=ColorMode.IMAGE): """ Args: metadata (MetadataCatalog): image metadata. """ self.metadata = metadata self._old_instances = [] assert instance_mode in [ ColorMode.IMAGE, ColorMode.IMAGE_BW, ], "Other mode not supported yet." self._instance_mode = instance_mode self._max_num_instances = self.metadata.get("max_num_instances", 74) self._assigned_colors = {} self._color_pool = random_colors(self._max_num_instances, rgb=True, maximum=1) self._color_idx_set = set(range(len(self._color_pool))) def draw_instance_predictions(self, frame, predictions): """ Draw instance-level prediction results on an image. Args: frame (ndarray): an RGB image of shape (H, W, C), in the range [0, 255]. predictions (Instances): the output of an instance detection/segmentation model. Following fields will be used to draw: "pred_boxes", "pred_classes", "scores", "pred_masks" (or "pred_masks_rle"). Returns: output (VisImage): image object with visualizations. """ frame_visualizer = Visualizer(frame, self.metadata) num_instances = len(predictions) if num_instances == 0: return frame_visualizer.output boxes = predictions.pred_boxes.tensor.numpy() if predictions.has("pred_boxes") else None scores = predictions.scores if predictions.has("scores") else None classes = predictions.pred_classes.numpy() if predictions.has("pred_classes") else None keypoints = predictions.pred_keypoints if predictions.has("pred_keypoints") else None colors = predictions.COLOR if predictions.has("COLOR") else [None] * len(predictions) periods = predictions.ID_period if predictions.has("ID_period") else None period_threshold = self.metadata.get("period_threshold", 0) visibilities = ( [True] * len(predictions) if periods is None else [x > period_threshold for x in periods] ) if predictions.has("pred_masks"): masks = predictions.pred_masks # mask IOU is not yet enabled # masks_rles = mask_util.encode(np.asarray(masks.permute(1, 2, 0), order="F")) # assert len(masks_rles) == num_instances else: masks = None if not predictions.has("COLOR"): if predictions.has("ID"): colors = self._assign_colors_by_id(predictions) else: # ToDo: clean old assign color method and use a default tracker to assign id detected = [ _DetectedInstance(classes[i], boxes[i], mask_rle=None, color=colors[i], ttl=8) for i in range(num_instances) ] colors = self._assign_colors(detected) labels = _create_text_labels(classes, scores, self.metadata.get("thing_classes", None)) if self._instance_mode == ColorMode.IMAGE_BW: # any() returns uint8 tensor frame_visualizer.output.reset_image( frame_visualizer._create_grayscale_image( (masks.any(dim=0) > 0).numpy() if masks is not None else None ) ) alpha = 0.3 else: alpha = 0.5 labels = ( None if labels is None else [y[0] for y in filter(lambda x: x[1], zip(labels, visibilities))] ) # noqa assigned_colors = ( None if colors is None else [y[0] for y in filter(lambda x: x[1], zip(colors, visibilities))] ) # noqa frame_visualizer.overlay_instances( boxes=None if masks is not None else boxes[visibilities], # boxes are a bit distracting masks=None if masks is None else masks[visibilities], labels=labels, keypoints=None if keypoints is None else keypoints[visibilities], assigned_colors=assigned_colors, alpha=alpha, ) return frame_visualizer.output def draw_sem_seg(self, frame, sem_seg, area_threshold=None): """ Args: sem_seg (ndarray or Tensor): semantic segmentation of shape (H, W), each value is the integer label. area_threshold (Optional[int]): only draw segmentations larger than the threshold """ # don't need to do anything special frame_visualizer = Visualizer(frame, self.metadata) frame_visualizer.draw_sem_seg(sem_seg, area_threshold=None) return frame_visualizer.output def draw_panoptic_seg_predictions( self, frame, panoptic_seg, segments_info, area_threshold=None, alpha=0.5 ): frame_visualizer = Visualizer(frame, self.metadata) pred = _PanopticPrediction(panoptic_seg, segments_info, self.metadata) if self._instance_mode == ColorMode.IMAGE_BW: frame_visualizer.output.reset_image( frame_visualizer._create_grayscale_image(pred.non_empty_mask()) ) # draw mask for all semantic segments first i.e. "stuff" for mask, sinfo in pred.semantic_masks(): category_idx = sinfo["category_id"] try: mask_color = [x / 255 for x in self.metadata.stuff_colors[category_idx]] except AttributeError: mask_color = None frame_visualizer.draw_binary_mask( mask, color=mask_color, text=self.metadata.stuff_classes[category_idx], alpha=alpha, area_threshold=area_threshold, ) all_instances = list(pred.instance_masks()) if len(all_instances) == 0: return frame_visualizer.output # draw mask for all instances second masks, sinfo = list(zip(*all_instances)) num_instances = len(masks) masks_rles = mask_util.encode( np.asarray(np.asarray(masks).transpose(1, 2, 0), dtype=np.uint8, order="F") ) assert len(masks_rles) == num_instances category_ids = [x["category_id"] for x in sinfo] detected = [ _DetectedInstance(category_ids[i], bbox=None, mask_rle=masks_rles[i], color=None, ttl=8) for i in range(num_instances) ] colors = self._assign_colors(detected) labels = [self.metadata.thing_classes[k] for k in category_ids] frame_visualizer.overlay_instances( boxes=None, masks=masks, labels=labels, keypoints=None, assigned_colors=colors, alpha=alpha, ) return frame_visualizer.output def _assign_colors(self, instances): """ Naive tracking heuristics to assign same color to the same instance, will update the internal state of tracked instances. Returns: list[tuple[float]]: list of colors. """ # Compute iou with either boxes or masks: is_crowd = np.zeros((len(instances),), dtype=np.bool) if instances[0].bbox is None: assert instances[0].mask_rle is not None # use mask iou only when box iou is None # because box seems good enough rles_old = [x.mask_rle for x in self._old_instances] rles_new = [x.mask_rle for x in instances] ious = mask_util.iou(rles_old, rles_new, is_crowd) threshold = 0.5 else: boxes_old = [x.bbox for x in self._old_instances] boxes_new = [x.bbox for x in instances] ious = mask_util.iou(boxes_old, boxes_new, is_crowd) threshold = 0.6 if len(ious) == 0: ious = np.zeros((len(self._old_instances), len(instances)), dtype="float32") # Only allow matching instances of the same label: for old_idx, old in enumerate(self._old_instances): for new_idx, new in enumerate(instances): if old.label != new.label: ious[old_idx, new_idx] = 0 matched_new_per_old = np.asarray(ious).argmax(axis=1) max_iou_per_old = np.asarray(ious).max(axis=1) # Try to find match for each old instance: extra_instances = [] for idx, inst in enumerate(self._old_instances): if max_iou_per_old[idx] > threshold: newidx = matched_new_per_old[idx] if instances[newidx].color is None: instances[newidx].color = inst.color continue # If an old instance does not match any new instances, # keep it for the next frame in case it is just missed by the detector inst.ttl -= 1 if inst.ttl > 0: extra_instances.append(inst) # Assign random color to newly-detected instances: for inst in instances: if inst.color is None: inst.color = random_color(rgb=True, maximum=1) self._old_instances = instances[:] + extra_instances return [d.color for d in instances] def _assign_colors_by_id(self, instances: Instances) -> List: colors = [] untracked_ids = set(self._assigned_colors.keys()) for id in instances.ID: if id in self._assigned_colors: colors.append(self._color_pool[self._assigned_colors[id]]) untracked_ids.remove(id) else: assert ( len(self._color_idx_set) >= 1 ), f"Number of id exceeded maximum, \ max = {self._max_num_instances}" idx = self._color_idx_set.pop() color = self._color_pool[idx] self._assigned_colors[id] = idx colors.append(color) for id in untracked_ids: self._color_idx_set.add(self._assigned_colors[id]) del self._assigned_colors[id] return colors ================================================ FILE: detectron2/detectron2/utils/visualizer.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import colorsys import logging import math import numpy as np from enum import Enum, unique import cv2 import matplotlib as mpl import matplotlib.colors as mplc import matplotlib.figure as mplfigure import pycocotools.mask as mask_util import torch from matplotlib.backends.backend_agg import FigureCanvasAgg from PIL import Image from detectron2.data import MetadataCatalog from detectron2.structures import BitMasks, Boxes, BoxMode, Keypoints, PolygonMasks, RotatedBoxes from detectron2.utils.file_io import PathManager from .colormap import random_color logger = logging.getLogger(__name__) __all__ = ["ColorMode", "VisImage", "Visualizer"] _SMALL_OBJECT_AREA_THRESH = 1000 _LARGE_MASK_AREA_THRESH = 120000 _OFF_WHITE = (1.0, 1.0, 240.0 / 255) _BLACK = (0, 0, 0) _RED = (1.0, 0, 0) _KEYPOINT_THRESHOLD = 0.05 @unique class ColorMode(Enum): """ Enum of different color modes to use for instance visualizations. """ IMAGE = 0 """ Picks a random color for every instance and overlay segmentations with low opacity. """ SEGMENTATION = 1 """ Let instances of the same category have similar colors (from metadata.thing_colors), and overlay them with high opacity. This provides more attention on the quality of segmentation. """ IMAGE_BW = 2 """ Same as IMAGE, but convert all areas without masks to gray-scale. Only available for drawing per-instance mask predictions. """ class GenericMask: """ Attribute: polygons (list[ndarray]): list[ndarray]: polygons for this mask. Each ndarray has format [x, y, x, y, ...] mask (ndarray): a binary mask """ def __init__(self, mask_or_polygons, height, width): self._mask = self._polygons = self._has_holes = None self.height = height self.width = width m = mask_or_polygons if isinstance(m, dict): # RLEs assert "counts" in m and "size" in m if isinstance(m["counts"], list): # uncompressed RLEs h, w = m["size"] assert h == height and w == width m = mask_util.frPyObjects(m, h, w) self._mask = mask_util.decode(m)[:, :] return if isinstance(m, list): # list[ndarray] self._polygons = [np.asarray(x).reshape(-1) for x in m] return if isinstance(m, np.ndarray): # assumed to be a binary mask assert m.shape[1] != 2, m.shape assert m.shape == ( height, width, ), f"mask shape: {m.shape}, target dims: {height}, {width}" self._mask = m.astype("uint8") return raise ValueError("GenericMask cannot handle object {} of type '{}'".format(m, type(m))) @property def mask(self): if self._mask is None: self._mask = self.polygons_to_mask(self._polygons) return self._mask @property def polygons(self): if self._polygons is None: self._polygons, self._has_holes = self.mask_to_polygons(self._mask) return self._polygons @property def has_holes(self): if self._has_holes is None: if self._mask is not None: self._polygons, self._has_holes = self.mask_to_polygons(self._mask) else: self._has_holes = False # if original format is polygon, does not have holes return self._has_holes def mask_to_polygons(self, mask): # cv2.RETR_CCOMP flag retrieves all the contours and arranges them to a 2-level # hierarchy. External contours (boundary) of the object are placed in hierarchy-1. # Internal contours (holes) are placed in hierarchy-2. # cv2.CHAIN_APPROX_NONE flag gets vertices of polygons from contours. mask = np.ascontiguousarray(mask) # some versions of cv2 does not support incontiguous arr res = cv2.findContours(mask.astype("uint8"), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE) hierarchy = res[-1] if hierarchy is None: # empty mask return [], False has_holes = (hierarchy.reshape(-1, 4)[:, 3] >= 0).sum() > 0 res = res[-2] res = [x.flatten() for x in res] # These coordinates from OpenCV are integers in range [0, W-1 or H-1]. # We add 0.5 to turn them into real-value coordinate space. A better solution # would be to first +0.5 and then dilate the returned polygon by 0.5. res = [x + 0.5 for x in res if len(x) >= 6] return res, has_holes def polygons_to_mask(self, polygons): rle = mask_util.frPyObjects(polygons, self.height, self.width) rle = mask_util.merge(rle) return mask_util.decode(rle)[:, :] def area(self): return self.mask.sum() def bbox(self): p = mask_util.frPyObjects(self.polygons, self.height, self.width) p = mask_util.merge(p) bbox = mask_util.toBbox(p) bbox[2] += bbox[0] bbox[3] += bbox[1] return bbox class _PanopticPrediction: """ Unify different panoptic annotation/prediction formats """ def __init__(self, panoptic_seg, segments_info, metadata=None): if segments_info is None: assert metadata is not None # If "segments_info" is None, we assume "panoptic_img" is a # H*W int32 image storing the panoptic_id in the format of # category_id * label_divisor + instance_id. We reserve -1 for # VOID label. label_divisor = metadata.label_divisor segments_info = [] for panoptic_label in np.unique(panoptic_seg.numpy()): if panoptic_label == -1: # VOID region. continue pred_class = panoptic_label // label_divisor isthing = pred_class in metadata.thing_dataset_id_to_contiguous_id.values() segments_info.append( { "id": int(panoptic_label), "category_id": int(pred_class), "isthing": bool(isthing), } ) del metadata self._seg = panoptic_seg self._sinfo = {s["id"]: s for s in segments_info} # seg id -> seg info segment_ids, areas = torch.unique(panoptic_seg, sorted=True, return_counts=True) areas = areas.numpy() sorted_idxs = np.argsort(-areas) self._seg_ids, self._seg_areas = segment_ids[sorted_idxs], areas[sorted_idxs] self._seg_ids = self._seg_ids.tolist() for sid, area in zip(self._seg_ids, self._seg_areas): if sid in self._sinfo: self._sinfo[sid]["area"] = float(area) def non_empty_mask(self): """ Returns: (H, W) array, a mask for all pixels that have a prediction """ empty_ids = [] for id in self._seg_ids: if id not in self._sinfo: empty_ids.append(id) if len(empty_ids) == 0: return np.zeros(self._seg.shape, dtype=np.uint8) assert ( len(empty_ids) == 1 ), ">1 ids corresponds to no labels. This is currently not supported" return (self._seg != empty_ids[0]).numpy().astype(np.bool) def semantic_masks(self): for sid in self._seg_ids: sinfo = self._sinfo.get(sid) if sinfo is None or sinfo["isthing"]: # Some pixels (e.g. id 0 in PanopticFPN) have no instance or semantic predictions. continue yield (self._seg == sid).numpy().astype(np.bool), sinfo def instance_masks(self): for sid in self._seg_ids: sinfo = self._sinfo.get(sid) if sinfo is None or not sinfo["isthing"]: continue mask = (self._seg == sid).numpy().astype(np.bool) if mask.sum() > 0: yield mask, sinfo def _create_text_labels(classes, scores, class_names, is_crowd=None): """ Args: classes (list[int] or None): scores (list[float] or None): class_names (list[str] or None): is_crowd (list[bool] or None): Returns: list[str] or None """ labels = None if classes is not None: if class_names is not None and len(class_names) > 0: labels = [class_names[i] for i in classes] else: labels = [str(i) for i in classes] if scores is not None: if labels is None: labels = ["{:.0f}%".format(s * 100) for s in scores] else: labels = ["{} {:.0f}%".format(l, s * 100) for l, s in zip(labels, scores)] if labels is not None and is_crowd is not None: labels = [l + ("|crowd" if crowd else "") for l, crowd in zip(labels, is_crowd)] return labels class VisImage: def __init__(self, img, scale=1.0): """ Args: img (ndarray): an RGB image of shape (H, W, 3) in range [0, 255]. scale (float): scale the input image """ self.img = img self.scale = scale self.width, self.height = img.shape[1], img.shape[0] self._setup_figure(img) def _setup_figure(self, img): """ Args: Same as in :meth:`__init__()`. Returns: fig (matplotlib.pyplot.figure): top level container for all the image plot elements. ax (matplotlib.pyplot.Axes): contains figure elements and sets the coordinate system. """ fig = mplfigure.Figure(frameon=False) self.dpi = fig.get_dpi() # add a small 1e-2 to avoid precision lost due to matplotlib's truncation # (https://github.com/matplotlib/matplotlib/issues/15363) fig.set_size_inches( (self.width * self.scale + 1e-2) / self.dpi, (self.height * self.scale + 1e-2) / self.dpi, ) self.canvas = FigureCanvasAgg(fig) # self.canvas = mpl.backends.backend_cairo.FigureCanvasCairo(fig) ax = fig.add_axes([0.0, 0.0, 1.0, 1.0]) ax.axis("off") self.fig = fig self.ax = ax self.reset_image(img) def reset_image(self, img): """ Args: img: same as in __init__ """ img = img.astype("uint8") self.ax.imshow(img, extent=(0, self.width, self.height, 0), interpolation="nearest") def save(self, filepath): """ Args: filepath (str): a string that contains the absolute path, including the file name, where the visualized image will be saved. """ self.fig.savefig(filepath) def get_image(self): """ Returns: ndarray: the visualized image of shape (H, W, 3) (RGB) in uint8 type. The shape is scaled w.r.t the input image using the given `scale` argument. """ canvas = self.canvas s, (width, height) = canvas.print_to_buffer() # buf = io.BytesIO() # works for cairo backend # canvas.print_rgba(buf) # width, height = self.width, self.height # s = buf.getvalue() buffer = np.frombuffer(s, dtype="uint8") img_rgba = buffer.reshape(height, width, 4) rgb, alpha = np.split(img_rgba, [3], axis=2) return rgb.astype("uint8") class Visualizer: """ Visualizer that draws data about detection/segmentation on images. It contains methods like `draw_{text,box,circle,line,binary_mask,polygon}` that draw primitive objects to images, as well as high-level wrappers like `draw_{instance_predictions,sem_seg,panoptic_seg_predictions,dataset_dict}` that draw composite data in some pre-defined style. Note that the exact visualization style for the high-level wrappers are subject to change. Style such as color, opacity, label contents, visibility of labels, or even the visibility of objects themselves (e.g. when the object is too small) may change according to different heuristics, as long as the results still look visually reasonable. To obtain a consistent style, you can implement custom drawing functions with the abovementioned primitive methods instead. If you need more customized visualization styles, you can process the data yourself following their format documented in tutorials (:doc:`/tutorials/models`, :doc:`/tutorials/datasets`). This class does not intend to satisfy everyone's preference on drawing styles. This visualizer focuses on high rendering quality rather than performance. It is not designed to be used for real-time applications. """ # TODO implement a fast, rasterized version using OpenCV def __init__(self, img_rgb, metadata=None, scale=1.0, instance_mode=ColorMode.IMAGE): """ Args: img_rgb: a numpy array of shape (H, W, C), where H and W correspond to the height and width of the image respectively. C is the number of color channels. The image is required to be in RGB format since that is a requirement of the Matplotlib library. The image is also expected to be in the range [0, 255]. metadata (Metadata): dataset metadata (e.g. class names and colors) instance_mode (ColorMode): defines one of the pre-defined style for drawing instances on an image. """ self.img = np.asarray(img_rgb).clip(0, 255).astype(np.uint8) if metadata is None: metadata = MetadataCatalog.get("__nonexist__") self.metadata = metadata self.output = VisImage(self.img, scale=scale) self.cpu_device = torch.device("cpu") # too small texts are useless, therefore clamp to 9 self._default_font_size = max( np.sqrt(self.output.height * self.output.width) // 90, 10 // scale ) self._instance_mode = instance_mode self.keypoint_threshold = _KEYPOINT_THRESHOLD def draw_instance_predictions(self, predictions): """ Draw instance-level prediction results on an image. Args: predictions (Instances): the output of an instance detection/segmentation model. Following fields will be used to draw: "pred_boxes", "pred_classes", "scores", "pred_masks" (or "pred_masks_rle"). Returns: output (VisImage): image object with visualizations. """ boxes = predictions.pred_boxes if predictions.has("pred_boxes") else None scores = predictions.scores if predictions.has("scores") else None classes = predictions.pred_classes.tolist() if predictions.has("pred_classes") else None labels = _create_text_labels(classes, scores, self.metadata.get("thing_classes", None)) keypoints = predictions.pred_keypoints if predictions.has("pred_keypoints") else None if predictions.has("pred_masks"): masks = np.asarray(predictions.pred_masks) masks = [GenericMask(x, self.output.height, self.output.width) for x in masks] else: masks = None if self._instance_mode == ColorMode.SEGMENTATION and self.metadata.get("thing_colors"): colors = [ self._jitter([x / 255 for x in self.metadata.thing_colors[c]]) for c in classes ] alpha = 0.8 else: colors = None alpha = 0.5 if self._instance_mode == ColorMode.IMAGE_BW: self.output.reset_image( self._create_grayscale_image( (predictions.pred_masks.any(dim=0) > 0).numpy() if predictions.has("pred_masks") else None ) ) alpha = 0.3 self.overlay_instances( masks=masks, boxes=boxes, labels=labels, keypoints=keypoints, assigned_colors=colors, alpha=alpha, ) return self.output def draw_sem_seg(self, sem_seg, area_threshold=None, alpha=0.8): """ Draw semantic segmentation predictions/labels. Args: sem_seg (Tensor or ndarray): the segmentation of shape (H, W). Each value is the integer label of the pixel. area_threshold (int): segments with less than `area_threshold` are not drawn. alpha (float): the larger it is, the more opaque the segmentations are. Returns: output (VisImage): image object with visualizations. """ if isinstance(sem_seg, torch.Tensor): sem_seg = sem_seg.numpy() labels, areas = np.unique(sem_seg, return_counts=True) sorted_idxs = np.argsort(-areas).tolist() labels = labels[sorted_idxs] for label in filter(lambda l: l < len(self.metadata.stuff_classes), labels): try: mask_color = [x / 255 for x in self.metadata.stuff_colors[label]] except (AttributeError, IndexError): mask_color = None binary_mask = (sem_seg == label).astype(np.uint8) text = self.metadata.stuff_classes[label] self.draw_binary_mask( binary_mask, color=mask_color, edge_color=_OFF_WHITE, text=text, alpha=alpha, area_threshold=area_threshold, ) return self.output def draw_panoptic_seg(self, panoptic_seg, segments_info, area_threshold=None, alpha=0.7): """ Draw panoptic prediction annotations or results. Args: panoptic_seg (Tensor): of shape (height, width) where the values are ids for each segment. segments_info (list[dict] or None): Describe each segment in `panoptic_seg`. If it is a ``list[dict]``, each dict contains keys "id", "category_id". If None, category id of each pixel is computed by ``pixel // metadata.label_divisor``. area_threshold (int): stuff segments with less than `area_threshold` are not drawn. Returns: output (VisImage): image object with visualizations. """ pred = _PanopticPrediction(panoptic_seg, segments_info, self.metadata) if self._instance_mode == ColorMode.IMAGE_BW: self.output.reset_image(self._create_grayscale_image(pred.non_empty_mask())) # draw mask for all semantic segments first i.e. "stuff" for mask, sinfo in pred.semantic_masks(): category_idx = sinfo["category_id"] try: mask_color = [x / 255 for x in self.metadata.stuff_colors[category_idx]] except AttributeError: mask_color = None text = self.metadata.stuff_classes[category_idx] self.draw_binary_mask( mask, color=mask_color, edge_color=_OFF_WHITE, text=text, alpha=alpha, area_threshold=area_threshold, ) # draw mask for all instances second all_instances = list(pred.instance_masks()) if len(all_instances) == 0: return self.output masks, sinfo = list(zip(*all_instances)) category_ids = [x["category_id"] for x in sinfo] try: scores = [x["score"] for x in sinfo] except KeyError: scores = None labels = _create_text_labels( category_ids, scores, self.metadata.thing_classes, [x.get("iscrowd", 0) for x in sinfo] ) try: colors = [ self._jitter([x / 255 for x in self.metadata.thing_colors[c]]) for c in category_ids ] except AttributeError: colors = None self.overlay_instances(masks=masks, labels=labels, assigned_colors=colors, alpha=alpha) return self.output draw_panoptic_seg_predictions = draw_panoptic_seg # backward compatibility def draw_dataset_dict(self, dic): """ Draw annotations/segmentaions in Detectron2 Dataset format. Args: dic (dict): annotation/segmentation data of one image, in Detectron2 Dataset format. Returns: output (VisImage): image object with visualizations. """ annos = dic.get("annotations", None) if annos: if "segmentation" in annos[0]: masks = [x["segmentation"] for x in annos] else: masks = None if "keypoints" in annos[0]: keypts = [x["keypoints"] for x in annos] keypts = np.array(keypts).reshape(len(annos), -1, 3) else: keypts = None boxes = [ BoxMode.convert(x["bbox"], x["bbox_mode"], BoxMode.XYXY_ABS) if len(x["bbox"]) == 4 else x["bbox"] for x in annos ] colors = None category_ids = [x["category_id"] for x in annos] if self._instance_mode == ColorMode.SEGMENTATION and self.metadata.get("thing_colors"): colors = [ self._jitter([x / 255 for x in self.metadata.thing_colors[c]]) for c in category_ids ] names = self.metadata.get("thing_classes", None) labels = _create_text_labels( category_ids, scores=None, class_names=names, is_crowd=[x.get("iscrowd", 0) for x in annos], ) self.overlay_instances( labels=labels, boxes=boxes, masks=masks, keypoints=keypts, assigned_colors=colors ) sem_seg = dic.get("sem_seg", None) if sem_seg is None and "sem_seg_file_name" in dic: with PathManager.open(dic["sem_seg_file_name"], "rb") as f: sem_seg = Image.open(f) sem_seg = np.asarray(sem_seg, dtype="uint8") if sem_seg is not None: self.draw_sem_seg(sem_seg, area_threshold=0, alpha=0.5) pan_seg = dic.get("pan_seg", None) if pan_seg is None and "pan_seg_file_name" in dic: with PathManager.open(dic["pan_seg_file_name"], "rb") as f: pan_seg = Image.open(f) pan_seg = np.asarray(pan_seg) from panopticapi.utils import rgb2id pan_seg = rgb2id(pan_seg) if pan_seg is not None: segments_info = dic["segments_info"] pan_seg = torch.tensor(pan_seg) self.draw_panoptic_seg(pan_seg, segments_info, area_threshold=0, alpha=0.5) return self.output def overlay_instances( self, *, boxes=None, labels=None, masks=None, keypoints=None, assigned_colors=None, alpha=0.5, ): """ Args: boxes (Boxes, RotatedBoxes or ndarray): either a :class:`Boxes`, or an Nx4 numpy array of XYXY_ABS format for the N objects in a single image, or a :class:`RotatedBoxes`, or an Nx5 numpy array of (x_center, y_center, width, height, angle_degrees) format for the N objects in a single image, labels (list[str]): the text to be displayed for each instance. masks (masks-like object): Supported types are: * :class:`detectron2.structures.PolygonMasks`, :class:`detectron2.structures.BitMasks`. * list[list[ndarray]]: contains the segmentation masks for all objects in one image. The first level of the list corresponds to individual instances. The second level to all the polygon that compose the instance, and the third level to the polygon coordinates. The third level should have the format of [x0, y0, x1, y1, ..., xn, yn] (n >= 3). * list[ndarray]: each ndarray is a binary mask of shape (H, W). * list[dict]: each dict is a COCO-style RLE. keypoints (Keypoint or array like): an array-like object of shape (N, K, 3), where the N is the number of instances and K is the number of keypoints. The last dimension corresponds to (x, y, visibility or score). assigned_colors (list[matplotlib.colors]): a list of colors, where each color corresponds to each mask or box in the image. Refer to 'matplotlib.colors' for full list of formats that the colors are accepted in. Returns: output (VisImage): image object with visualizations. """ num_instances = 0 if boxes is not None: boxes = self._convert_boxes(boxes) num_instances = len(boxes) if masks is not None: masks = self._convert_masks(masks) if num_instances: assert len(masks) == num_instances else: num_instances = len(masks) if keypoints is not None: if num_instances: assert len(keypoints) == num_instances else: num_instances = len(keypoints) keypoints = self._convert_keypoints(keypoints) if labels is not None: assert len(labels) == num_instances if assigned_colors is None: assigned_colors = [random_color(rgb=True, maximum=1) for _ in range(num_instances)] if num_instances == 0: return self.output if boxes is not None and boxes.shape[1] == 5: return self.overlay_rotated_instances( boxes=boxes, labels=labels, assigned_colors=assigned_colors ) # Display in largest to smallest order to reduce occlusion. areas = None if boxes is not None: areas = np.prod(boxes[:, 2:] - boxes[:, :2], axis=1) elif masks is not None: areas = np.asarray([x.area() for x in masks]) if areas is not None: sorted_idxs = np.argsort(-areas).tolist() # Re-order overlapped instances in descending order. boxes = boxes[sorted_idxs] if boxes is not None else None labels = [labels[k] for k in sorted_idxs] if labels is not None else None masks = [masks[idx] for idx in sorted_idxs] if masks is not None else None assigned_colors = [assigned_colors[idx] for idx in sorted_idxs] keypoints = keypoints[sorted_idxs] if keypoints is not None else None for i in range(num_instances): color = assigned_colors[i] if boxes is not None: self.draw_box(boxes[i], edge_color=color) if masks is not None: for segment in masks[i].polygons: self.draw_polygon(segment.reshape(-1, 2), color, alpha=alpha) if labels is not None: # first get a box if boxes is not None: x0, y0, x1, y1 = boxes[i] text_pos = (x0, y0) # if drawing boxes, put text on the box corner. horiz_align = "left" elif masks is not None: # skip small mask without polygon if len(masks[i].polygons) == 0: continue x0, y0, x1, y1 = masks[i].bbox() # draw text in the center (defined by median) when box is not drawn # median is less sensitive to outliers. text_pos = np.median(masks[i].mask.nonzero(), axis=1)[::-1] horiz_align = "center" else: continue # drawing the box confidence for keypoints isn't very useful. # for small objects, draw text at the side to avoid occlusion instance_area = (y1 - y0) * (x1 - x0) if ( instance_area < _SMALL_OBJECT_AREA_THRESH * self.output.scale or y1 - y0 < 40 * self.output.scale ): if y1 >= self.output.height - 5: text_pos = (x1, y0) else: text_pos = (x0, y1) height_ratio = (y1 - y0) / np.sqrt(self.output.height * self.output.width) lighter_color = self._change_color_brightness(color, brightness_factor=0.7) font_size = ( np.clip((height_ratio - 0.02) / 0.08 + 1, 1.2, 2) * 0.5 * self._default_font_size ) self.draw_text( labels[i], text_pos, color=lighter_color, horizontal_alignment=horiz_align, font_size=font_size, ) # draw keypoints if keypoints is not None: for keypoints_per_instance in keypoints: self.draw_and_connect_keypoints(keypoints_per_instance) return self.output def overlay_rotated_instances(self, boxes=None, labels=None, assigned_colors=None): """ Args: boxes (ndarray): an Nx5 numpy array of (x_center, y_center, width, height, angle_degrees) format for the N objects in a single image. labels (list[str]): the text to be displayed for each instance. assigned_colors (list[matplotlib.colors]): a list of colors, where each color corresponds to each mask or box in the image. Refer to 'matplotlib.colors' for full list of formats that the colors are accepted in. Returns: output (VisImage): image object with visualizations. """ num_instances = len(boxes) if assigned_colors is None: assigned_colors = [random_color(rgb=True, maximum=1) for _ in range(num_instances)] if num_instances == 0: return self.output # Display in largest to smallest order to reduce occlusion. if boxes is not None: areas = boxes[:, 2] * boxes[:, 3] sorted_idxs = np.argsort(-areas).tolist() # Re-order overlapped instances in descending order. boxes = boxes[sorted_idxs] labels = [labels[k] for k in sorted_idxs] if labels is not None else None colors = [assigned_colors[idx] for idx in sorted_idxs] for i in range(num_instances): self.draw_rotated_box_with_label( boxes[i], edge_color=colors[i], label=labels[i] if labels is not None else None ) return self.output def draw_and_connect_keypoints(self, keypoints): """ Draws keypoints of an instance and follows the rules for keypoint connections to draw lines between appropriate keypoints. This follows color heuristics for line color. Args: keypoints (Tensor): a tensor of shape (K, 3), where K is the number of keypoints and the last dimension corresponds to (x, y, probability). Returns: output (VisImage): image object with visualizations. """ visible = {} keypoint_names = self.metadata.get("keypoint_names") for idx, keypoint in enumerate(keypoints): # draw keypoint x, y, prob = keypoint if prob > self.keypoint_threshold: self.draw_circle((x, y), color=_RED) if keypoint_names: keypoint_name = keypoint_names[idx] visible[keypoint_name] = (x, y) if self.metadata.get("keypoint_connection_rules"): for kp0, kp1, color in self.metadata.keypoint_connection_rules: if kp0 in visible and kp1 in visible: x0, y0 = visible[kp0] x1, y1 = visible[kp1] color = tuple(x / 255.0 for x in color) self.draw_line([x0, x1], [y0, y1], color=color) # draw lines from nose to mid-shoulder and mid-shoulder to mid-hip # Note that this strategy is specific to person keypoints. # For other keypoints, it should just do nothing try: ls_x, ls_y = visible["left_shoulder"] rs_x, rs_y = visible["right_shoulder"] mid_shoulder_x, mid_shoulder_y = (ls_x + rs_x) / 2, (ls_y + rs_y) / 2 except KeyError: pass else: # draw line from nose to mid-shoulder nose_x, nose_y = visible.get("nose", (None, None)) if nose_x is not None: self.draw_line([nose_x, mid_shoulder_x], [nose_y, mid_shoulder_y], color=_RED) try: # draw line from mid-shoulder to mid-hip lh_x, lh_y = visible["left_hip"] rh_x, rh_y = visible["right_hip"] except KeyError: pass else: mid_hip_x, mid_hip_y = (lh_x + rh_x) / 2, (lh_y + rh_y) / 2 self.draw_line([mid_hip_x, mid_shoulder_x], [mid_hip_y, mid_shoulder_y], color=_RED) return self.output """ Primitive drawing functions: """ def draw_text( self, text, position, *, font_size=None, color="g", horizontal_alignment="center", rotation=0, ): """ Args: text (str): class label position (tuple): a tuple of the x and y coordinates to place text on image. font_size (int, optional): font of the text. If not provided, a font size proportional to the image width is calculated and used. color: color of the text. Refer to `matplotlib.colors` for full list of formats that are accepted. horizontal_alignment (str): see `matplotlib.text.Text` rotation: rotation angle in degrees CCW Returns: output (VisImage): image object with text drawn. """ if not font_size: font_size = self._default_font_size # since the text background is dark, we don't want the text to be dark color = np.maximum(list(mplc.to_rgb(color)), 0.2) color[np.argmax(color)] = max(0.8, np.max(color)) x, y = position self.output.ax.text( x, y, text, size=font_size * self.output.scale, family="sans-serif", bbox={"facecolor": "black", "alpha": 0.8, "pad": 0.7, "edgecolor": "none"}, verticalalignment="top", horizontalalignment=horizontal_alignment, color=color, zorder=10, rotation=rotation, ) return self.output def draw_box(self, box_coord, alpha=0.5, edge_color="g", line_style="-"): """ Args: box_coord (tuple): a tuple containing x0, y0, x1, y1 coordinates, where x0 and y0 are the coordinates of the image's top left corner. x1 and y1 are the coordinates of the image's bottom right corner. alpha (float): blending efficient. Smaller values lead to more transparent masks. edge_color: color of the outline of the box. Refer to `matplotlib.colors` for full list of formats that are accepted. line_style (string): the string to use to create the outline of the boxes. Returns: output (VisImage): image object with box drawn. """ x0, y0, x1, y1 = box_coord width = x1 - x0 height = y1 - y0 linewidth = max(self._default_font_size / 4, 1) self.output.ax.add_patch( mpl.patches.Rectangle( (x0, y0), width, height, fill=False, edgecolor=edge_color, linewidth=linewidth * self.output.scale, alpha=alpha, linestyle=line_style, ) ) return self.output def draw_rotated_box_with_label( self, rotated_box, alpha=0.5, edge_color="g", line_style="-", label=None ): """ Draw a rotated box with label on its top-left corner. Args: rotated_box (tuple): a tuple containing (cnt_x, cnt_y, w, h, angle), where cnt_x and cnt_y are the center coordinates of the box. w and h are the width and height of the box. angle represents how many degrees the box is rotated CCW with regard to the 0-degree box. alpha (float): blending efficient. Smaller values lead to more transparent masks. edge_color: color of the outline of the box. Refer to `matplotlib.colors` for full list of formats that are accepted. line_style (string): the string to use to create the outline of the boxes. label (string): label for rotated box. It will not be rendered when set to None. Returns: output (VisImage): image object with box drawn. """ cnt_x, cnt_y, w, h, angle = rotated_box area = w * h # use thinner lines when the box is small linewidth = self._default_font_size / ( 6 if area < _SMALL_OBJECT_AREA_THRESH * self.output.scale else 3 ) theta = angle * math.pi / 180.0 c = math.cos(theta) s = math.sin(theta) rect = [(-w / 2, h / 2), (-w / 2, -h / 2), (w / 2, -h / 2), (w / 2, h / 2)] # x: left->right ; y: top->down rotated_rect = [(s * yy + c * xx + cnt_x, c * yy - s * xx + cnt_y) for (xx, yy) in rect] for k in range(4): j = (k + 1) % 4 self.draw_line( [rotated_rect[k][0], rotated_rect[j][0]], [rotated_rect[k][1], rotated_rect[j][1]], color=edge_color, linestyle="--" if k == 1 else line_style, linewidth=linewidth, ) if label is not None: text_pos = rotated_rect[1] # topleft corner height_ratio = h / np.sqrt(self.output.height * self.output.width) label_color = self._change_color_brightness(edge_color, brightness_factor=0.7) font_size = ( np.clip((height_ratio - 0.02) / 0.08 + 1, 1.2, 2) * 0.5 * self._default_font_size ) self.draw_text(label, text_pos, color=label_color, font_size=font_size, rotation=angle) return self.output def draw_circle(self, circle_coord, color, radius=3): """ Args: circle_coord (list(int) or tuple(int)): contains the x and y coordinates of the center of the circle. color: color of the polygon. Refer to `matplotlib.colors` for a full list of formats that are accepted. radius (int): radius of the circle. Returns: output (VisImage): image object with box drawn. """ x, y = circle_coord self.output.ax.add_patch( mpl.patches.Circle(circle_coord, radius=radius, fill=True, color=color) ) return self.output def draw_line(self, x_data, y_data, color, linestyle="-", linewidth=None): """ Args: x_data (list[int]): a list containing x values of all the points being drawn. Length of list should match the length of y_data. y_data (list[int]): a list containing y values of all the points being drawn. Length of list should match the length of x_data. color: color of the line. Refer to `matplotlib.colors` for a full list of formats that are accepted. linestyle: style of the line. Refer to `matplotlib.lines.Line2D` for a full list of formats that are accepted. linewidth (float or None): width of the line. When it's None, a default value will be computed and used. Returns: output (VisImage): image object with line drawn. """ if linewidth is None: linewidth = self._default_font_size / 3 linewidth = max(linewidth, 1) self.output.ax.add_line( mpl.lines.Line2D( x_data, y_data, linewidth=linewidth * self.output.scale, color=color, linestyle=linestyle, ) ) return self.output def draw_binary_mask( self, binary_mask, color=None, *, edge_color=None, text=None, alpha=0.5, area_threshold=10 ): """ Args: binary_mask (ndarray): numpy array of shape (H, W), where H is the image height and W is the image width. Each value in the array is either a 0 or 1 value of uint8 type. color: color of the mask. Refer to `matplotlib.colors` for a full list of formats that are accepted. If None, will pick a random color. edge_color: color of the polygon edges. Refer to `matplotlib.colors` for a full list of formats that are accepted. text (str): if None, will be drawn on the object alpha (float): blending efficient. Smaller values lead to more transparent masks. area_threshold (float): a connected component smaller than this area will not be shown. Returns: output (VisImage): image object with mask drawn. """ if color is None: color = random_color(rgb=True, maximum=1) color = mplc.to_rgb(color) has_valid_segment = False binary_mask = binary_mask.astype("uint8") # opencv needs uint8 mask = GenericMask(binary_mask, self.output.height, self.output.width) shape2d = (binary_mask.shape[0], binary_mask.shape[1]) if not mask.has_holes: # draw polygons for regular masks for segment in mask.polygons: area = mask_util.area(mask_util.frPyObjects([segment], shape2d[0], shape2d[1])) if area < (area_threshold or 0): continue has_valid_segment = True segment = segment.reshape(-1, 2) self.draw_polygon(segment, color=color, edge_color=edge_color, alpha=alpha) else: # TODO: Use Path/PathPatch to draw vector graphics: # https://stackoverflow.com/questions/8919719/how-to-plot-a-complex-polygon rgba = np.zeros(shape2d + (4,), dtype="float32") rgba[:, :, :3] = color rgba[:, :, 3] = (mask.mask == 1).astype("float32") * alpha has_valid_segment = True self.output.ax.imshow(rgba, extent=(0, self.output.width, self.output.height, 0)) if text is not None and has_valid_segment: lighter_color = self._change_color_brightness(color, brightness_factor=0.7) self._draw_text_in_mask(binary_mask, text, lighter_color) return self.output def draw_soft_mask(self, soft_mask, color=None, *, text=None, alpha=0.5): """ Args: soft_mask (ndarray): float array of shape (H, W), each value in [0, 1]. color: color of the mask. Refer to `matplotlib.colors` for a full list of formats that are accepted. If None, will pick a random color. text (str): if None, will be drawn on the object alpha (float): blending efficient. Smaller values lead to more transparent masks. Returns: output (VisImage): image object with mask drawn. """ if color is None: color = random_color(rgb=True, maximum=1) color = mplc.to_rgb(color) shape2d = (soft_mask.shape[0], soft_mask.shape[1]) rgba = np.zeros(shape2d + (4,), dtype="float32") rgba[:, :, :3] = color rgba[:, :, 3] = soft_mask * alpha self.output.ax.imshow(rgba, extent=(0, self.output.width, self.output.height, 0)) if text is not None: lighter_color = self._change_color_brightness(color, brightness_factor=0.7) binary_mask = (soft_mask > 0.5).astype("uint8") self._draw_text_in_mask(binary_mask, text, lighter_color) return self.output def draw_polygon(self, segment, color, edge_color=None, alpha=0.5): """ Args: segment: numpy array of shape Nx2, containing all the points in the polygon. color: color of the polygon. Refer to `matplotlib.colors` for a full list of formats that are accepted. edge_color: color of the polygon edges. Refer to `matplotlib.colors` for a full list of formats that are accepted. If not provided, a darker shade of the polygon color will be used instead. alpha (float): blending efficient. Smaller values lead to more transparent masks. Returns: output (VisImage): image object with polygon drawn. """ if edge_color is None: # make edge color darker than the polygon color if alpha > 0.8: edge_color = self._change_color_brightness(color, brightness_factor=-0.7) else: edge_color = color edge_color = mplc.to_rgb(edge_color) + (1,) polygon = mpl.patches.Polygon( segment, fill=True, facecolor=mplc.to_rgb(color) + (alpha,), edgecolor=edge_color, linewidth=max(self._default_font_size // 15 * self.output.scale, 1), ) self.output.ax.add_patch(polygon) return self.output """ Internal methods: """ def _jitter(self, color): """ Randomly modifies given color to produce a slightly different color than the color given. Args: color (tuple[double]): a tuple of 3 elements, containing the RGB values of the color picked. The values in the list are in the [0.0, 1.0] range. Returns: jittered_color (tuple[double]): a tuple of 3 elements, containing the RGB values of the color after being jittered. The values in the list are in the [0.0, 1.0] range. """ color = mplc.to_rgb(color) vec = np.random.rand(3) # better to do it in another color space vec = vec / np.linalg.norm(vec) * 0.5 res = np.clip(vec + color, 0, 1) return tuple(res) def _create_grayscale_image(self, mask=None): """ Create a grayscale version of the original image. The colors in masked area, if given, will be kept. """ img_bw = self.img.astype("f4").mean(axis=2) img_bw = np.stack([img_bw] * 3, axis=2) if mask is not None: img_bw[mask] = self.img[mask] return img_bw def _change_color_brightness(self, color, brightness_factor): """ Depending on the brightness_factor, gives a lighter or darker color i.e. a color with less or more saturation than the original color. Args: color: color of the polygon. Refer to `matplotlib.colors` for a full list of formats that are accepted. brightness_factor (float): a value in [-1.0, 1.0] range. A lightness factor of 0 will correspond to no change, a factor in [-1.0, 0) range will result in a darker color and a factor in (0, 1.0] range will result in a lighter color. Returns: modified_color (tuple[double]): a tuple containing the RGB values of the modified color. Each value in the tuple is in the [0.0, 1.0] range. """ assert brightness_factor >= -1.0 and brightness_factor <= 1.0 color = mplc.to_rgb(color) polygon_color = colorsys.rgb_to_hls(*mplc.to_rgb(color)) modified_lightness = polygon_color[1] + (brightness_factor * polygon_color[1]) modified_lightness = 0.0 if modified_lightness < 0.0 else modified_lightness modified_lightness = 1.0 if modified_lightness > 1.0 else modified_lightness modified_color = colorsys.hls_to_rgb(polygon_color[0], modified_lightness, polygon_color[2]) return modified_color def _convert_boxes(self, boxes): """ Convert different format of boxes to an NxB array, where B = 4 or 5 is the box dimension. """ if isinstance(boxes, Boxes) or isinstance(boxes, RotatedBoxes): return boxes.tensor.detach().numpy() else: return np.asarray(boxes) def _convert_masks(self, masks_or_polygons): """ Convert different format of masks or polygons to a tuple of masks and polygons. Returns: list[GenericMask]: """ m = masks_or_polygons if isinstance(m, PolygonMasks): m = m.polygons if isinstance(m, BitMasks): m = m.tensor.numpy() if isinstance(m, torch.Tensor): m = m.numpy() ret = [] for x in m: if isinstance(x, GenericMask): ret.append(x) else: ret.append(GenericMask(x, self.output.height, self.output.width)) return ret def _draw_text_in_mask(self, binary_mask, text, color): """ Find proper places to draw text given a binary mask. """ # TODO sometimes drawn on wrong objects. the heuristics here can improve. _num_cc, cc_labels, stats, centroids = cv2.connectedComponentsWithStats(binary_mask, 8) if stats[1:, -1].size == 0: return largest_component_id = np.argmax(stats[1:, -1]) + 1 # draw text on the largest component, as well as other very large components. for cid in range(1, _num_cc): if cid == largest_component_id or stats[cid, -1] > _LARGE_MASK_AREA_THRESH: # median is more stable than centroid # center = centroids[largest_component_id] center = np.median((cc_labels == cid).nonzero(), axis=1)[::-1] self.draw_text(text, center, color=color) def _convert_keypoints(self, keypoints): if isinstance(keypoints, Keypoints): keypoints = keypoints.tensor keypoints = np.asarray(keypoints) return keypoints def get_output(self): """ Returns: output (VisImage): the image output containing the visualizations added to the image. """ return self.output ================================================ FILE: detectron2/dev/README.md ================================================ ## Some scripts for developers to use, include: - `linter.sh`: lint the codebase before commit. - `run_{inference,instant}_tests.sh`: run inference/training for a few iterations. Note that these tests require 2 GPUs. - `parse_results.sh`: parse results from a log file. ================================================ FILE: detectron2/dev/linter.sh ================================================ #!/bin/bash -e # Copyright (c) Facebook, Inc. and its affiliates. # cd to detectron2 project root cd "$(dirname "${BASH_SOURCE[0]}")/.." { black --version | grep -E "22\." > /dev/null } || { echo "Linter requires 'black==22.*' !" exit 1 } ISORT_VERSION=$(isort --version-number) if [[ "$ISORT_VERSION" != 4.3* ]]; then echo "Linter requires isort==4.3.21 !" exit 1 fi set -v echo "Running isort ..." isort -y -sp . --atomic echo "Running black ..." black -l 100 . echo "Running flake8 ..." if [ -x "$(command -v flake8)" ]; then flake8 . else python3 -m flake8 . fi # echo "Running mypy ..." # Pytorch does not have enough type annotations # mypy detectron2/solver detectron2/structures detectron2/config echo "Running clang-format ..." find . -regex ".*\.\(cpp\|c\|cc\|cu\|cxx\|h\|hh\|hpp\|hxx\|tcc\|mm\|m\)" -print0 | xargs -0 clang-format -i command -v arc > /dev/null && arc lint ================================================ FILE: detectron2/dev/packaging/README.md ================================================ ## To build a cu101 wheel for release: ``` $ nvidia-docker run -it --storage-opt "size=20GB" --name pt pytorch/manylinux-cuda101 # inside the container: # git clone https://github.com/facebookresearch/detectron2/ # cd detectron2 # export CU_VERSION=cu101 D2_VERSION_SUFFIX= PYTHON_VERSION=3.7 PYTORCH_VERSION=1.8 # ./dev/packaging/build_wheel.sh ``` ## To build all wheels for combinations of CUDA and Python ``` ./dev/packaging/build_all_wheels.sh ./dev/packaging/gen_wheel_index.sh /path/to/wheels ``` ================================================ FILE: detectron2/dev/packaging/build_all_wheels.sh ================================================ #!/bin/bash -e # Copyright (c) Facebook, Inc. and its affiliates. [[ -d "dev/packaging" ]] || { echo "Please run this script at detectron2 root!" exit 1 } build_one() { cu=$1 pytorch_ver=$2 case "$cu" in cu*) container_name=manylinux-cuda${cu/cu/} ;; cpu) container_name=manylinux-cuda101 ;; *) echo "Unrecognized cu=$cu" exit 1 ;; esac echo "Launching container $container_name ..." container_id="$container_name"_"$cu"_"$pytorch_ver" py_versions=(3.7 3.8 3.9) for py in "${py_versions[@]}"; do docker run -itd \ --name "$container_id" \ --mount type=bind,source="$(pwd)",target=/detectron2 \ pytorch/$container_name cat </dev/null 2>&1 && pwd )" . "$script_dir/pkg_helpers.bash" echo "Build Settings:" echo "CU_VERSION: $CU_VERSION" # e.g. cu101 echo "D2_VERSION_SUFFIX: $D2_VERSION_SUFFIX" # e.g. +cu101 or "" echo "PYTHON_VERSION: $PYTHON_VERSION" # e.g. 3.7 echo "PYTORCH_VERSION: $PYTORCH_VERSION" # e.g. 1.4 setup_cuda setup_wheel_python yum install ninja-build -y ln -sv /usr/bin/ninja-build /usr/bin/ninja || true pip_install pip numpy -U pip_install "torch==$PYTORCH_VERSION" \ -f https://download.pytorch.org/whl/"$CU_VERSION"/torch_stable.html # use separate directories to allow parallel build BASE_BUILD_DIR=build/$CU_VERSION-py$PYTHON_VERSION-pt$PYTORCH_VERSION python setup.py \ build -b "$BASE_BUILD_DIR" \ bdist_wheel -b "$BASE_BUILD_DIR/build_dist" -d "wheels/$CU_VERSION/torch$PYTORCH_VERSION" rm -rf "$BASE_BUILD_DIR" ================================================ FILE: detectron2/dev/packaging/gen_install_table.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. # -*- coding: utf-8 -*- import argparse template = """
install
\
python -m pip install detectron2{d2_version} -f \\
  https://dl.fbaipublicfiles.com/detectron2/wheels/{cuda}/torch{torch}/index.html
""" CUDA_SUFFIX = { "11.3": "cu113", "11.1": "cu111", "11.0": "cu110", "10.2": "cu102", "10.1": "cu101", "10.0": "cu100", "9.2": "cu92", "cpu": "cpu", } def gen_header(torch_versions): return '' + "".join( [ ''.format(t) for t in torch_versions ] ) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--d2-version", help="detectron2 version number, default to empty") args = parser.parse_args() d2_version = f"=={args.d2_version}" if args.d2_version else "" all_versions = ( [("1.8", k) for k in ["11.1", "10.2", "10.1", "cpu"]] + [("1.9", k) for k in ["11.1", "10.2", "cpu"]] + [("1.10", k) for k in ["11.3", "11.1", "10.2", "cpu"]] ) torch_versions = sorted( {k[0] for k in all_versions}, key=lambda x: int(x.split(".")[1]), reverse=True ) cuda_versions = sorted( {k[1] for k in all_versions}, key=lambda x: float(x) if x != "cpu" else 0, reverse=True ) table = gen_header(torch_versions) for cu in cuda_versions: table += f""" """ cu_suffix = CUDA_SUFFIX[cu] for torch in torch_versions: if (torch, cu) in all_versions: cell = template.format(d2_version=d2_version, cuda=cu_suffix, torch=torch) else: cell = "" table += f""" """ table += "" table += "
CUDA torch {}
{cu}{cell}
" print(table) ================================================ FILE: detectron2/dev/packaging/gen_wheel_index.sh ================================================ #!/bin/bash -e # Copyright (c) Facebook, Inc. and its affiliates. root=$(readlink -f $1) if [[ -z "$root" ]]; then echo "Usage: ./gen_wheel_index.sh /absolute/path/to/wheels" exit fi export LC_ALL=C # reproducible sort # NOTE: all sort in this script might not work when xx.10 is released index=$root/index.html cd "$root" for cu in cpu cu92 cu100 cu101 cu102 cu110 cu111 cu113; do mkdir -p "$root/$cu" cd "$root/$cu" echo "Creating $PWD/index.html ..." # First sort by torch version, then stable sort by d2 version with unique. # As a result, the latest torch version for each d2 version is kept. for whl in $(find -type f -name '*.whl' -printf '%P\n' \ | sort -k 1 -r | sort -t '/' -k 2 --stable -r --unique); do echo "$whl
" done > index.html for torch in torch*; do cd "$root/$cu/$torch" # list all whl for each cuda,torch version echo "Creating $PWD/index.html ..." for whl in $(find . -type f -name '*.whl' -printf '%P\n' | sort -r); do echo "$whl
" done > index.html done done cd "$root" # Just list everything: echo "Creating $index ..." for whl in $(find . -type f -name '*.whl' -printf '%P\n' | sort -r); do echo "$whl
" done > "$index" ================================================ FILE: detectron2/dev/packaging/pkg_helpers.bash ================================================ #!/bin/bash -e # Copyright (c) Facebook, Inc. and its affiliates. # Function to retry functions that sometimes timeout or have flaky failures retry () { $* || (sleep 1 && $*) || (sleep 2 && $*) || (sleep 4 && $*) || (sleep 8 && $*) } # Install with pip a bit more robustly than the default pip_install() { retry pip install --progress-bar off "$@" } setup_cuda() { # Now work out the CUDA settings # Like other torch domain libraries, we choose common GPU architectures only. # See https://github.com/pytorch/pytorch/blob/master/torch/utils/cpp_extension.py # and https://github.com/pytorch/vision/blob/main/packaging/pkg_helpers.bash for reference. export FORCE_CUDA=1 case "$CU_VERSION" in cu113) export CUDA_HOME=/usr/local/cuda-11.3/ export TORCH_CUDA_ARCH_LIST="3.7;5.0;5.2;6.0;6.1+PTX;7.0;7.5+PTX;8.0;8.6+PTX" ;; cu112) export CUDA_HOME=/usr/local/cuda-11.2/ export TORCH_CUDA_ARCH_LIST="3.7;5.0;5.2;6.0;6.1+PTX;7.0;7.5+PTX;8.0;8.6+PTX" ;; cu111) export CUDA_HOME=/usr/local/cuda-11.1/ export TORCH_CUDA_ARCH_LIST="3.7;5.0;5.2;6.0;6.1+PTX;7.0;7.5+PTX;8.0;8.6+PTX" ;; cu110) export CUDA_HOME=/usr/local/cuda-11.0/ export TORCH_CUDA_ARCH_LIST="3.7;5.0;5.2;6.0;6.1+PTX;7.0;7.5+PTX;8.0+PTX" ;; cu102) export CUDA_HOME=/usr/local/cuda-10.2/ export TORCH_CUDA_ARCH_LIST="3.7;5.0;5.2;6.0;6.1+PTX;7.0;7.5+PTX" ;; cu101) export CUDA_HOME=/usr/local/cuda-10.1/ export TORCH_CUDA_ARCH_LIST="3.7;5.0;5.2;6.0;6.1+PTX;7.0;7.5+PTX" ;; cu100) export CUDA_HOME=/usr/local/cuda-10.0/ export TORCH_CUDA_ARCH_LIST="3.7;5.0;5.2;6.0;6.1+PTX;7.0;7.5+PTX" ;; cu92) export CUDA_HOME=/usr/local/cuda-9.2/ export TORCH_CUDA_ARCH_LIST="3.7;5.0;5.2;6.0;6.1+PTX;7.0+PTX" ;; cpu) unset FORCE_CUDA export CUDA_VISIBLE_DEVICES= ;; *) echo "Unrecognized CU_VERSION=$CU_VERSION" exit 1 ;; esac } setup_wheel_python() { case "$PYTHON_VERSION" in 3.7) python_abi=cp37-cp37m ;; 3.8) python_abi=cp38-cp38 ;; 3.9) python_abi=cp39-cp39 ;; *) echo "Unrecognized PYTHON_VERSION=$PYTHON_VERSION" exit 1 ;; esac export PATH="/opt/python/$python_abi/bin:$PATH" } ================================================ FILE: detectron2/dev/parse_results.sh ================================================ #!/bin/bash # Copyright (c) Facebook, Inc. and its affiliates. # A shell script that parses metrics from the log file. # Make it easier for developers to track performance of models. LOG="$1" if [[ -z "$LOG" ]]; then echo "Usage: $0 /path/to/log/file" exit 1 fi # [12/15 11:47:32] trainer INFO: Total training time: 12:15:04.446477 (0.4900 s / it) # [12/15 11:49:03] inference INFO: Total inference time: 0:01:25.326167 (0.13652186737060548 s / img per device, on 8 devices) # [12/15 11:49:03] inference INFO: Total inference pure compute time: ..... # training time trainspeed=$(grep -o 'Overall training.*' "$LOG" | grep -Eo '\(.*\)' | grep -o '[0-9\.]*') echo "Training speed: $trainspeed s/it" # inference time: there could be multiple inference during training inferencespeed=$(grep -o 'Total inference pure.*' "$LOG" | tail -n1 | grep -Eo '\(.*\)' | grep -o '[0-9\.]*' | head -n1) echo "Inference speed: $inferencespeed s/it" # [12/15 11:47:18] trainer INFO: eta: 0:00:00 iter: 90000 loss: 0.5407 (0.7256) loss_classifier: 0.1744 (0.2446) loss_box_reg: 0.0838 (0.1160) loss_mask: 0.2159 (0.2722) loss_objectness: 0.0244 (0.0429) loss_rpn_box_reg: 0.0279 (0.0500) time: 0.4487 (0.4899) data: 0.0076 (0.0975) lr: 0.000200 max mem: 4161 memory=$(grep -o 'max[_ ]mem: [0-9]*' "$LOG" | tail -n1 | grep -o '[0-9]*') echo "Training memory: $memory MB" echo "Easy to copypaste:" echo "$trainspeed","$inferencespeed","$memory" echo "------------------------------" # [12/26 17:26:32] engine.coco_evaluation: copypaste: Task: bbox # [12/26 17:26:32] engine.coco_evaluation: copypaste: AP,AP50,AP75,APs,APm,APl # [12/26 17:26:32] engine.coco_evaluation: copypaste: 0.0017,0.0024,0.0017,0.0005,0.0019,0.0011 # [12/26 17:26:32] engine.coco_evaluation: copypaste: Task: segm # [12/26 17:26:32] engine.coco_evaluation: copypaste: AP,AP50,AP75,APs,APm,APl # [12/26 17:26:32] engine.coco_evaluation: copypaste: 0.0014,0.0021,0.0016,0.0005,0.0016,0.0011 echo "COCO Results:" num_tasks=$(grep -o 'copypaste:.*Task.*' "$LOG" | sort -u | wc -l) # each task has 3 lines grep -o 'copypaste:.*' "$LOG" | cut -d ' ' -f 2- | tail -n $((num_tasks * 3)) ================================================ FILE: detectron2/dev/run_inference_tests.sh ================================================ #!/bin/bash -e # Copyright (c) Facebook, Inc. and its affiliates. BIN="python tools/train_net.py" OUTPUT="inference_test_output" NUM_GPUS=2 CFG_LIST=( "${@:1}" ) if [ ${#CFG_LIST[@]} -eq 0 ]; then CFG_LIST=( ./configs/quick_schedules/*inference_acc_test.yaml ) fi echo "========================================================================" echo "Configs to run:" echo "${CFG_LIST[@]}" echo "========================================================================" for cfg in "${CFG_LIST[@]}"; do echo "========================================================================" echo "Running $cfg ..." echo "========================================================================" $BIN \ --eval-only \ --num-gpus $NUM_GPUS \ --config-file "$cfg" \ OUTPUT_DIR $OUTPUT rm -rf $OUTPUT done echo "========================================================================" echo "Running demo.py ..." echo "========================================================================" DEMO_BIN="python demo/demo.py" COCO_DIR=datasets/coco/val2014 mkdir -pv $OUTPUT set -v $DEMO_BIN --config-file ./configs/quick_schedules/panoptic_fpn_R_50_inference_acc_test.yaml \ --input $COCO_DIR/COCO_val2014_0000001933* --output $OUTPUT rm -rf $OUTPUT ================================================ FILE: detectron2/dev/run_instant_tests.sh ================================================ #!/bin/bash -e # Copyright (c) Facebook, Inc. and its affiliates. BIN="python tools/train_net.py" OUTPUT="instant_test_output" NUM_GPUS=2 CFG_LIST=( "${@:1}" ) if [ ${#CFG_LIST[@]} -eq 0 ]; then CFG_LIST=( ./configs/quick_schedules/*instant_test.yaml ) fi echo "========================================================================" echo "Configs to run:" echo "${CFG_LIST[@]}" echo "========================================================================" for cfg in "${CFG_LIST[@]}"; do echo "========================================================================" echo "Running $cfg ..." echo "========================================================================" $BIN --num-gpus $NUM_GPUS --config-file "$cfg" \ SOLVER.IMS_PER_BATCH $(($NUM_GPUS * 2)) \ OUTPUT_DIR "$OUTPUT" rm -rf "$OUTPUT" done ================================================ FILE: detectron2/docker/Dockerfile ================================================ FROM nvidia/cuda:11.1.1-cudnn8-devel-ubuntu18.04 # use an older system (18.04) to avoid opencv incompatibility (issue#3524) ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y \ python3-opencv ca-certificates python3-dev git wget sudo ninja-build RUN ln -sv /usr/bin/python3 /usr/bin/python # create a non-root user ARG USER_ID=1000 RUN useradd -m --no-log-init --system --uid ${USER_ID} appuser -g sudo RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers USER appuser WORKDIR /home/appuser ENV PATH="/home/appuser/.local/bin:${PATH}" RUN wget https://bootstrap.pypa.io/pip/3.6/get-pip.py && \ python3 get-pip.py --user && \ rm get-pip.py # install dependencies # See https://pytorch.org/ for other options if you use a different version of CUDA RUN pip install --user tensorboard cmake onnx # cmake from apt-get is too old RUN pip install --user torch==1.10 torchvision==0.11.1 -f https://download.pytorch.org/whl/cu111/torch_stable.html RUN pip install --user 'git+https://github.com/facebookresearch/fvcore' # install detectron2 RUN git clone https://github.com/facebookresearch/detectron2 detectron2_repo # set FORCE_CUDA because during `docker build` cuda is not accessible ENV FORCE_CUDA="1" # This will by default build detectron2 for all common cuda architectures and take a lot more time, # because inside `docker build`, there is no way to tell which architecture will be used. ARG TORCH_CUDA_ARCH_LIST="Kepler;Kepler+Tesla;Maxwell;Maxwell+Tegra;Pascal;Volta;Turing" ENV TORCH_CUDA_ARCH_LIST="${TORCH_CUDA_ARCH_LIST}" RUN pip install --user -e detectron2_repo # Set a fixed model cache directory. ENV FVCORE_CACHE="/tmp" WORKDIR /home/appuser/detectron2_repo # run detectron2 under user "appuser": # wget http://images.cocodataset.org/val2017/000000439715.jpg -O input.jpg # python3 demo/demo.py \ #--config-file configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml \ #--input input.jpg --output outputs/ \ #--opts MODEL.WEIGHTS detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl ================================================ FILE: detectron2/docker/README.md ================================================ ## Use the container (with docker ≥ 19.03) ``` cd docker/ # Build: docker build --build-arg USER_ID=$UID -t detectron2:v0 . # Launch (require GPUs): docker run --gpus all -it \ --shm-size=8gb --env="DISPLAY" --volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \ --name=detectron2 detectron2:v0 # Grant docker access to host X server to show images xhost +local:`docker inspect --format='{{ .Config.Hostname }}' detectron2` ``` ## Use the container (with docker-compose ≥ 1.28.0) Install docker-compose and nvidia-docker-toolkit, then run: ``` cd docker && USER_ID=$UID docker-compose run detectron2 ``` ## Use the deployment container (to test C++ examples) After building the base detectron2 container as above, do: ``` # Build: docker build -t detectron2-deploy:v0 -f deploy.Dockerfile . # Launch: docker run --gpus all -it detectron2-deploy:v0 ``` #### Using a persistent cache directory You can prevent models from being re-downloaded on every run, by storing them in a cache directory. To do this, add `--volume=$HOME/.torch/fvcore_cache:/tmp:rw` in the run command. ## Install new dependencies Add the following to `Dockerfile` to make persistent changes. ``` RUN sudo apt-get update && sudo apt-get install -y vim ``` Or run them in the container to make temporary changes. ================================================ FILE: detectron2/docker/deploy.Dockerfile ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # This file defines a container that compiles the C++ examples of detectron2. # See docker/README.md for usage. # Depends on the image produced by "./Dockerfile" FROM detectron2:v0 USER appuser ENV HOME=/home/appuser WORKDIR $HOME # Let torchvision find libtorch ENV CMAKE_PREFIX_PATH=$HOME/.local/lib/python3.6/site-packages/torch/ RUN sudo apt-get update && sudo apt-get install libopencv-dev --yes # install libtorchvision RUN git clone --branch v0.11.1 https://github.com/pytorch/vision/ RUN mkdir vision/build && cd vision/build && \ cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/.local -DCMAKE_BUILD_TYPE=Release -DWITH_CUDA=on -DTORCH_CUDA_ARCH_LIST=$TORCH_CUDA_ARCH_LIST && \ make -j && make install # make our installation take effect ENV CPATH=$HOME/.local/include \ LIBRARY_PATH=$HOME/.local/lib \ LD_LIBRARY_PATH=$HOME/.local/lib # build C++ examples of detectron2 RUN cd detectron2_repo/tools/deploy && mkdir build && cd build && \ cmake -DTORCH_CUDA_ARCH_LIST=$TORCH_CUDA_ARCH_LIST .. && make # binaries will be available under tools/deploy/build ================================================ FILE: detectron2/docker/docker-compose.yml ================================================ version: "2.3" services: detectron2: build: context: . dockerfile: Dockerfile args: USER_ID: ${USER_ID:-1000} deploy: resources: reservations: devices: - capabilities: - gpu shm_size: "8gb" ulimits: memlock: -1 stack: 67108864 volumes: - /tmp/.X11-unix:/tmp/.X11-unix:ro environment: - DISPLAY=$DISPLAY - NVIDIA_VISIBLE_DEVICES=all # Uncomment with proper source to access webcam from docker # devices: # - /dev/video0:/dev/video0 ================================================ FILE: detectron2/docs/.gitignore ================================================ _build ================================================ FILE: detectron2/docs/Makefile ================================================ # Minimal makefile for Sphinx documentation # Copyright (c) Facebook, Inc. and its affiliates. # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) ================================================ FILE: detectron2/docs/README.md ================================================ # Read the docs: The latest documentation built from this directory is available at [detectron2.readthedocs.io](https://detectron2.readthedocs.io/). Documents in this directory are not meant to be read on github. # Build the docs: 1. Install detectron2 according to [INSTALL.md](../INSTALL.md). 2. Install additional libraries required to build docs: - docutils==0.16 - Sphinx==3.2.0 - recommonmark==0.6.0 - sphinx_rtd_theme 3. Run `make html` from this directory. ================================================ FILE: detectron2/docs/_static/css/custom.css ================================================ /* * Copyright (c) Facebook, Inc. and its affiliates. * some extra css to make markdown look similar between github/sphinx */ /* * Below is for install.md: */ .rst-content code { white-space: pre; border: 0px; } .rst-content th { border: 1px solid #e1e4e5; } .rst-content th p { /* otherwise will be default 24px for regular paragraph */ margin-bottom: 0px; } .rst-content .line-block { /* otherwise will be 24px */ margin-bottom: 0px; } div.section > details { padding-bottom: 1em; } ================================================ FILE: detectron2/docs/conf.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. # flake8: noqa # Configuration file for the Sphinx documentation builder. # # This file does only contain a selection of the most common options. For a # full list see the documentation: # http://www.sphinx-doc.org/en/master/config # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys from unittest import mock from sphinx.domains import Domain from typing import Dict, List, Tuple # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # import sphinx_rtd_theme class GithubURLDomain(Domain): """ Resolve certain links in markdown files to github source. """ name = "githuburl" ROOT = "https://github.com/facebookresearch/detectron2/blob/main/" LINKED_DOC = ["tutorials/install", "tutorials/getting_started"] def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): github_url = None if not target.endswith("html") and target.startswith("../../"): url = target.replace("../", "") github_url = url if fromdocname in self.LINKED_DOC: # unresolved links in these docs are all github links github_url = target if github_url is not None: if github_url.endswith("MODEL_ZOO") or github_url.endswith("README"): # bug of recommonmark. # https://github.com/readthedocs/recommonmark/blob/ddd56e7717e9745f11300059e4268e204138a6b1/recommonmark/parser.py#L152-L155 github_url += ".md" print("Ref {} resolved to github:{}".format(target, github_url)) contnode["refuri"] = self.ROOT + github_url return [("githuburl:any", contnode)] else: return [] # to support markdown from recommonmark.parser import CommonMarkParser sys.path.insert(0, os.path.abspath("../")) os.environ["_DOC_BUILDING"] = "True" DEPLOY = os.environ.get("READTHEDOCS") == "True" # -- Project information ----------------------------------------------------- # fmt: off try: import torch # noqa except ImportError: for m in [ "torch", "torchvision", "torch.nn", "torch.nn.parallel", "torch.distributed", "torch.multiprocessing", "torch.autograd", "torch.autograd.function", "torch.nn.modules", "torch.nn.modules.utils", "torch.utils", "torch.utils.data", "torch.onnx", "torchvision", "torchvision.ops", ]: sys.modules[m] = mock.Mock(name=m) sys.modules['torch'].__version__ = "1.7" # fake version HAS_TORCH = False else: try: torch.ops.detectron2 = mock.Mock(name="torch.ops.detectron2") except: pass HAS_TORCH = True for m in [ "cv2", "scipy", "portalocker", "detectron2._C", "pycocotools", "pycocotools.mask", "pycocotools.coco", "pycocotools.cocoeval", "google", "google.protobuf", "google.protobuf.internal", "onnx", "caffe2", "caffe2.proto", "caffe2.python", "caffe2.python.utils", "caffe2.python.onnx", "caffe2.python.onnx.backend", ]: sys.modules[m] = mock.Mock(name=m) # fmt: on sys.modules["cv2"].__version__ = "3.4" import detectron2 # isort: skip if HAS_TORCH: from detectron2.utils.env import fixup_module_metadata fixup_module_metadata("torch.nn", torch.nn.__dict__) fixup_module_metadata("torch.utils.data", torch.utils.data.__dict__) project = "detectron2" copyright = "2019-2020, detectron2 contributors" author = "detectron2 contributors" # The short X.Y version version = detectron2.__version__ # The full version, including alpha/beta/rc tags release = version # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = "3.0" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "recommonmark", "sphinx.ext.autodoc", "sphinx.ext.napoleon", "sphinx.ext.intersphinx", "sphinx.ext.todo", "sphinx.ext.coverage", "sphinx.ext.mathjax", "sphinx.ext.viewcode", "sphinx.ext.githubpages", ] # -- Configurations for plugins ------------ napoleon_google_docstring = True napoleon_include_init_with_doc = True napoleon_include_special_with_doc = True napoleon_numpy_docstring = False napoleon_use_rtype = False autodoc_inherit_docstrings = False autodoc_member_order = "bysource" if DEPLOY: intersphinx_timeout = 10 else: # skip this when building locally intersphinx_timeout = 0.5 intersphinx_mapping = { "python": ("https://docs.python.org/3.7", None), "numpy": ("https://docs.scipy.org/doc/numpy/", None), "torch": ("https://pytorch.org/docs/master/", None), } # ------------------------- # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] source_suffix = [".rst", ".md"] # The master toctree document. master_doc = "index" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "build", "README.md", "tutorials/README.md"] # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # -- Options for HTML output ------------------------------------------------- html_theme = "sphinx_rtd_theme" html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] html_css_files = ["css/custom.css"] # Custom sidebar templates, must be a dictionary that maps document names # to template names. # # The default sidebars (for documents that don't match any pattern) are # defined by theme itself. Builtin themes are using these templates by # default: ``['localtoc.html', 'relations.html', 'sourcelink.html', # 'searchbox.html']``. # # html_sidebars = {} # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. htmlhelp_basename = "detectron2doc" # -- Options for LaTeX output ------------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, "detectron2.tex", "detectron2 Documentation", "detectron2 contributors", "manual") ] # -- Options for manual page output ------------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [(master_doc, "detectron2", "detectron2 Documentation", [author], 1)] # -- Options for Texinfo output ---------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( master_doc, "detectron2", "detectron2 Documentation", author, "detectron2", "One line description of project.", "Miscellaneous", ) ] # -- Options for todo extension ---------------------------------------------- # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True def autodoc_skip_member(app, what, name, obj, skip, options): # we hide something deliberately if getattr(obj, "__HIDE_SPHINX_DOC__", False): return True # Hide some that are deprecated or not intended to be used HIDDEN = { "ResNetBlockBase", "GroupedBatchSampler", "build_transform_gen", "apply_transform_gens", "TransformGen", "apply_augmentations", "StandardAugInput", "build_batch_data_loader", "draw_panoptic_seg_predictions", "WarmupCosineLR", "WarmupMultiStepLR", "downgrade_config", "upgrade_config", "add_export_config", } try: if name in HIDDEN or ( hasattr(obj, "__doc__") and obj.__doc__.lower().strip().startswith("deprecated") ): print("Skipping deprecated object: {}".format(name)) return True except: pass return skip _PAPER_DATA = { "resnet": ("1512.03385", "Deep Residual Learning for Image Recognition"), "fpn": ("1612.03144", "Feature Pyramid Networks for Object Detection"), "mask r-cnn": ("1703.06870", "Mask R-CNN"), "faster r-cnn": ( "1506.01497", "Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks", ), "deformconv": ("1703.06211", "Deformable Convolutional Networks"), "deformconv2": ("1811.11168", "Deformable ConvNets v2: More Deformable, Better Results"), "panopticfpn": ("1901.02446", "Panoptic Feature Pyramid Networks"), "retinanet": ("1708.02002", "Focal Loss for Dense Object Detection"), "cascade r-cnn": ("1712.00726", "Cascade R-CNN: Delving into High Quality Object Detection"), "lvis": ("1908.03195", "LVIS: A Dataset for Large Vocabulary Instance Segmentation"), "rrpn": ("1703.01086", "Arbitrary-Oriented Scene Text Detection via Rotation Proposals"), "imagenet in 1h": ("1706.02677", "Accurate, Large Minibatch SGD: Training ImageNet in 1 Hour"), "xception": ("1610.02357", "Xception: Deep Learning with Depthwise Separable Convolutions"), "mobilenet": ( "1704.04861", "MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications", ), "deeplabv3+": ( "1802.02611", "Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation", ), "dds": ("2003.13678", "Designing Network Design Spaces"), "scaling": ("2103.06877", "Fast and Accurate Model Scaling"), "fcos": ("2006.09214", "FCOS: A Simple and Strong Anchor-free Object Detector"), "rethinking-batchnorm": ("2105.07576", 'Rethinking "Batch" in BatchNorm'), "vitdet": ("2203.16527", "Exploring Plain Vision Transformer Backbones for Object Detection"), "mvitv2": ( "2112.01526", "MViTv2: Improved Multiscale Vision Transformers for Classification and Detection", ), "swin": ( "2103.14030", "Swin Transformer: Hierarchical Vision Transformer using Shifted Windows", ), } def paper_ref_role( typ: str, rawtext: str, text: str, lineno: int, inliner, options: Dict = {}, content: List[str] = [], ): """ Parse :paper:`xxx`. Similar to the "extlinks" sphinx extension. """ from docutils import nodes, utils from sphinx.util.nodes import split_explicit_title text = utils.unescape(text) has_explicit_title, title, link = split_explicit_title(text) link = link.lower() if link not in _PAPER_DATA: inliner.reporter.warning("Cannot find paper " + link) paper_url, paper_title = "#", link else: paper_url, paper_title = _PAPER_DATA[link] if "/" not in paper_url: paper_url = "https://arxiv.org/abs/" + paper_url if not has_explicit_title: title = paper_title pnode = nodes.reference(title, title, internal=False, refuri=paper_url) return [pnode], [] def setup(app): from recommonmark.transform import AutoStructify app.add_domain(GithubURLDomain) app.connect("autodoc-skip-member", autodoc_skip_member) app.add_role("paper", paper_ref_role) app.add_config_value( "recommonmark_config", {"enable_math": True, "enable_inline_math": True, "enable_eval_rst": True}, True, ) app.add_transform(AutoStructify) ================================================ FILE: detectron2/docs/index.rst ================================================ .. detectron2 documentation master file, created by sphinx-quickstart on Sat Sep 21 13:46:45 2019. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to detectron2's documentation! ====================================== .. toctree:: :maxdepth: 2 tutorials/index notes/index modules/index ================================================ FILE: detectron2/docs/modules/checkpoint.rst ================================================ detectron2.checkpoint ============================= .. automodule:: detectron2.checkpoint :members: :undoc-members: :show-inheritance: ================================================ FILE: detectron2/docs/modules/config.rst ================================================ detectron2.config ========================= Related tutorials: :doc:`../tutorials/configs`, :doc:`../tutorials/extend`. .. automodule:: detectron2.config :members: :undoc-members: :show-inheritance: Yaml Config References ----------------- .. literalinclude:: ../../detectron2/config/defaults.py :language: python :linenos: :lines: 7- ================================================ FILE: detectron2/docs/modules/data.rst ================================================ detectron2.data ======================= .. autodata:: detectron2.data.DatasetCatalog(dict) :annotation: .. autodata:: detectron2.data.MetadataCatalog(dict) :annotation: .. automodule:: detectron2.data :members: :undoc-members: :show-inheritance: detectron2.data.detection\_utils module --------------------------------------- .. automodule:: detectron2.data.detection_utils :members: :undoc-members: :show-inheritance: detectron2.data.datasets module --------------------------------------- .. automodule:: detectron2.data.datasets :members: :undoc-members: :show-inheritance: detectron2.data.samplers module --------------------------------------- .. automodule:: detectron2.data.samplers :members: :undoc-members: :show-inheritance: ================================================ FILE: detectron2/docs/modules/data_transforms.rst ================================================ detectron2.data.transforms ==================================== Related tutorial: :doc:`../tutorials/augmentation`. .. automodule:: detectron2.data.transforms :members: :undoc-members: :show-inheritance: :imported-members: ================================================ FILE: detectron2/docs/modules/engine.rst ================================================ detectron2.engine ========================= Related tutorial: :doc:`../tutorials/training`. .. automodule:: detectron2.engine :members: :undoc-members: :show-inheritance: detectron2.engine.defaults module --------------------------------- .. automodule:: detectron2.engine.defaults :members: :undoc-members: :show-inheritance: detectron2.engine.hooks module --------------------------------- .. automodule:: detectron2.engine.hooks :members: :undoc-members: :show-inheritance: ================================================ FILE: detectron2/docs/modules/evaluation.rst ================================================ detectron2.evaluation ============================= .. automodule:: detectron2.evaluation :members: :undoc-members: :show-inheritance: ================================================ FILE: detectron2/docs/modules/export.rst ================================================ detectron2.export ========================= Related tutorial: :doc:`../tutorials/deployment`. .. automodule:: detectron2.export :members: :undoc-members: :show-inheritance: ================================================ FILE: detectron2/docs/modules/fvcore.rst ================================================ fvcore documentation ==================== Detectron2 depends on utilities in `fvcore `_. We include part of fvcore documentation here for easier reference. fvcore.nn ----------------- .. automodule:: fvcore.nn :members: :inherited-members: :undoc-members: :show-inheritance: fvcore.common --------------------- .. automodule:: fvcore.common.checkpoint :members: :undoc-members: :show-inheritance: .. automodule:: fvcore.common.config :members: :undoc-members: :show-inheritance: .. automodule:: fvcore.common.history_buffer :members: :undoc-members: :show-inheritance: .. automodule:: fvcore.common.param_scheduler :members: :inherited-members: :undoc-members: :show-inheritance: .. automodule:: fvcore.common.registry :members: :undoc-members: :show-inheritance: .. automodule:: fvcore.common.timer :members: :undoc-members: :show-inheritance: ================================================ FILE: detectron2/docs/modules/index.rst ================================================ API Documentation ================== .. toctree:: checkpoint config data data_transforms engine evaluation layers model_zoo modeling solver structures utils export fvcore ================================================ FILE: detectron2/docs/modules/layers.rst ================================================ detectron2.layers ========================= .. automodule:: detectron2.layers :members: :undoc-members: :show-inheritance: ================================================ FILE: detectron2/docs/modules/model_zoo.rst ================================================ detectron2.model_zoo ============================ .. automodule:: detectron2.model_zoo :members: :undoc-members: :show-inheritance: ================================================ FILE: detectron2/docs/modules/modeling.rst ================================================ detectron2.modeling =========================== .. automodule:: detectron2.modeling :members: :undoc-members: :show-inheritance: detectron2.modeling.poolers module --------------------------------------- .. automodule:: detectron2.modeling.poolers :members: :undoc-members: :show-inheritance: detectron2.modeling.sampling module ------------------------------------ .. automodule:: detectron2.modeling.sampling :members: :undoc-members: :show-inheritance: detectron2.modeling.box_regression module ------------------------------------------ .. automodule:: detectron2.modeling.box_regression :members: :undoc-members: :show-inheritance: Model Registries ----------------- These are different registries provided in modeling. Each registry provide you the ability to replace it with your customized component, without having to modify detectron2's code. Note that it is impossible to allow users to customize any line of code directly. Even just to add one line at some place, you'll likely need to find out the smallest registry which contains that line, and register your component to that registry. .. autodata:: detectron2.modeling.META_ARCH_REGISTRY .. autodata:: detectron2.modeling.BACKBONE_REGISTRY .. autodata:: detectron2.modeling.PROPOSAL_GENERATOR_REGISTRY .. autodata:: detectron2.modeling.RPN_HEAD_REGISTRY .. autodata:: detectron2.modeling.ANCHOR_GENERATOR_REGISTRY .. autodata:: detectron2.modeling.ROI_HEADS_REGISTRY .. autodata:: detectron2.modeling.ROI_BOX_HEAD_REGISTRY .. autodata:: detectron2.modeling.ROI_MASK_HEAD_REGISTRY .. autodata:: detectron2.modeling.ROI_KEYPOINT_HEAD_REGISTRY ================================================ FILE: detectron2/docs/modules/solver.rst ================================================ detectron2.solver ========================= .. automodule:: detectron2.solver :members: :undoc-members: :show-inheritance: ================================================ FILE: detectron2/docs/modules/structures.rst ================================================ detectron2.structures ============================= .. automodule:: detectron2.structures :members: :undoc-members: :show-inheritance: ================================================ FILE: detectron2/docs/modules/utils.rst ================================================ detectron2.utils ======================== detectron2.utils.colormap module -------------------------------- .. automodule:: detectron2.utils.colormap :members: :undoc-members: :show-inheritance: detectron2.utils.comm module ---------------------------- .. automodule:: detectron2.utils.comm :members: :undoc-members: :show-inheritance: detectron2.utils.events module ------------------------------ .. automodule:: detectron2.utils.events :members: :undoc-members: :show-inheritance: detectron2.utils.logger module ------------------------------ .. automodule:: detectron2.utils.logger :members: :undoc-members: :show-inheritance: detectron2.utils.registry module -------------------------------- .. automodule:: detectron2.utils.registry :members: :undoc-members: :show-inheritance: detectron2.utils.memory module ---------------------------------- .. automodule:: detectron2.utils.memory :members: :undoc-members: :show-inheritance: detectron2.utils.analysis module ---------------------------------- .. automodule:: detectron2.utils.analysis :members: :undoc-members: :show-inheritance: detectron2.utils.visualizer module ---------------------------------- .. automodule:: detectron2.utils.visualizer :members: :undoc-members: :show-inheritance: detectron2.utils.video\_visualizer module ----------------------------------------- .. automodule:: detectron2.utils.video_visualizer :members: :undoc-members: :show-inheritance: ================================================ FILE: detectron2/docs/notes/benchmarks.md ================================================ # Benchmarks Here we benchmark the training speed of a Mask R-CNN in detectron2, with some other popular open source Mask R-CNN implementations. ### Settings * Hardware: 8 NVIDIA V100s with NVLink. * Software: Python 3.7, CUDA 10.1, cuDNN 7.6.5, PyTorch 1.5, TensorFlow 1.15.0rc2, Keras 2.2.5, MxNet 1.6.0b20190820. * Model: an end-to-end R-50-FPN Mask-RCNN model, using the same hyperparameter as the [Detectron baseline config](https://github.com/facebookresearch/Detectron/blob/master/configs/12_2017_baselines/e2e_mask_rcnn_R-50-FPN_1x.yaml) (it does not have scale augmentation). * Metrics: We use the average throughput in iterations 100-500 to skip GPU warmup time. Note that for R-CNN-style models, the throughput of a model typically changes during training, because it depends on the predictions of the model. Therefore this metric is not directly comparable with "train speed" in model zoo, which is the average speed of the entire training run. ### Main Results ```eval_rst +-------------------------------+--------------------+ | Implementation | Throughput (img/s) | +===============================+====================+ | |D2| |PT| | 62 | +-------------------------------+--------------------+ | mmdetection_ |PT| | 53 | +-------------------------------+--------------------+ | maskrcnn-benchmark_ |PT| | 53 | +-------------------------------+--------------------+ | tensorpack_ |TF| | 50 | +-------------------------------+--------------------+ | simpledet_ |mxnet| | 39 | +-------------------------------+--------------------+ | Detectron_ |C2| | 19 | +-------------------------------+--------------------+ | `matterport/Mask_RCNN`__ |TF| | 14 | +-------------------------------+--------------------+ .. _maskrcnn-benchmark: https://github.com/facebookresearch/maskrcnn-benchmark/ .. _tensorpack: https://github.com/tensorpack/tensorpack/tree/master/examples/FasterRCNN .. _mmdetection: https://github.com/open-mmlab/mmdetection/ .. _simpledet: https://github.com/TuSimple/simpledet/ .. _Detectron: https://github.com/facebookresearch/Detectron __ https://github.com/matterport/Mask_RCNN/ .. |D2| image:: https://github.com/facebookresearch/detectron2/raw/main/.github/Detectron2-Logo-Horz.svg?sanitize=true :height: 15pt :target: https://github.com/facebookresearch/detectron2/ .. |PT| image:: https://pytorch.org/assets/images/logo-icon.svg :width: 15pt :height: 15pt :target: https://pytorch.org .. |TF| image:: https://static.nvidiagrid.net/ngc/containers/tensorflow.png :width: 15pt :height: 15pt :target: https://tensorflow.org .. |mxnet| image:: https://github.com/dmlc/web-data/raw/master/mxnet/image/mxnet_favicon.png :width: 15pt :height: 15pt :target: https://mxnet.apache.org/ .. |C2| image:: https://caffe2.ai/static/logo.svg :width: 15pt :height: 15pt :target: https://caffe2.ai ``` Details for each implementation: * __Detectron2__: with release v0.1.2, run: ``` python tools/train_net.py --config-file configs/Detectron1-Comparisons/mask_rcnn_R_50_FPN_noaug_1x.yaml --num-gpus 8 ``` * __mmdetection__: at commit `b0d845f`, run ``` ./tools/dist_train.sh configs/mask_rcnn/mask_rcnn_r50_caffe_fpn_1x_coco.py 8 ``` * __maskrcnn-benchmark__: use commit `0ce8f6f` with `sed -i 's/torch.uint8/torch.bool/g' **/*.py; sed -i 's/AT_CHECK/TORCH_CHECK/g' **/*.cu` to make it compatible with PyTorch 1.5. Then, run training with ``` python -m torch.distributed.launch --nproc_per_node=8 tools/train_net.py --config-file configs/e2e_mask_rcnn_R_50_FPN_1x.yaml ``` The speed we observed is faster than its model zoo, likely due to different software versions. * __tensorpack__: at commit `caafda`, `export TF_CUDNN_USE_AUTOTUNE=0`, then run ``` mpirun -np 8 ./train.py --config DATA.BASEDIR=/data/coco TRAINER=horovod BACKBONE.STRIDE_1X1=True TRAIN.STEPS_PER_EPOCH=50 --load ImageNet-R50-AlignPadding.npz ``` * __SimpleDet__: at commit `9187a1`, run ``` python detection_train.py --config config/mask_r50v1_fpn_1x.py ``` * __Detectron__: run ``` python tools/train_net.py --cfg configs/12_2017_baselines/e2e_mask_rcnn_R-50-FPN_1x.yaml ``` Note that many of its ops run on CPUs, therefore the performance is limited. * __matterport/Mask_RCNN__: at commit `3deaec`, apply the following diff, `export TF_CUDNN_USE_AUTOTUNE=0`, then run ``` python coco.py train --dataset=/data/coco/ --model=imagenet ``` Note that many small details in this implementation might be different from Detectron's standards.
(diff to make it use the same hyperparameters - click to expand) ```diff diff --git i/mrcnn/model.py w/mrcnn/model.py index 62cb2b0..61d7779 100644 --- i/mrcnn/model.py +++ w/mrcnn/model.py @@ -2367,8 +2367,8 @@ class MaskRCNN(): epochs=epochs, steps_per_epoch=self.config.STEPS_PER_EPOCH, callbacks=callbacks, - validation_data=val_generator, - validation_steps=self.config.VALIDATION_STEPS, + #validation_data=val_generator, + #validation_steps=self.config.VALIDATION_STEPS, max_queue_size=100, workers=workers, use_multiprocessing=True, diff --git i/mrcnn/parallel_model.py w/mrcnn/parallel_model.py index d2bf53b..060172a 100644 --- i/mrcnn/parallel_model.py +++ w/mrcnn/parallel_model.py @@ -32,6 +32,7 @@ class ParallelModel(KM.Model): keras_model: The Keras model to parallelize gpu_count: Number of GPUs. Must be > 1 """ + super().__init__() self.inner_model = keras_model self.gpu_count = gpu_count merged_outputs = self.make_parallel() diff --git i/samples/coco/coco.py w/samples/coco/coco.py index 5d172b5..239ed75 100644 --- i/samples/coco/coco.py +++ w/samples/coco/coco.py @@ -81,7 +81,10 @@ class CocoConfig(Config): IMAGES_PER_GPU = 2 # Uncomment to train on 8 GPUs (default is 1) - # GPU_COUNT = 8 + GPU_COUNT = 8 + BACKBONE = "resnet50" + STEPS_PER_EPOCH = 50 + TRAIN_ROIS_PER_IMAGE = 512 # Number of classes (including background) NUM_CLASSES = 1 + 80 # COCO has 80 classes @@ -496,29 +499,10 @@ if __name__ == '__main__': # *** This training schedule is an example. Update to your needs *** # Training - Stage 1 - print("Training network heads") model.train(dataset_train, dataset_val, learning_rate=config.LEARNING_RATE, epochs=40, - layers='heads', - augmentation=augmentation) - - # Training - Stage 2 - # Finetune layers from ResNet stage 4 and up - print("Fine tune Resnet stage 4 and up") - model.train(dataset_train, dataset_val, - learning_rate=config.LEARNING_RATE, - epochs=120, - layers='4+', - augmentation=augmentation) - - # Training - Stage 3 - # Fine tune all layers - print("Fine tune all layers") - model.train(dataset_train, dataset_val, - learning_rate=config.LEARNING_RATE / 10, - epochs=160, - layers='all', + layers='3+', augmentation=augmentation) elif args.command == "evaluate": ```
================================================ FILE: detectron2/docs/notes/changelog.md ================================================ # Change Log and Backward Compatibility ### Releases See release logs at [https://github.com/facebookresearch/detectron2/releases](https://github.com/facebookresearch/detectron2/releases) for new updates. ### Backward Compatibility Due to the research nature of what the library does, there might be backward incompatible changes. But we try to reduce users' disruption by the following ways: * APIs listed in [API documentation](https://detectron2.readthedocs.io/modules/index.html), including function/class names, their arguments, and documented class attributes, are considered *stable* unless otherwise noted in the documentation. They are less likely to be broken, but if needed, will trigger a deprecation warning for a reasonable period before getting broken, and will be documented in release logs. * Others functions/classses/attributes are considered internal, and are more likely to change. However, we're aware that some of them may be already used by other projects, and in particular we may use them for convenience among projects under `detectron2/projects`. For such APIs, we may treat them as stable APIs and also apply the above strategies. They may be promoted to stable when we're ready. * Projects under "detectron2/projects" or imported with "detectron2.projects" are research projects and are all considered experimental. * Classes/functions that contain the word "default" or are explicitly documented to produce "default behavior" may change their behaviors when new features are added. Despite of the possible breakage, if a third-party project would like to keep up with the latest updates in detectron2, using it as a library will still be less disruptive than forking, because the frequency and scope of API changes will be much smaller than code changes. To see such changes, search for "incompatible changes" in [release logs](https://github.com/facebookresearch/detectron2/releases). ### Config Version Change Log Detectron2's config version has not been changed since open source. There is no need for an open source user to worry about this. * v1: Rename `RPN_HEAD.NAME` to `RPN.HEAD_NAME`. * v2: A batch of rename of many configurations before release. ### Silent Regressions in Historical Versions: We list a few silent regressions, since they may silently produce incorrect results and will be hard to debug. * 04/01/2020 - 05/11/2020: Bad accuracy if `TRAIN_ON_PRED_BOXES` is set to True. * 03/30/2020 - 04/01/2020: ResNets are not correctly built. * 12/19/2019 - 12/26/2019: Using aspect ratio grouping causes a drop in accuracy. * - 11/9/2019: Test time augmentation does not predict the last category. ================================================ FILE: detectron2/docs/notes/compatibility.md ================================================ # Compatibility with Other Libraries ## Compatibility with Detectron (and maskrcnn-benchmark) Detectron2 addresses some legacy issues left in Detectron. As a result, their models are not compatible: running inference with the same model weights will produce different results in the two code bases. The major differences regarding inference are: - The height and width of a box with corners (x1, y1) and (x2, y2) is now computed more naturally as width = x2 - x1 and height = y2 - y1; In Detectron, a "+ 1" was added both height and width. Note that the relevant ops in Caffe2 have [adopted this change of convention](https://github.com/pytorch/pytorch/pull/20550) with an extra option. So it is still possible to run inference with a Detectron2-trained model in Caffe2. The change in height/width calculations most notably changes: - encoding/decoding in bounding box regression. - non-maximum suppression. The effect here is very negligible, though. - RPN now uses simpler anchors with fewer quantization artifacts. In Detectron, the anchors were quantized and [do not have accurate areas](https://github.com/facebookresearch/Detectron/issues/227). In Detectron2, the anchors are center-aligned to feature grid points and not quantized. - Classification layers have a different ordering of class labels. This involves any trainable parameter with shape (..., num_categories + 1, ...). In Detectron2, integer labels [0, K-1] correspond to the K = num_categories object categories and the label "K" corresponds to the special "background" category. In Detectron, label "0" means background, and labels [1, K] correspond to the K categories. - ROIAlign is implemented differently. The new implementation is [available in Caffe2](https://github.com/pytorch/pytorch/pull/23706). 1. All the ROIs are shifted by half a pixel compared to Detectron in order to create better image-feature-map alignment. See `layers/roi_align.py` for details. To enable the old behavior, use `ROIAlign(aligned=False)`, or `POOLER_TYPE=ROIAlign` instead of `ROIAlignV2` (the default). 1. The ROIs are not required to have a minimum size of 1. This will lead to tiny differences in the output, but should be negligible. - Mask inference function is different. In Detectron2, the "paste_mask" function is different and should be more accurate than in Detectron. This change can improve mask AP on COCO by ~0.5% absolute. There are some other differences in training as well, but they won't affect model-level compatibility. The major ones are: - We fixed a [bug](https://github.com/facebookresearch/Detectron/issues/459) in Detectron, by making `RPN.POST_NMS_TOPK_TRAIN` per-image, rather than per-batch. The fix may lead to a small accuracy drop for a few models (e.g. keypoint detection) and will require some parameter tuning to match the Detectron results. - For simplicity, we change the default loss in bounding box regression to L1 loss, instead of smooth L1 loss. We have observed that this tends to slightly decrease box AP50 while improving box AP for higher overlap thresholds (and leading to a slight overall improvement in box AP). - We interpret the coordinates in COCO bounding box and segmentation annotations as coordinates in range `[0, width]` or `[0, height]`. The coordinates in COCO keypoint annotations are interpreted as pixel indices in range `[0, width - 1]` or `[0, height - 1]`. Note that this affects how flip augmentation is implemented. [This article](https://ppwwyyxx.com/blog/2021/Where-are-Pixels/) explains more details on the above mentioned issues about pixels, coordinates, and "+1"s. ## Compatibility with Caffe2 As mentioned above, despite the incompatibilities with Detectron, the relevant ops have been implemented in Caffe2. Therefore, models trained with detectron2 can be converted in Caffe2. See [Deployment](../tutorials/deployment.md) for the tutorial. ## Compatibility with TensorFlow Most ops are available in TensorFlow, although some tiny differences in the implementation of resize / ROIAlign / padding need to be addressed. A working conversion script is provided by [tensorpack Faster R-CNN](https://github.com/tensorpack/tensorpack/tree/master/examples/FasterRCNN/convert_d2) to run a standard detectron2 model in TensorFlow. ================================================ FILE: detectron2/docs/notes/contributing.md ================================================ # Contributing to detectron2 ## Issues We use GitHub issues to track public bugs and questions. Please make sure to follow one of the [issue templates](https://github.com/facebookresearch/detectron2/issues/new/choose) when reporting any issues. Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe disclosure of security bugs. In those cases, please go through the process outlined on that page and do not file a public issue. ## Pull Requests We actively welcome pull requests. However, if you're adding any significant features (e.g. > 50 lines), please make sure to discuss with maintainers about your motivation and proposals in an issue before sending a PR. This is to save your time so you don't spend time on a PR that we'll not accept. We do not always accept new features, and we take the following factors into consideration: 1. Whether the same feature can be achieved without modifying detectron2. Detectron2 is designed so that you can implement many extensions from the outside, e.g. those in [projects](https://github.com/facebookresearch/detectron2/tree/master/projects). * If some part of detectron2 is not extensible enough, you can also bring up a more general issue to improve it. Such feature request may be useful to more users. 2. Whether the feature is potentially useful to a large audience (e.g. an impactful detection paper, a popular dataset, a significant speedup, a widely useful utility), or only to a small portion of users (e.g., a less-known paper, an improvement not in the object detection field, a trick that's not very popular in the community, code to handle a non-standard type of data) * Adoption of additional models, datasets, new task are by default not added to detectron2 before they receive significant popularity in the community. We sometimes accept such features in `projects/`, or as a link in `projects/README.md`. 3. Whether the proposed solution has a good design / interface. This can be discussed in the issue prior to PRs, or in the form of a draft PR. 4. Whether the proposed solution adds extra mental/practical overhead to users who don't need such feature. 5. Whether the proposed solution breaks existing APIs. To add a feature to an existing function/class `Func`, there are always two approaches: (1) add new arguments to `Func`; (2) write a new `Func_with_new_feature`. To meet the above criteria, we often prefer approach (2), because: 1. It does not involve modifying or potentially breaking existing code. 2. It does not add overhead to users who do not need the new feature. 3. Adding new arguments to a function/class is not scalable w.r.t. all the possible new research ideas in the future. When sending a PR, please do: 1. If a PR contains multiple orthogonal changes, split it to several PRs. 2. If you've added code that should be tested, add tests. 3. For PRs that need experiments (e.g. adding a new model or new methods), you don't need to update model zoo, but do provide experiment results in the description of the PR. 4. If APIs are changed, update the documentation. 5. We use the [Google style docstrings](https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html) in python. 6. Make sure your code lints with `./dev/linter.sh`. ## Contributor License Agreement ("CLA") In order to accept your pull request, we need you to submit a CLA. You only need to do this once to work on any of Facebook's open source projects. Complete your CLA here: ## License By contributing to detectron2, you agree that your contributions will be licensed under the LICENSE file in the root directory of this source tree. ================================================ FILE: detectron2/docs/notes/index.rst ================================================ Notes ====================================== .. toctree:: :maxdepth: 2 benchmarks compatibility contributing changelog ================================================ FILE: detectron2/docs/requirements.txt ================================================ docutils==0.16 # https://github.com/sphinx-doc/sphinx/commit/7acd3ada3f38076af7b2b5c9f3b60bb9c2587a3d sphinx==3.2.0 recommonmark==0.6.0 sphinx_rtd_theme # Dependencies here are only those required by import termcolor numpy tqdm matplotlib termcolor yacs tabulate cloudpickle Pillow future git+https://github.com/facebookresearch/fvcore.git https://download.pytorch.org/whl/cpu/torch-1.8.1%2Bcpu-cp37-cp37m-linux_x86_64.whl https://download.pytorch.org/whl/cpu/torchvision-0.9.1%2Bcpu-cp37-cp37m-linux_x86_64.whl omegaconf>=2.1.0.dev24 hydra-core>=1.1.0.dev5 scipy timm fairscale ================================================ FILE: detectron2/docs/tutorials/README.md ================================================ # Read the docs: The latest documentation built from this directory is available at [detectron2.readthedocs.io](https://detectron2.readthedocs.io/). Documents in this directory are not meant to be read on github. ================================================ FILE: detectron2/docs/tutorials/augmentation.md ================================================ # Data Augmentation Augmentation is an important part of training. Detectron2's data augmentation system aims at addressing the following goals: 1. Allow augmenting multiple data types together (e.g., images together with their bounding boxes and masks) 2. Allow applying a sequence of statically-declared augmentation 3. Allow adding custom new data types to augment (rotated bounding boxes, video clips, etc.) 4. Process and manipulate the __operations__ that are applied by augmentations The first two features cover most of the common use cases, and is also available in other libraries such as [albumentations](https://medium.com/pytorch/multi-target-in-albumentations-16a777e9006e). Supporting other features adds some overhead to detectron2's augmentation API, which we'll explain in this tutorial. This tutorial focuses on how to use augmentations when writing new data loaders, and how to write new augmentations. If you use the default data loader in detectron2, it already supports taking a user-provided list of custom augmentations, as explained in the [Dataloader tutorial](data_loading). ## Basic Usage The basic usage of feature (1) and (2) is like the following: ```python from detectron2.data import transforms as T # Define a sequence of augmentations: augs = T.AugmentationList([ T.RandomBrightness(0.9, 1.1), T.RandomFlip(prob=0.5), T.RandomCrop("absolute", (640, 640)) ]) # type: T.Augmentation # Define the augmentation input ("image" required, others optional): input = T.AugInput(image, boxes=boxes, sem_seg=sem_seg) # Apply the augmentation: transform = augs(input) # type: T.Transform image_transformed = input.image # new image sem_seg_transformed = input.sem_seg # new semantic segmentation # For any extra data that needs to be augmented together, use transform, e.g.: image2_transformed = transform.apply_image(image2) polygons_transformed = transform.apply_polygons(polygons) ``` Three basic concepts are involved here. They are: * [T.Augmentation](../modules/data_transforms.html#detectron2.data.transforms.Augmentation) defines the __"policy"__ to modify inputs. * its `__call__(AugInput) -> Transform` method augments the inputs in-place, and returns the operation that is applied * [T.Transform](../modules/data_transforms.html#detectron2.data.transforms.Transform) implements the actual __operations__ to transform data * it has methods such as `apply_image`, `apply_coords` that define how to transform each data type * [T.AugInput](../modules/data_transforms.html#detectron2.data.transforms.AugInput) stores inputs needed by `T.Augmentation` and how they should be transformed. This concept is needed for some advanced usage. Using this class directly should be sufficient for all common use cases, since extra data not in `T.AugInput` can be augmented using the returned `transform`, as shown in the above example. ## Write New Augmentations Most 2D augmentations only need to know about the input image. Such augmentation can be implemented easily like this: ```python class MyColorAugmentation(T.Augmentation): def get_transform(self, image): r = np.random.rand(2) return T.ColorTransform(lambda x: x * r[0] + r[1] * 10) class MyCustomResize(T.Augmentation): def get_transform(self, image): old_h, old_w = image.shape[:2] new_h, new_w = int(old_h * np.random.rand()), int(old_w * 1.5) return T.ResizeTransform(old_h, old_w, new_h, new_w) augs = MyCustomResize() transform = augs(input) ``` In addition to image, any attributes of the given `AugInput` can be used as long as they are part of the function signature, e.g.: ```python class MyCustomCrop(T.Augmentation): def get_transform(self, image, sem_seg): # decide where to crop using both image and sem_seg return T.CropTransform(...) augs = MyCustomCrop() assert hasattr(input, "image") and hasattr(input, "sem_seg") transform = augs(input) ``` New transform operation can also be added by subclassing [T.Transform](../modules/data_transforms.html#detectron2.data.transforms.Transform). ## Advanced Usage We give a few examples of advanced usages that are enabled by our system. These options can be interesting to new research, although changing them is often not needed for standard use cases. ### Custom transform strategy Instead of only returning the augmented data, detectron2's `Augmentation` returns the __operations__ as `T.Transform`. This allows users to apply custom transform strategy on their data. We use keypoints data as an example. Keypoints are (x, y) coordinates, but they are not so trivial to augment due to the semantic meaning they carry. Such meaning is only known to the users, therefore users may want to augment them manually by looking at the returned `transform`. For example, when an image is horizontally flipped, we'd like to swap the keypoint annotations for "left eye" and "right eye". This can be done like this (included by default in detectron2's default data loader): ```python # augs, input are defined as in previous examples transform = augs(input) # type: T.Transform keypoints_xy = transform.apply_coords(keypoints_xy) # transform the coordinates # get a list of all transforms that were applied transforms = T.TransformList([transform]).transforms # check if it is flipped for odd number of times do_hflip = sum(isinstance(t, T.HFlipTransform) for t in transforms) % 2 == 1 if do_hflip: keypoints_xy = keypoints_xy[flip_indices_mapping] ``` As another example, keypoints annotations often have a "visibility" field. A sequence of augmentations might augment a visible keypoint out of the image boundary (e.g. with cropping), but then bring it back within the boundary afterwards (e.g. with image padding). If users decide to label such keypoints "invisible", then the visibility check has to happen after every transform step. This can be achieved by: ```python transform = augs(input) # type: T.TransformList assert isinstance(transform, T.TransformList) for t in transform.transforms: keypoints_xy = t.apply_coords(keypoints_xy) visibility &= (keypoints_xy >= [0, 0] & keypoints_xy <= [W, H]).all(axis=1) # btw, detectron2's `transform_keypoint_annotations` function chooses to label such keypoints "visible": # keypoints_xy = transform.apply_coords(keypoints_xy) # visibility &= (keypoints_xy >= [0, 0] & keypoints_xy <= [W, H]).all(axis=1) ``` ### Geometrically invert the transform If images are pre-processed by augmentations before inference, the predicted results such as segmentation masks are localized on the augmented image. We'd like to invert the applied augmentation with the [inverse()](../modules/data_transforms.html#detectron2.data.transforms.Transform.inverse) API, to obtain results on the original image: ```python transform = augs(input) pred_mask = make_prediction(input.image) inv_transform = transform.inverse() pred_mask_orig = inv_transform.apply_segmentation(pred_mask) ``` ### Add new data types [T.Transform](../modules/data_transforms.html#detectron2.data.transforms.Transform) supports a few common data types to transform, including images, coordinates, masks, boxes, polygons. It allows registering new data types, e.g.: ```python @T.HFlipTransform.register_type("rotated_boxes") def func(flip_transform: T.HFlipTransform, rotated_boxes: Any): # do the work return flipped_rotated_boxes t = HFlipTransform(width=800) transformed_rotated_boxes = t.apply_rotated_boxes(rotated_boxes) # func will be called ``` ### Extend T.AugInput An augmentation can only access attributes available in the given input. [T.AugInput](../modules/data_transforms.html#detectron2.data.transforms.StandardAugInput) defines "image", "boxes", "sem_seg", which are sufficient for common augmentation strategies to decide how to augment. If not, a custom implementation is needed. By re-implement the "transform()" method in AugInput, it is also possible to augment different fields in ways that are dependent on each other. Such use case is uncommon (e.g. post-process bounding box based on augmented masks), but allowed by the system. ================================================ FILE: detectron2/docs/tutorials/builtin_datasets.md ================================================ # Use Builtin Datasets A dataset can be used by accessing [DatasetCatalog](https://detectron2.readthedocs.io/modules/data.html#detectron2.data.DatasetCatalog) for its data, or [MetadataCatalog](https://detectron2.readthedocs.io/modules/data.html#detectron2.data.MetadataCatalog) for its metadata (class names, etc). This document explains how to setup the builtin datasets so they can be used by the above APIs. [Use Custom Datasets](https://detectron2.readthedocs.io/tutorials/datasets.html) gives a deeper dive on how to use `DatasetCatalog` and `MetadataCatalog`, and how to add new datasets to them. Detectron2 has builtin support for a few datasets. The datasets are assumed to exist in a directory specified by the environment variable `DETECTRON2_DATASETS`. Under this directory, detectron2 will look for datasets in the structure described below, if needed. ``` $DETECTRON2_DATASETS/ coco/ lvis/ cityscapes/ VOC20{07,12}/ ``` You can set the location for builtin datasets by `export DETECTRON2_DATASETS=/path/to/datasets`. If left unset, the default is `./datasets` relative to your current working directory. The [model zoo](https://github.com/facebookresearch/detectron2/blob/master/MODEL_ZOO.md) contains configs and models that use these builtin datasets. ## Expected dataset structure for [COCO instance/keypoint detection](https://cocodataset.org/#download): ``` coco/ annotations/ instances_{train,val}2017.json person_keypoints_{train,val}2017.json {train,val}2017/ # image files that are mentioned in the corresponding json ``` You can use the 2014 version of the dataset as well. Some of the builtin tests (`dev/run_*_tests.sh`) uses a tiny version of the COCO dataset, which you can download with `./datasets/prepare_for_tests.sh`. ## Expected dataset structure for PanopticFPN: Extract panoptic annotations from [COCO website](https://cocodataset.org/#download) into the following structure: ``` coco/ annotations/ panoptic_{train,val}2017.json panoptic_{train,val}2017/ # png annotations panoptic_stuff_{train,val}2017/ # generated by the script mentioned below ``` Install panopticapi by: ``` pip install git+https://github.com/cocodataset/panopticapi.git ``` Then, run `python datasets/prepare_panoptic_fpn.py`, to extract semantic annotations from panoptic annotations. ## Expected dataset structure for [LVIS instance segmentation](https://www.lvisdataset.org/dataset): ``` coco/ {train,val,test}2017/ lvis/ lvis_v0.5_{train,val}.json lvis_v0.5_image_info_test.json lvis_v1_{train,val}.json lvis_v1_image_info_test{,_challenge}.json ``` Install lvis-api by: ``` pip install git+https://github.com/lvis-dataset/lvis-api.git ``` To evaluate models trained on the COCO dataset using LVIS annotations, run `python datasets/prepare_cocofied_lvis.py` to prepare "cocofied" LVIS annotations. ## Expected dataset structure for [cityscapes](https://www.cityscapes-dataset.com/downloads/): ``` cityscapes/ gtFine/ train/ aachen/ color.png, instanceIds.png, labelIds.png, polygons.json, labelTrainIds.png ... val/ test/ # below are generated Cityscapes panoptic annotation cityscapes_panoptic_train.json cityscapes_panoptic_train/ cityscapes_panoptic_val.json cityscapes_panoptic_val/ cityscapes_panoptic_test.json cityscapes_panoptic_test/ leftImg8bit/ train/ val/ test/ ``` Install cityscapes scripts by: ``` pip install git+https://github.com/mcordts/cityscapesScripts.git ``` Note: to create labelTrainIds.png, first prepare the above structure, then run cityscapesescript with: ``` CITYSCAPES_DATASET=/path/to/abovementioned/cityscapes python cityscapesscripts/preparation/createTrainIdLabelImgs.py ``` These files are not needed for instance segmentation. Note: to generate Cityscapes panoptic dataset, run cityscapesescript with: ``` CITYSCAPES_DATASET=/path/to/abovementioned/cityscapes python cityscapesscripts/preparation/createPanopticImgs.py ``` These files are not needed for semantic and instance segmentation. ## Expected dataset structure for [Pascal VOC](http://host.robots.ox.ac.uk/pascal/VOC/index.html): ``` VOC20{07,12}/ Annotations/ ImageSets/ Main/ trainval.txt test.txt # train.txt or val.txt, if you use these splits JPEGImages/ ``` ## Expected dataset structure for [ADE20k Scene Parsing](http://sceneparsing.csail.mit.edu/): ``` ADEChallengeData2016/ annotations/ annotations_detectron2/ images/ objectInfo150.txt ``` The directory `annotations_detectron2` is generated by running `python datasets/prepare_ade20k_sem_seg.py`. ================================================ FILE: detectron2/docs/tutorials/configs.md ================================================ # Yacs Configs Detectron2 provides a key-value based config system that can be used to obtain standard, common behaviors. This system uses YAML and [yacs](https://github.com/rbgirshick/yacs). Yaml is a very limited language, so we do not expect all features in detectron2 to be available through configs. If you need something that's not available in the config space, please write code using detectron2's API. With the introduction of a more powerful [LazyConfig system](lazyconfigs.md), we no longer add functionality / new keys to the Yacs/Yaml-based config system. ### Basic Usage Some basic usage of the `CfgNode` object is shown here. See more in [documentation](../modules/config.html#detectron2.config.CfgNode). ```python from detectron2.config import get_cfg cfg = get_cfg() # obtain detectron2's default config cfg.xxx = yyy # add new configs for your own custom components cfg.merge_from_file("my_cfg.yaml") # load values from a file cfg.merge_from_list(["MODEL.WEIGHTS", "weights.pth"]) # can also load values from a list of str print(cfg.dump()) # print formatted configs with open("output.yaml", "w") as f: f.write(cfg.dump()) # save config to file ``` In addition to the basic Yaml syntax, the config file can define a `_BASE_: base.yaml` field, which will load a base config file first. Values in the base config will be overwritten in sub-configs, if there are any conflicts. We provided several base configs for standard model architectures. Many builtin tools in detectron2 accept command line config overwrite: Key-value pairs provided in the command line will overwrite the existing values in the config file. For example, [demo.py](../../demo/demo.py) can be used with ```sh ./demo.py --config-file config.yaml [--other-options] \ --opts MODEL.WEIGHTS /path/to/weights INPUT.MIN_SIZE_TEST 1000 ``` To see a list of available configs in detectron2 and what they mean, check [Config References](../modules/config.html#config-references) ### Configs in Projects A project that lives outside the detectron2 library may define its own configs, which will need to be added for the project to be functional, e.g.: ```python from detectron2.projects.point_rend import add_pointrend_config cfg = get_cfg() # obtain detectron2's default config add_pointrend_config(cfg) # add pointrend's default config # ... ... ``` ### Best Practice with Configs 1. Treat the configs you write as "code": avoid copying them or duplicating them; use `_BASE_` to share common parts between configs. 2. Keep the configs you write simple: don't include keys that do not affect the experimental setting. ================================================ FILE: detectron2/docs/tutorials/data_loading.md ================================================ # Dataloader Dataloader is the component that provides data to models. A dataloader usually (but not necessarily) takes raw information from [datasets](./datasets.md), and process them into a format needed by the model. ## How the Existing Dataloader Works Detectron2 contains a builtin data loading pipeline. It's good to understand how it works, in case you need to write a custom one. Detectron2 provides two functions [build_detection_{train,test}_loader](../modules/data.html#detectron2.data.build_detection_train_loader) that create a default data loader from a given config. Here is how `build_detection_{train,test}_loader` work: 1. It takes the name of a registered dataset (e.g., "coco_2017_train") and loads a `list[dict]` representing the dataset items in a lightweight format. These dataset items are not yet ready to be used by the model (e.g., images are not loaded into memory, random augmentations have not been applied, etc.). Details about the dataset format and dataset registration can be found in [datasets](./datasets.md). 2. Each dict in this list is mapped by a function ("mapper"): * Users can customize this mapping function by specifying the "mapper" argument in `build_detection_{train,test}_loader`. The default mapper is [DatasetMapper](../modules/data.html#detectron2.data.DatasetMapper). * The output format of the mapper can be arbitrary, as long as it is accepted by the consumer of this data loader (usually the model). The outputs of the default mapper, after batching, follow the default model input format documented in [Use Models](./models.html#model-input-format). * The role of the mapper is to transform the lightweight representation of a dataset item into a format that is ready for the model to consume (including, e.g., read images, perform random data augmentation and convert to torch Tensors). If you would like to perform custom transformations to data, you often want a custom mapper. 3. The outputs of the mapper are batched (simply into a list). 4. This batched data is the output of the data loader. Typically, it's also the input of `model.forward()`. ## Write a Custom Dataloader Using a different "mapper" with `build_detection_{train,test}_loader(mapper=)` works for most use cases of custom data loading. For example, if you want to resize all images to a fixed size for training, use: ```python import detectron2.data.transforms as T from detectron2.data import DatasetMapper # the default mapper dataloader = build_detection_train_loader(cfg, mapper=DatasetMapper(cfg, is_train=True, augmentations=[ T.Resize((800, 800)) ])) # use this dataloader instead of the default ``` If the arguments of the default [DatasetMapper](../modules/data.html#detectron2.data.DatasetMapper) does not provide what you need, you may write a custom mapper function and use it instead, e.g.: ```python from detectron2.data import detection_utils as utils # Show how to implement a minimal mapper, similar to the default DatasetMapper def mapper(dataset_dict): dataset_dict = copy.deepcopy(dataset_dict) # it will be modified by code below # can use other ways to read image image = utils.read_image(dataset_dict["file_name"], format="BGR") # See "Data Augmentation" tutorial for details usage auginput = T.AugInput(image) transform = T.Resize((800, 800))(auginput) image = torch.from_numpy(auginput.image.transpose(2, 0, 1)) annos = [ utils.transform_instance_annotations(annotation, [transform], image.shape[1:]) for annotation in dataset_dict.pop("annotations") ] return { # create the format that the model expects "image": image, "instances": utils.annotations_to_instances(annos, image.shape[1:]) } dataloader = build_detection_train_loader(cfg, mapper=mapper) ``` If you want to change not only the mapper (e.g., in order to implement different sampling or batching logic), `build_detection_train_loader` won't work and you will need to write a different data loader. The data loader is simply a python iterator that produces [the format](./models.md) that the model accepts. You can implement it using any tools you like. No matter what to implement, it's recommended to check out [API documentation of detectron2.data](../modules/data) to learn more about the APIs of these functions. ## Use a Custom Dataloader If you use [DefaultTrainer](../modules/engine.html#detectron2.engine.defaults.DefaultTrainer), you can overwrite its `build_{train,test}_loader` method to use your own dataloader. See the [deeplab dataloader](../../projects/DeepLab/train_net.py) for an example. If you write your own training loop, you can plug in your data loader easily. ================================================ FILE: detectron2/docs/tutorials/datasets.md ================================================ # Use Custom Datasets This document explains how the dataset APIs ([DatasetCatalog](../modules/data.html#detectron2.data.DatasetCatalog), [MetadataCatalog](../modules/data.html#detectron2.data.MetadataCatalog)) work, and how to use them to add custom datasets. Datasets that have builtin support in detectron2 are listed in [builtin datasets](builtin_datasets.md). If you want to use a custom dataset while also reusing detectron2's data loaders, you will need to: 1. __Register__ your dataset (i.e., tell detectron2 how to obtain your dataset). 2. Optionally, __register metadata__ for your dataset. Next, we explain the above two concepts in detail. The [Colab tutorial](https://colab.research.google.com/drive/16jcaJoc6bCFAQ96jDe2HwtXj7BMD_-m5) has a live example of how to register and train on a dataset of custom formats. ### Register a Dataset To let detectron2 know how to obtain a dataset named "my_dataset", users need to implement a function that returns the items in your dataset and then tell detectron2 about this function: ```python def my_dataset_function(): ... return list[dict] in the following format from detectron2.data import DatasetCatalog DatasetCatalog.register("my_dataset", my_dataset_function) # later, to access the data: data: List[Dict] = DatasetCatalog.get("my_dataset") ``` Here, the snippet associates a dataset named "my_dataset" with a function that returns the data. The function must return the same data (with same order) if called multiple times. The registration stays effective until the process exits. The function can do arbitrary things and should return the data in `list[dict]`, each dict in either of the following formats: 1. Detectron2's standard dataset dict, described below. This will make it work with many other builtin features in detectron2, so it's recommended to use it when it's sufficient. 2. Any custom format. You can also return arbitrary dicts in your own format, such as adding extra keys for new tasks. Then you will need to handle them properly downstream as well. See below for more details. #### Standard Dataset Dicts For standard tasks (instance detection, instance/semantic/panoptic segmentation, keypoint detection), we load the original dataset into `list[dict]` with a specification similar to COCO's annotations. This is our standard representation for a dataset. Each dict contains information about one image. The dict may have the following fields, and the required fields vary based on what the dataloader or the task needs (see more below). ```eval_rst .. list-table:: :header-rows: 1 * - Task - Fields * - Common - file_name, height, width, image_id * - Instance detection/segmentation - annotations * - Semantic segmentation - sem_seg_file_name * - Panoptic segmentation - pan_seg_file_name, segments_info ``` + `file_name`: the full path to the image file. + `height`, `width`: integer. The shape of the image. + `image_id` (str or int): a unique id that identifies this image. Required by many evaluators to identify the images, but a dataset may use it for different purposes. + `annotations` (list[dict]): Required by __instance detection/segmentation or keypoint detection__ tasks. Each dict corresponds to annotations of one instance in this image, and may contain the following keys: + `bbox` (list[float], required): list of 4 numbers representing the bounding box of the instance. + `bbox_mode` (int, required): the format of bbox. It must be a member of [structures.BoxMode](../modules/structures.html#detectron2.structures.BoxMode). Currently supports: `BoxMode.XYXY_ABS`, `BoxMode.XYWH_ABS`. + `category_id` (int, required): an integer in the range [0, num_categories-1] representing the category label. The value num_categories is reserved to represent the "background" category, if applicable. + `segmentation` (list[list[float]] or dict): the segmentation mask of the instance. + If `list[list[float]]`, it represents a list of polygons, one for each connected component of the object. Each `list[float]` is one simple polygon in the format of `[x1, y1, ..., xn, yn]` (n≥3). The Xs and Ys are absolute coordinates in unit of pixels. + If `dict`, it represents the per-pixel segmentation mask in COCO's compressed RLE format. The dict should have keys "size" and "counts". You can convert a uint8 segmentation mask of 0s and 1s into such dict by `pycocotools.mask.encode(np.asarray(mask, order="F"))`. `cfg.INPUT.MASK_FORMAT` must be set to `bitmask` if using the default data loader with such format. + `keypoints` (list[float]): in the format of [x1, y1, v1,..., xn, yn, vn]. v[i] means the [visibility](http://cocodataset.org/#format-data) of this keypoint. `n` must be equal to the number of keypoint categories. The Xs and Ys are absolute real-value coordinates in range [0, W or H]. (Note that the keypoint coordinates in COCO format are integers in range [0, W-1 or H-1], which is different from our standard format. Detectron2 adds 0.5 to COCO keypoint coordinates to convert them from discrete pixel indices to floating point coordinates.) + `iscrowd`: 0 (default) or 1. Whether this instance is labeled as COCO's "crowd region". Don't include this field if you don't know what it means. If `annotations` is an empty list, it means the image is labeled to have no objects. Such images will by default be removed from training, but can be included using `DATALOADER.FILTER_EMPTY_ANNOTATIONS`. + `sem_seg_file_name` (str): The full path to the semantic segmentation ground truth file. It should be a grayscale image whose pixel values are integer labels. + `pan_seg_file_name` (str): The full path to panoptic segmentation ground truth file. It should be an RGB image whose pixel values are integer ids encoded using the [panopticapi.utils.id2rgb](https://github.com/cocodataset/panopticapi/) function. The ids are defined by `segments_info`. If an id does not appear in `segments_info`, the pixel is considered unlabeled and is usually ignored in training & evaluation. + `segments_info` (list[dict]): defines the meaning of each id in panoptic segmentation ground truth. Each dict has the following keys: + `id` (int): integer that appears in the ground truth image. + `category_id` (int): an integer in the range [0, num_categories-1] representing the category label. + `iscrowd`: 0 (default) or 1. Whether this instance is labeled as COCO's "crowd region". ```eval_rst .. note:: The PanopticFPN model does not use the panoptic segmentation format defined here, but a combination of both instance segmentation and semantic segmentation data format. See :doc:`builtin_datasets` for instructions on COCO. ``` Fast R-CNN (with pre-computed proposals) models are rarely used today. To train a Fast R-CNN, the following extra keys are needed: + `proposal_boxes` (array): 2D numpy array with shape (K, 4) representing K precomputed proposal boxes for this image. + `proposal_objectness_logits` (array): numpy array with shape (K, ), which corresponds to the objectness logits of proposals in 'proposal_boxes'. + `proposal_bbox_mode` (int): the format of the precomputed proposal bbox. It must be a member of [structures.BoxMode](../modules/structures.html#detectron2.structures.BoxMode). Default is `BoxMode.XYXY_ABS`. #### Custom Dataset Dicts for New Tasks In the `list[dict]` that your dataset function returns, the dictionary can also have __arbitrary custom data__. This will be useful for a new task that needs extra information not covered by the standard dataset dicts. In this case, you need to make sure the downstream code can handle your data correctly. Usually this requires writing a new `mapper` for the dataloader (see [Use Custom Dataloaders](./data_loading.md)). When designing a custom format, note that all dicts are stored in memory (sometimes serialized and with multiple copies). To save memory, each dict is meant to contain __small__ but sufficient information about each sample, such as file names and annotations. Loading full samples typically happens in the data loader. For attributes shared among the entire dataset, use `Metadata` (see below). To avoid extra memory, do not save such information inside each sample. ### "Metadata" for Datasets Each dataset is associated with some metadata, accessible through `MetadataCatalog.get(dataset_name).some_metadata`. Metadata is a key-value mapping that contains information that's shared among the entire dataset, and usually is used to interpret what's in the dataset, e.g., names of classes, colors of classes, root of files, etc. This information will be useful for augmentation, evaluation, visualization, logging, etc. The structure of metadata depends on what is needed from the corresponding downstream code. If you register a new dataset through `DatasetCatalog.register`, you may also want to add its corresponding metadata through `MetadataCatalog.get(dataset_name).some_key = some_value`, to enable any features that need the metadata. You can do it like this (using the metadata key "thing_classes" as an example): ```python from detectron2.data import MetadataCatalog MetadataCatalog.get("my_dataset").thing_classes = ["person", "dog"] ``` Here is a list of metadata keys that are used by builtin features in detectron2. If you add your own dataset without these metadata, some features may be unavailable to you: * `thing_classes` (list[str]): Used by all instance detection/segmentation tasks. A list of names for each instance/thing category. If you load a COCO format dataset, it will be automatically set by the function `load_coco_json`. * `thing_colors` (list[tuple(r, g, b)]): Pre-defined color (in [0, 255]) for each thing category. Used for visualization. If not given, random colors will be used. * `stuff_classes` (list[str]): Used by semantic and panoptic segmentation tasks. A list of names for each stuff category. * `stuff_colors` (list[tuple(r, g, b)]): Pre-defined color (in [0, 255]) for each stuff category. Used for visualization. If not given, random colors are used. * `ignore_label` (int): Used by semantic and panoptic segmentation tasks. Pixels in ground-truth annotations with this category label should be ignored in evaluation. Typically these are "unlabeled" pixels. * `keypoint_names` (list[str]): Used by keypoint detection. A list of names for each keypoint. * `keypoint_flip_map` (list[tuple[str]]): Used by keypoint detection. A list of pairs of names, where each pair are the two keypoints that should be flipped if the image is flipped horizontally during augmentation. * `keypoint_connection_rules`: list[tuple(str, str, (r, g, b))]. Each tuple specifies a pair of keypoints that are connected and the color (in [0, 255]) to use for the line between them when visualized. Some additional metadata that are specific to the evaluation of certain datasets (e.g. COCO): * `thing_dataset_id_to_contiguous_id` (dict[int->int]): Used by all instance detection/segmentation tasks in the COCO format. A mapping from instance class ids in the dataset to contiguous ids in range [0, #class). Will be automatically set by the function `load_coco_json`. * `stuff_dataset_id_to_contiguous_id` (dict[int->int]): Used when generating prediction json files for semantic/panoptic segmentation. A mapping from semantic segmentation class ids in the dataset to contiguous ids in [0, num_categories). It is useful for evaluation only. * `json_file`: The COCO annotation json file. Used by COCO evaluation for COCO-format datasets. * `panoptic_root`, `panoptic_json`: Used by COCO-format panoptic evaluation. * `evaluator_type`: Used by the builtin main training script to select evaluator. Don't use it in a new training script. You can just provide the [DatasetEvaluator](../modules/evaluation.html#detectron2.evaluation.DatasetEvaluator) for your dataset directly in your main script. ```eval_rst .. note:: In recognition, sometimes we use the term "thing" for instance-level tasks, and "stuff" for semantic segmentation tasks. Both are used in panoptic segmentation tasks. For background on the concept of "thing" and "stuff", see `On Seeing Stuff: The Perception of Materials by Humans and Machines `_. ``` ### Register a COCO Format Dataset If your instance-level (detection, segmentation, keypoint) dataset is already a json file in the COCO format, the dataset and its associated metadata can be registered easily with: ```python from detectron2.data.datasets import register_coco_instances register_coco_instances("my_dataset", {}, "json_annotation.json", "path/to/image/dir") ``` If your dataset is in COCO format but need to be further processed, or has extra custom per-instance annotations, the [load_coco_json](../modules/data.html#detectron2.data.datasets.load_coco_json) function might be useful. ### Update the Config for New Datasets Once you've registered the dataset, you can use the name of the dataset (e.g., "my_dataset" in example above) in `cfg.DATASETS.{TRAIN,TEST}`. There are other configs you might want to change to train or evaluate on new datasets: * `MODEL.ROI_HEADS.NUM_CLASSES` and `MODEL.RETINANET.NUM_CLASSES` are the number of thing classes for R-CNN and RetinaNet models, respectively. * `MODEL.ROI_KEYPOINT_HEAD.NUM_KEYPOINTS` sets the number of keypoints for Keypoint R-CNN. You'll also need to set [Keypoint OKS](http://cocodataset.org/#keypoints-eval) with `TEST.KEYPOINT_OKS_SIGMAS` for evaluation. * `MODEL.SEM_SEG_HEAD.NUM_CLASSES` sets the number of stuff classes for Semantic FPN & Panoptic FPN. * `TEST.DETECTIONS_PER_IMAGE` controls the maximum number of objects to be detected. Set it to a larger number if test images may contain >100 objects. * If you're training Fast R-CNN (with precomputed proposals), `DATASETS.PROPOSAL_FILES_{TRAIN,TEST}` need to match the datasets. The format of proposal files are documented [here](../modules/data.html#detectron2.data.load_proposals_into_dataset). New models (e.g. [TensorMask](../../projects/TensorMask), [PointRend](../../projects/PointRend)) often have similar configs of their own that need to be changed as well. ```eval_rst .. tip:: After changing the number of classes, certain layers in a pre-trained model will become incompatible and therefore cannot be loaded to the new model. This is expected, and loading such pre-trained models will produce warnings about such layers. ``` ================================================ FILE: detectron2/docs/tutorials/deployment.md ================================================ # Deployment Models written in Python need to go through an export process to become a deployable artifact. A few basic concepts about this process: __"Export method"__ is how a Python model is fully serialized to a deployable format. We support the following export methods: * `tracing`: see [pytorch documentation](https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html) to learn about it * `scripting`: see [pytorch documentation](https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html) to learn about it * `caffe2_tracing`: replace parts of the model by caffe2 operators, then use tracing. __"Format"__ is how a serialized model is described in a file, e.g. TorchScript, Caffe2 protobuf, ONNX format. __"Runtime"__ is an engine that loads a serialized model and executes it, e.g., PyTorch, Caffe2, TensorFlow, onnxruntime, TensorRT, etc. A runtime is often tied to a specific format (e.g. PyTorch needs TorchScript format, Caffe2 needs protobuf format). We currently support the following combination and each has some limitations: ```eval_rst +----------------------------+-------------+-------------+-----------------------------+ | Export Method | tracing | scripting | caffe2_tracing | +============================+=============+=============+=============================+ | **Formats** | TorchScript | TorchScript | Caffe2, TorchScript, ONNX | +----------------------------+-------------+-------------+-----------------------------+ | **Runtime** | PyTorch | PyTorch | Caffe2, PyTorch | +----------------------------+-------------+-------------+-----------------------------+ | C++/Python inference | ✅ | ✅ | ✅ | +----------------------------+-------------+-------------+-----------------------------+ | Dynamic resolution | ✅ | ✅ | ✅ | +----------------------------+-------------+-------------+-----------------------------+ | Batch size requirement | Constant | Dynamic | Batch inference unsupported | +----------------------------+-------------+-------------+-----------------------------+ | Extra runtime deps | torchvision | torchvision | Caffe2 ops (usually already | | | | | | | | | | included in PyTorch) | +----------------------------+-------------+-------------+-----------------------------+ | Faster/Mask/Keypoint R-CNN | ✅ | ✅ | ✅ | +----------------------------+-------------+-------------+-----------------------------+ | RetinaNet | ✅ | ✅ | ✅ | +----------------------------+-------------+-------------+-----------------------------+ | PointRend R-CNN | ✅ | ❌ | ❌ | +----------------------------+-------------+-------------+-----------------------------+ | Cascade R-CNN | ✅ | ❌ | ❌ | +----------------------------+-------------+-------------+-----------------------------+ ``` `caffe2_tracing` is going to be deprecated. We don't plan to work on additional support for other formats/runtime, but contributions are welcome. ## Deployment with Tracing or Scripting Models can be exported to TorchScript format, by either [tracing or scripting](https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html). The output model file can be loaded without detectron2 dependency in either Python or C++. The exported model often requires torchvision (or its C++ library) dependency for some custom ops. This feature requires PyTorch ≥ 1.8. ### Coverage Most official models under the meta architectures `GeneralizedRCNN` and `RetinaNet` are supported in both tracing and scripting mode. Cascade R-CNN and PointRend are currently supported in tracing. Users' custom extensions are supported if they are also scriptable or traceable. For models exported with tracing, dynamic input resolution is allowed, but batch size (number of input images) must be fixed. Scripting can support dynamic batch size. ### Usage The main export APIs for tracing and scripting are [TracingAdapter](../modules/export.html#detectron2.export.TracingAdapter) and [scripting_with_instances](../modules/export.html#detectron2.export.scripting_with_instances). Their usage is currently demonstrated in [test_export_torchscript.py](../../tests/test_export_torchscript.py) (see `TestScripting` and `TestTracing`) as well as the [deployment example](../../tools/deploy). Please check that these examples can run, and then modify for your use cases. The usage now requires some user effort and necessary knowledge for each model to workaround the limitation of scripting and tracing. In the future we plan to wrap these under simpler APIs to lower the bar to use them. ## Deployment with Caffe2-tracing We provide [Caffe2Tracer](../modules/export.html#detectron2.export.Caffe2Tracer) that performs the export logic. It replaces parts of the model with Caffe2 operators, and then export the model into Caffe2, TorchScript or ONNX format. The converted model is able to run in either Python or C++ without detectron2/torchvision dependency, on CPU or GPUs. It has a runtime optimized for CPU & mobile inference, but not optimized for GPU inference. This feature requires ONNX ≥ 1.6. ### Coverage Most official models under these 3 common meta architectures: `GeneralizedRCNN`, `RetinaNet`, `PanopticFPN` are supported. Cascade R-CNN is not supported. Batch inference is not supported. Users' custom extensions under these architectures (added through registration) are supported as long as they do not contain control flow or operators not available in Caffe2 (e.g. deformable convolution). For example, custom backbones and heads are often supported out of the box. ### Usage The APIs are listed at [the API documentation](../modules/export). We provide [export_model.py](../../tools/deploy/) as an example that uses these APIs to convert a standard model. For custom models/datasets, you can add them to this script. ### Use the model in C++/Python The model can be loaded in C++ and deployed with either Caffe2 or Pytorch runtime.. [C++ examples](../../tools/deploy/) for Mask R-CNN are given as a reference. Note that: * Models exported with `caffe2_tracing` method take a special input format described in [documentation](../modules/export.html#detectron2.export.Caffe2Tracer). This was taken care of in the C++ example. * The converted models do not contain post-processing operations that transform raw layer outputs into formatted predictions. For example, the C++ examples only produce raw outputs (28x28 masks) from the final layers that are not post-processed, because in actual deployment, an application often needs its custom lightweight post-processing, so this step is left for users. To help use the Caffe2-format model in python, we provide a python wrapper around the converted model, in the [Caffe2Model.\_\_call\_\_](../modules/export.html#detectron2.export.Caffe2Model.__call__) method. This method has an interface that's identical to the [pytorch versions of models](./models.md), and it internally applies pre/post-processing code to match the formats. This wrapper can serve as a reference for how to use Caffe2's python API, or for how to implement pre/post-processing in actual deployment. ## Conversion to TensorFlow [tensorpack Faster R-CNN](https://github.com/tensorpack/tensorpack/tree/master/examples/FasterRCNN/convert_d2) provides scripts to convert a few standard detectron2 R-CNN models to TensorFlow's pb format. It works by translating configs and weights, therefore only support a few models. ================================================ FILE: detectron2/docs/tutorials/evaluation.md ================================================ # Evaluation Evaluation is a process that takes a number of inputs/outputs pairs and aggregate them. You can always [use the model](./models.md) directly and just parse its inputs/outputs manually to perform evaluation. Alternatively, evaluation is implemented in detectron2 using the [DatasetEvaluator](../modules/evaluation.html#detectron2.evaluation.DatasetEvaluator) interface. Detectron2 includes a few `DatasetEvaluator` that computes metrics using standard dataset-specific APIs (e.g., COCO, LVIS). You can also implement your own `DatasetEvaluator` that performs some other jobs using the inputs/outputs pairs. For example, to count how many instances are detected on the validation set: ```python class Counter(DatasetEvaluator): def reset(self): self.count = 0 def process(self, inputs, outputs): for output in outputs: self.count += len(output["instances"]) def evaluate(self): # save self.count somewhere, or print it, or return it. return {"count": self.count} ``` ## Use evaluators To evaluate using the methods of evaluators manually: ```python def get_all_inputs_outputs(): for data in data_loader: yield data, model(data) evaluator.reset() for inputs, outputs in get_all_inputs_outputs(): evaluator.process(inputs, outputs) eval_results = evaluator.evaluate() ``` Evaluators can also be used with [inference_on_dataset](../modules/evaluation.html#detectron2.evaluation.inference_on_dataset). For example, ```python eval_results = inference_on_dataset( model, data_loader, DatasetEvaluators([COCOEvaluator(...), Counter()])) ``` This will execute `model` on all inputs from `data_loader`, and call evaluator to process them. Compared to running the evaluation manually using the model, the benefit of this function is that evaluators can be merged together using [DatasetEvaluators](../modules/evaluation.html#detectron2.evaluation.DatasetEvaluators), and all the evaluation can finish in one forward pass over the dataset. This function also provides accurate speed benchmarks for the given model and dataset. ## Evaluators for custom dataset Many evaluators in detectron2 are made for specific datasets, in order to obtain scores using each dataset's official API. In addition to that, two evaluators are able to evaluate any generic dataset that follows detectron2's [standard dataset format](./datasets.md), so they can be used to evaluate custom datasets: * [COCOEvaluator](../modules/evaluation.html#detectron2.evaluation.COCOEvaluator) is able to evaluate AP (Average Precision) for box detection, instance segmentation, keypoint detection on any custom dataset. * [SemSegEvaluator](../modules/evaluation.html#detectron2.evaluation.SemSegEvaluator) is able to evaluate semantic segmentation metrics on any custom dataset. ================================================ FILE: detectron2/docs/tutorials/extend.md ================================================ # Extend Detectron2's Defaults __Research is about doing things in new ways__. This brings a tension in how to create abstractions in code, which is a challenge for any research engineering project of a significant size: 1. On one hand, it needs to have very thin abstractions to allow for the possibility of doing everything in new ways. It should be reasonably easy to break existing abstractions and replace them with new ones. 2. On the other hand, such a project also needs reasonably high-level abstractions, so that users can easily do things in standard ways, without worrying too much about the details that only certain researchers care about. In detectron2, there are two types of interfaces that address this tension together: 1. Functions and classes that take a config (`cfg`) argument created from a yaml file (sometimes with few extra arguments). Such functions and classes implement the "standard default" behavior: it will read what it needs from a given config and do the "standard" thing. Users only need to load an expert-made config and pass it around, without having to worry about which arguments are used and what they all mean. See [Yacs Configs](configs.md) for a detailed tutorial. 2. Functions and classes that have well-defined explicit arguments. Each of these is a small building block of the entire system. They require users' expertise to understand what each argument should be, and require more effort to stitch together to a larger system. But they can be stitched together in more flexible ways. When you need to implement something not supported by the "standard defaults" included in detectron2, these well-defined components can be reused. The [LazyConfig system](lazyconfigs.md) relies on such functions and classes. 3. A few functions and classes are implemented with the [@configurable](../modules/config.html#detectron2.config.configurable) decorator - they can be called with either a config, or with explicit arguments, or a mixture of both. Their explicit argument interfaces are currently experimental. As an example, a Mask R-CNN model can be built in the following ways: 1. Config-only: ```python # load proper yaml config file, then model = build_model(cfg) ``` 2. Mixture of config and additional argument overrides: ```python model = GeneralizedRCNN( cfg, roi_heads=StandardROIHeads(cfg, batch_size_per_image=666), pixel_std=[57.0, 57.0, 57.0]) ``` 3. Full explicit arguments:
(click to expand) ```python model = GeneralizedRCNN( backbone=FPN( ResNet( BasicStem(3, 64, norm="FrozenBN"), ResNet.make_default_stages(50, stride_in_1x1=True, norm="FrozenBN"), out_features=["res2", "res3", "res4", "res5"], ).freeze(2), ["res2", "res3", "res4", "res5"], 256, top_block=LastLevelMaxPool(), ), proposal_generator=RPN( in_features=["p2", "p3", "p4", "p5", "p6"], head=StandardRPNHead(in_channels=256, num_anchors=3), anchor_generator=DefaultAnchorGenerator( sizes=[[32], [64], [128], [256], [512]], aspect_ratios=[0.5, 1.0, 2.0], strides=[4, 8, 16, 32, 64], offset=0.0, ), anchor_matcher=Matcher([0.3, 0.7], [0, -1, 1], allow_low_quality_matches=True), box2box_transform=Box2BoxTransform([1.0, 1.0, 1.0, 1.0]), batch_size_per_image=256, positive_fraction=0.5, pre_nms_topk=(2000, 1000), post_nms_topk=(1000, 1000), nms_thresh=0.7, ), roi_heads=StandardROIHeads( num_classes=80, batch_size_per_image=512, positive_fraction=0.25, proposal_matcher=Matcher([0.5], [0, 1], allow_low_quality_matches=False), box_in_features=["p2", "p3", "p4", "p5"], box_pooler=ROIPooler(7, (1.0 / 4, 1.0 / 8, 1.0 / 16, 1.0 / 32), 0, "ROIAlignV2"), box_head=FastRCNNConvFCHead( ShapeSpec(channels=256, height=7, width=7), conv_dims=[], fc_dims=[1024, 1024] ), box_predictor=FastRCNNOutputLayers( ShapeSpec(channels=1024), test_score_thresh=0.05, box2box_transform=Box2BoxTransform((10, 10, 5, 5)), num_classes=80, ), mask_in_features=["p2", "p3", "p4", "p5"], mask_pooler=ROIPooler(14, (1.0 / 4, 1.0 / 8, 1.0 / 16, 1.0 / 32), 0, "ROIAlignV2"), mask_head=MaskRCNNConvUpsampleHead( ShapeSpec(channels=256, width=14, height=14), num_classes=80, conv_dims=[256, 256, 256, 256, 256], ), ), pixel_mean=[103.530, 116.280, 123.675], pixel_std=[1.0, 1.0, 1.0], input_format="BGR", ) ```
If you only need the standard behavior, the [Beginner's Tutorial](./getting_started.md) should suffice. If you need to extend detectron2 to your own needs, see the following tutorials for more details: * Detectron2 includes a few standard datasets. To use custom ones, see [Use Custom Datasets](./datasets.md). * Detectron2 contains the standard logic that creates a data loader for training/testing from a dataset, but you can write your own as well. See [Use Custom Data Loaders](./data_loading.md). * Detectron2 implements many standard detection models, and provide ways for you to overwrite their behaviors. See [Use Models](./models.md) and [Write Models](./write-models.md). * Detectron2 provides a default training loop that is good for common training tasks. You can customize it with hooks, or write your own loop instead. See [training](./training.md). ================================================ FILE: detectron2/docs/tutorials/getting_started.md ================================================ ## Getting Started with Detectron2 This document provides a brief intro of the usage of builtin command-line tools in detectron2. For a tutorial that involves actual coding with the API, see our [Colab Notebook](https://colab.research.google.com/drive/16jcaJoc6bCFAQ96jDe2HwtXj7BMD_-m5) which covers how to run inference with an existing model, and how to train a builtin model on a custom dataset. ### Inference Demo with Pre-trained Models 1. Pick a model and its config file from [model zoo](MODEL_ZOO.md), for example, `mask_rcnn_R_50_FPN_3x.yaml`. 2. We provide `demo.py` that is able to demo builtin configs. Run it with: ``` cd demo/ python demo.py --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml \ --input input1.jpg input2.jpg \ [--other-options] --opts MODEL.WEIGHTS detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl ``` The configs are made for training, therefore we need to specify `MODEL.WEIGHTS` to a model from model zoo for evaluation. This command will run the inference and show visualizations in an OpenCV window. For details of the command line arguments, see `demo.py -h` or look at its source code to understand its behavior. Some common arguments are: * To run __on your webcam__, replace `--input files` with `--webcam`. * To run __on a video__, replace `--input files` with `--video-input video.mp4`. * To run __on cpu__, add `MODEL.DEVICE cpu` after `--opts`. * To save outputs to a directory (for images) or a file (for webcam or video), use `--output`. ### Training & Evaluation in Command Line We provide two scripts in "tools/plain_train_net.py" and "tools/train_net.py", that are made to train all the configs provided in detectron2. You may want to use it as a reference to write your own training script. Compared to "train_net.py", "plain_train_net.py" supports fewer default features. It also includes fewer abstraction, therefore is easier to add custom logic. To train a model with "train_net.py", first setup the corresponding datasets following [datasets/README.md](./datasets/README.md), then run: ``` cd tools/ ./train_net.py --num-gpus 8 \ --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml ``` The configs are made for 8-GPU training. To train on 1 GPU, you may need to [change some parameters](https://arxiv.org/abs/1706.02677), e.g.: ``` ./train_net.py \ --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml \ --num-gpus 1 SOLVER.IMS_PER_BATCH 2 SOLVER.BASE_LR 0.0025 ``` To evaluate a model's performance, use ``` ./train_net.py \ --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml \ --eval-only MODEL.WEIGHTS /path/to/checkpoint_file ``` For more options, see `./train_net.py -h`. ### Use Detectron2 APIs in Your Code See our [Colab Notebook](https://colab.research.google.com/drive/16jcaJoc6bCFAQ96jDe2HwtXj7BMD_-m5) to learn how to use detectron2 APIs to: 1. run inference with an existing model 2. train a builtin model on a custom dataset See [detectron2/projects](https://github.com/facebookresearch/detectron2/tree/main/projects) for more ways to build your project on detectron2. ================================================ FILE: detectron2/docs/tutorials/index.rst ================================================ Tutorials ====================================== .. toctree:: :maxdepth: 2 install getting_started builtin_datasets extend datasets data_loading augmentation models write-models training evaluation configs lazyconfigs deployment ================================================ FILE: detectron2/docs/tutorials/install.md ================================================ ## Installation ### Requirements - Linux or macOS with Python ≥ 3.7 - PyTorch ≥ 1.8 and [torchvision](https://github.com/pytorch/vision/) that matches the PyTorch installation. Install them together at [pytorch.org](https://pytorch.org) to make sure of this - OpenCV is optional but needed by demo and visualization ### Build Detectron2 from Source gcc & g++ ≥ 5.4 are required. [ninja](https://ninja-build.org/) is optional but recommended for faster build. After having them, run: ``` python -m pip install 'git+https://github.com/facebookresearch/detectron2.git' # (add --user if you don't have permission) # Or, to install it from a local clone: git clone https://github.com/facebookresearch/detectron2.git python -m pip install -e detectron2 # On macOS, you may need to prepend the above commands with a few environment variables: CC=clang CXX=clang++ ARCHFLAGS="-arch x86_64" python -m pip install ... ``` To __rebuild__ detectron2 that's built from a local clone, use `rm -rf build/ **/*.so` to clean the old build first. You often need to rebuild detectron2 after reinstalling PyTorch. ### Install Pre-Built Detectron2 (Linux only) Choose from this table to install [v0.6 (Oct 2021)](https://github.com/facebookresearch/detectron2/releases):
CUDA torch 1.10torch 1.9torch 1.8
11.3
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu113/torch1.10/index.html
11.1
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu111/torch1.10/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu111/torch1.9/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu111/torch1.8/index.html
10.2
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu102/torch1.10/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu102/torch1.9/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu102/torch1.8/index.html
10.1
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cu101/torch1.8/index.html
cpu
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cpu/torch1.10/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cpu/torch1.9/index.html
install
python -m pip install detectron2 -f \
  https://dl.fbaipublicfiles.com/detectron2/wheels/cpu/torch1.8/index.html
Note that: 1. The pre-built packages have to be used with corresponding version of CUDA and the official package of PyTorch. Otherwise, please build detectron2 from source. 2. New packages are released every few months. Therefore, packages may not contain latest features in the main branch and may not be compatible with the main branch of a research project that uses detectron2 (e.g. those in [projects](projects)). ### Common Installation Issues Click each issue for its solutions:
Undefined symbols that looks like "TH..","at::Tensor...","torch..."
This usually happens when detectron2 or torchvision is not compiled with the version of PyTorch you're running. If the error comes from a pre-built torchvision, uninstall torchvision and pytorch and reinstall them following [pytorch.org](http://pytorch.org). So the versions will match. If the error comes from a pre-built detectron2, check [release notes](https://github.com/facebookresearch/detectron2/releases), uninstall and reinstall the correct pre-built detectron2 that matches pytorch version. If the error comes from detectron2 or torchvision that you built manually from source, remove files you built (`build/`, `**/*.so`) and rebuild it so it can pick up the version of pytorch currently in your environment. If the above instructions do not resolve this problem, please provide an environment (e.g. a dockerfile) that can reproduce the issue.
Missing torch dynamic libraries, OR segmentation fault immediately when using detectron2. This usually happens when detectron2 or torchvision is not compiled with the version of PyTorch you're running. See the previous common issue for the solution.
Undefined C++ symbols (e.g. "GLIBCXX..") or C++ symbols not found.
Usually it's because the library is compiled with a newer C++ compiler but run with an old C++ runtime. This often happens with old anaconda. It may help to run `conda update libgcc` to upgrade its runtime. The fundamental solution is to avoid the mismatch, either by compiling using older version of C++ compiler, or run the code with proper C++ runtime. To run the code with a specific C++ runtime, you can use environment variable `LD_PRELOAD=/path/to/libstdc++.so`.
"nvcc not found" or "Not compiled with GPU support" or "Detectron2 CUDA Compiler: not available".
CUDA is not found when building detectron2. You should make sure ``` python -c 'import torch; from torch.utils.cpp_extension import CUDA_HOME; print(torch.cuda.is_available(), CUDA_HOME)' ``` print `(True, a directory with cuda)` at the time you build detectron2. Most models can run inference (but not training) without GPU support. To use CPUs, set `MODEL.DEVICE='cpu'` in the config.
"invalid device function" or "no kernel image is available for execution".
Two possibilities: * You build detectron2 with one version of CUDA but run it with a different version. To check whether it is the case, use `python -m detectron2.utils.collect_env` to find out inconsistent CUDA versions. In the output of this command, you should expect "Detectron2 CUDA Compiler", "CUDA_HOME", "PyTorch built with - CUDA" to contain cuda libraries of the same version. When they are inconsistent, you need to either install a different build of PyTorch (or build by yourself) to match your local CUDA installation, or install a different version of CUDA to match PyTorch. * PyTorch/torchvision/Detectron2 is not built for the correct GPU SM architecture (aka. compute capability). The architecture included by PyTorch/detectron2/torchvision is available in the "architecture flags" in `python -m detectron2.utils.collect_env`. It must include the architecture of your GPU, which can be found at [developer.nvidia.com/cuda-gpus](https://developer.nvidia.com/cuda-gpus). If you're using pre-built PyTorch/detectron2/torchvision, they have included support for most popular GPUs already. If not supported, you need to build them from source. When building detectron2/torchvision from source, they detect the GPU device and build for only the device. This means the compiled code may not work on a different GPU device. To recompile them for the correct architecture, remove all installed/compiled files, and rebuild them with the `TORCH_CUDA_ARCH_LIST` environment variable set properly. For example, `export TORCH_CUDA_ARCH_LIST="6.0;7.0"` makes it compile for both P100s and V100s.
Undefined CUDA symbols; Cannot open libcudart.so
The version of NVCC you use to build detectron2 or torchvision does not match the version of CUDA you are running with. This often happens when using anaconda's CUDA runtime. Use `python -m detectron2.utils.collect_env` to find out inconsistent CUDA versions. In the output of this command, you should expect "Detectron2 CUDA Compiler", "CUDA_HOME", "PyTorch built with - CUDA" to contain cuda libraries of the same version. When they are inconsistent, you need to either install a different build of PyTorch (or build by yourself) to match your local CUDA installation, or install a different version of CUDA to match PyTorch.
C++ compilation errors from NVCC / NVRTC, or "Unsupported gpu architecture"
A few possibilities: 1. Local CUDA/NVCC version has to match the CUDA version of your PyTorch. Both can be found in `python collect_env.py` (download from [here](./detectron2/utils/collect_env.py)). When they are inconsistent, you need to either install a different build of PyTorch (or build by yourself) to match your local CUDA installation, or install a different version of CUDA to match PyTorch. 2. Local CUDA/NVCC version shall support the SM architecture (a.k.a. compute capability) of your GPU. The capability of your GPU can be found at [developer.nvidia.com/cuda-gpus](https://developer.nvidia.com/cuda-gpus). The capability supported by NVCC is listed at [here](https://gist.github.com/ax3l/9489132). If your NVCC version is too old, this can be workaround by setting environment variable `TORCH_CUDA_ARCH_LIST` to a lower, supported capability. 3. The combination of NVCC and GCC you use is incompatible. You need to change one of their versions. See [here](https://gist.github.com/ax3l/9489132) for some valid combinations. Notably, CUDA<=10.1.105 doesn't support GCC>7.3. The CUDA/GCC version used by PyTorch can be found by `print(torch.__config__.show())`.
"ImportError: cannot import name '_C'".
Please build and install detectron2 following the instructions above. Or, if you are running code from detectron2's root directory, `cd` to a different one. Otherwise you may not import the code that you installed.
Any issue on windows.
Detectron2 is continuously built on windows with [CircleCI](https://app.circleci.com/pipelines/github/facebookresearch/detectron2?branch=main). However we do not provide official support for it. PRs that improves code compatibility on windows are welcome.
ONNX conversion segfault after some "TraceWarning".
The ONNX package is compiled with a too old compiler. Please build and install ONNX from its source code using a compiler whose version is closer to what's used by PyTorch (available in `torch.__config__.show()`).
"library not found for -lstdc++" on older version of MacOS
See [this stackoverflow answer](https://stackoverflow.com/questions/56083725/macos-build-issues-lstdc-not-found-while-building-python-package).
### Installation inside specific environments: * __Colab__: see our [Colab Tutorial](https://colab.research.google.com/drive/16jcaJoc6bCFAQ96jDe2HwtXj7BMD_-m5) which has step-by-step instructions. * __Docker__: The official [Dockerfile](docker) installs detectron2 with a few simple commands. ================================================ FILE: detectron2/docs/tutorials/lazyconfigs.md ================================================ # Lazy Configs The traditional yacs-based config system provides basic, standard functionalities. However, it does not offer enough flexibility for many new projects. We develop an alternative, non-intrusive config system that can be used with detectron2 or potentially any other complex projects. ## Python Syntax Our config objects are still dictionaries. Instead of using Yaml to define dictionaries, we create dictionaries in Python directly. This gives users the following power that doesn't exist in Yaml: * Easily manipulate the dictionary (addition & deletion) using Python. * Write simple arithmetics or call simple functions. * Use more data types / objects. * Import / compose other config files, using the familiar Python import syntax. A Python config file can be loaded like this: ```python # config.py: a = dict(x=1, y=2, z=dict(xx=1)) b = dict(x=3, y=4) # my_code.py: from detectron2.config import LazyConfig cfg = LazyConfig.load("path/to/config.py") # an omegaconf dictionary assert cfg.a.z.xx == 1 ``` After [LazyConfig.load](../modules/config.html#detectron2.config.LazyConfig.load), `cfg` will be a dictionary that contains all dictionaries defined in the global scope of the config file. Note that: * All dictionaries are turned to an [omegaconf](https://omegaconf.readthedocs.io/) config object during loading. This enables access to omegaconf features, such as its [access syntax](https://omegaconf.readthedocs.io/en/2.1_branch/usage.html#access-and-manipulation) and [interpolation](https://omegaconf.readthedocs.io/en/2.1_branch/usage.html#variable-interpolation). * Absolute imports in `config.py` works the same as in regular Python. * Relative imports can only import dictionaries from config files. They are simply a syntax sugar for [LazyConfig.load_rel](../modules/config.html#detectron2.config.LazyConfig.load_rel). They can load Python files at relative path without requiring `__init__.py`. [LazyConfig.save](../modules/config.html#detectron2.config.LazyConfig.save) can save a config object to yaml. Note that this is not always successful if non-serializable objects appear in the config file (e.g. lambdas). It is up to users whether to sacrifice the ability to save in exchange for flexibility. ## Recursive Instantiation The LazyConfig system heavily uses recursive instantiation, which is a pattern that uses a dictionary to describe a call to a function/class. The dictionary consists of: 1. A "\_target\_" key which contains path to the callable, such as "module.submodule.class_name". 2. Other keys that represent arguments to pass to the callable. Arguments themselves can be defined using recursive instantiation. We provide a helper function [LazyCall](../modules/config.html#detectron2.config.LazyCall) that helps create such dictionaries. The following code using `LazyCall` ```python from detectron2.config import LazyCall as L from my_app import Trainer, Optimizer cfg = L(Trainer)( optimizer=L(Optimizer)( lr=0.01, algo="SGD" ) ) ``` creates a dictionary like this: ```python cfg = { "_target_": "my_app.Trainer", "optimizer": { "_target_": "my_app.Optimizer", "lr": 0.01, "algo": "SGD" } } ``` By representing objects using such dictionaries, a general [instantiate](../modules/config.html#detectron2.config.instantiate) function can turn them into actual objects, i.e.: ```python from detectron2.config import instantiate trainer = instantiate(cfg) # equivalent to: # from my_app import Trainer, Optimizer # trainer = Trainer(optimizer=Optimizer(lr=0.01, algo="SGD")) ``` This pattern is powerful enough to describe very complex objects, e.g.:
A Full Mask R-CNN described in recursive instantiation (click to expand) ```eval_rst .. literalinclude:: ../../configs/common/models/mask_rcnn_fpn.py :language: python :linenos: ```
There are also objects or logic that cannot be described simply by a dictionary, such as reused objects or method calls. They may require some refactoring to work with recursive instantiation. ## Using Model Zoo LazyConfigs We provide some configs in the model zoo using the LazyConfig system, for example: * [common baselines](../../configs/common/). * [new Mask R-CNN baselines](../../configs/new_baselines/) After installing detectron2, they can be loaded by the model zoo API [model_zoo.get_config](../modules/model_zoo.html#detectron2.model_zoo.get_config). Using these as references, you're free to define custom config structure / fields for your own project, as long as your training script can understand them. Despite of this, our model zoo configs still follow some simple conventions for consistency, e.g. `cfg.model` defines a model object, `cfg.dataloader.{train,test}` defines dataloader objects, and `cfg.train` contains training options in key-value form. In addition to `print()`, a better way to view the structure of a config is like this: ```python from detectron2.model_zoo import get_config from detectron2.config import LazyConfig print(LazyConfig.to_py(get_config("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.py"))) ``` From the output it's easier to find relevant options to change, e.g. `dataloader.train.total_batch_size` for the batch size, or `optimizer.lr` for base learning rate. We provide a reference training script [tools/lazyconfig_train_net.py](../../tools/lazyconfig_train_net.py), that can train/eval our model zoo configs. It also shows how to support command line value overrides. To demonstrate the power and flexibility of the new system, we show that [a simple config file](../../configs/Misc/torchvision_imagenet_R_50.py) can let detectron2 train an ImageNet classification model from torchvision, even though detectron2 contains no features about ImageNet classification. This can serve as a reference for using detectron2 in other deep learning tasks. ## Summary By using recursive instantiation to create objects, we avoid passing a giant config to many places, because `cfg` is only passed to `instantiate`. This has the following benefits: * It's __non-intrusive__: objects to be constructed are config-agnostic, regular Python functions/classes. They can even live in other libraries. For example, `{"_target_": "torch.nn.Conv2d", "in_channels": 10, "out_channels": 10, "kernel_size": 1}` defines a conv layer. * __Clarity__ of what function/classes will be called, and what arguments they use. * `cfg` doesn't need pre-defined keys and structures. It's valid as long as it translates to valid code. This gives a lot more __flexibility__. * You can still pass huge dictionaries as arguments, just like the old way. Recursive instantiation and Python syntax are orthogonal: you can use one without the other. But by putting them together, the config file looks a lot like the code that will be executed: ![img](./lazyconfig.jpg) However, the config file just defines dictionaries, which can be easily manipulated further by composition or overrides. The corresponding code will only be executed later when `instantiate` is called. In some way, in config files we're writing "editable code" that will be "lazily executed" later when needed. That's why we call this system "LazyConfig". ================================================ FILE: detectron2/docs/tutorials/models.md ================================================ # Use Models ## Build Models from Yacs Config From a yacs config object, models (and their sub-models) can be built by functions such as `build_model`, `build_backbone`, `build_roi_heads`: ```python from detectron2.modeling import build_model model = build_model(cfg) # returns a torch.nn.Module ``` `build_model` only builds the model structure and fills it with random parameters. See below for how to load an existing checkpoint to the model and how to use the `model` object. ### Load/Save a Checkpoint ```python from detectron2.checkpoint import DetectionCheckpointer DetectionCheckpointer(model).load(file_path_or_url) # load a file, usually from cfg.MODEL.WEIGHTS checkpointer = DetectionCheckpointer(model, save_dir="output") checkpointer.save("model_999") # save to output/model_999.pth ``` Detectron2's checkpointer recognizes models in pytorch's `.pth` format, as well as the `.pkl` files in our model zoo. See [API doc](../modules/checkpoint.html#detectron2.checkpoint.DetectionCheckpointer) for more details about its usage. The model files can be arbitrarily manipulated using `torch.{load,save}` for `.pth` files or `pickle.{dump,load}` for `.pkl` files. ### Use a Model A model can be called by `outputs = model(inputs)`, where `inputs` is a `list[dict]`. Each dict corresponds to one image and the required keys depend on the type of model, and whether the model is in training or evaluation mode. For example, in order to do inference, all existing models expect the "image" key, and optionally "height" and "width". The detailed format of inputs and outputs of existing models are explained below. __Training__: When in training mode, all models are required to be used under an `EventStorage`. The training statistics will be put into the storage: ```python from detectron2.utils.events import EventStorage with EventStorage() as storage: losses = model(inputs) ``` __Inference__: If you only want to do simple inference using an existing model, [DefaultPredictor](../modules/engine.html#detectron2.engine.defaults.DefaultPredictor) is a wrapper around model that provides such basic functionality. It includes default behavior including model loading, preprocessing, and operates on single image rather than batches. See its documentation for usage. You can also run inference directly like this: ```python model.eval() with torch.no_grad(): outputs = model(inputs) ``` ### Model Input Format Users can implement custom models that support any arbitrary input format. Here we describe the standard input format that all builtin models support in detectron2. They all take a `list[dict]` as the inputs. Each dict corresponds to information about one image. The dict may contain the following keys: * "image": `Tensor` in (C, H, W) format. The meaning of channels are defined by `cfg.INPUT.FORMAT`. Image normalization, if any, will be performed inside the model using `cfg.MODEL.PIXEL_{MEAN,STD}`. * "height", "width": the **desired** output height and width **in inference**, which is not necessarily the same as the height or width of the `image` field. For example, the `image` field contains the resized image, if resize is used as a preprocessing step. But you may want the outputs to be in **original** resolution. If provided, the model will produce output in this resolution, rather than in the resolution of the `image` as input into the model. This is more efficient and accurate. * "instances": an [Instances](../modules/structures.html#detectron2.structures.Instances) object for training, with the following fields: + "gt_boxes": a [Boxes](../modules/structures.html#detectron2.structures.Boxes) object storing N boxes, one for each instance. + "gt_classes": `Tensor` of long type, a vector of N labels, in range [0, num_categories). + "gt_masks": a [PolygonMasks](../modules/structures.html#detectron2.structures.PolygonMasks) or [BitMasks](../modules/structures.html#detectron2.structures.BitMasks) object storing N masks, one for each instance. + "gt_keypoints": a [Keypoints](../modules/structures.html#detectron2.structures.Keypoints) object storing N keypoint sets, one for each instance. * "sem_seg": `Tensor[int]` in (H, W) format. The semantic segmentation ground truth for training. Values represent category labels starting from 0. * "proposals": an [Instances](../modules/structures.html#detectron2.structures.Instances) object used only in Fast R-CNN style models, with the following fields: + "proposal_boxes": a [Boxes](../modules/structures.html#detectron2.structures.Boxes) object storing P proposal boxes. + "objectness_logits": `Tensor`, a vector of P scores, one for each proposal. For inference of builtin models, only "image" key is required, and "width/height" are optional. We currently don't define standard input format for panoptic segmentation training, because models now use custom formats produced by custom data loaders. #### How it connects to data loader: The output of the default [DatasetMapper]( ../modules/data.html#detectron2.data.DatasetMapper) is a dict that follows the above format. After the data loader performs batching, it becomes `list[dict]` which the builtin models support. ### Model Output Format When in training mode, the builtin models output a `dict[str->ScalarTensor]` with all the losses. When in inference mode, the builtin models output a `list[dict]`, one dict for each image. Based on the tasks the model is doing, each dict may contain the following fields: * "instances": [Instances](../modules/structures.html#detectron2.structures.Instances) object with the following fields: * "pred_boxes": [Boxes](../modules/structures.html#detectron2.structures.Boxes) object storing N boxes, one for each detected instance. * "scores": `Tensor`, a vector of N confidence scores. * "pred_classes": `Tensor`, a vector of N labels in range [0, num_categories). + "pred_masks": a `Tensor` of shape (N, H, W), masks for each detected instance. + "pred_keypoints": a `Tensor` of shape (N, num_keypoint, 3). Each row in the last dimension is (x, y, score). Confidence scores are larger than 0. * "sem_seg": `Tensor` of (num_categories, H, W), the semantic segmentation prediction. * "proposals": [Instances](../modules/structures.html#detectron2.structures.Instances) object with the following fields: * "proposal_boxes": [Boxes](../modules/structures.html#detectron2.structures.Boxes) object storing N boxes. * "objectness_logits": a torch vector of N confidence scores. * "panoptic_seg": A tuple of `(pred: Tensor, segments_info: Optional[list[dict]])`. The `pred` tensor has shape (H, W), containing the segment id of each pixel. * If `segments_info` exists, each dict describes one segment id in `pred` and has the following fields: * "id": the segment id * "isthing": whether the segment is a thing or stuff * "category_id": the category id of this segment. If a pixel's id does not exist in `segments_info`, it is considered to be void label defined in [Panoptic Segmentation](https://arxiv.org/abs/1801.00868). * If `segments_info` is None, all pixel values in `pred` must be ≥ -1. Pixels with value -1 are assigned void labels. Otherwise, the category id of each pixel is obtained by `category_id = pixel // metadata.label_divisor`. ### Partially execute a model: Sometimes you may want to obtain an intermediate tensor inside a model, such as the input of certain layer, the output before post-processing. Since there are typically hundreds of intermediate tensors, there isn't an API that provides you the intermediate result you need. You have the following options: 1. Write a (sub)model. Following the [tutorial](./write-models.md), you can rewrite a model component (e.g. a head of a model), such that it does the same thing as the existing component, but returns the output you need. 2. Partially execute a model. You can create the model as usual, but use custom code to execute it instead of its `forward()`. For example, the following code obtains mask features before mask head. ```python images = ImageList.from_tensors(...) # preprocessed input tensor model = build_model(cfg) model.eval() features = model.backbone(images.tensor) proposals, _ = model.proposal_generator(images, features) instances, _ = model.roi_heads(images, features, proposals) mask_features = [features[f] for f in model.roi_heads.in_features] mask_features = model.roi_heads.mask_pooler(mask_features, [x.pred_boxes for x in instances]) ``` 3. Use [forward hooks](https://pytorch.org/tutorials/beginner/former_torchies/nnft_tutorial.html#forward-and-backward-function-hooks). Forward hooks can help you obtain inputs or outputs of a certain module. If they are not exactly what you want, they can at least be used together with partial execution to obtain other tensors. All options require you to read documentation and sometimes code of the existing models to understand the internal logic, in order to write code to obtain the internal tensors. ================================================ FILE: detectron2/docs/tutorials/training.md ================================================ # Training From the previous tutorials, you may now have a custom model and a data loader. To run training, users typically have a preference in one of the following two styles: ### Custom Training Loop With a model and a data loader ready, everything else needed to write a training loop can be found in PyTorch, and you are free to write the training loop yourself. This style allows researchers to manage the entire training logic more clearly and have full control. One such example is provided in [tools/plain_train_net.py](../../tools/plain_train_net.py). Any customization on the training logic is then easily controlled by the user. ### Trainer Abstraction We also provide a standardized "trainer" abstraction with a hook system that helps simplify the standard training behavior. It includes the following two instantiations: * [SimpleTrainer](../modules/engine.html#detectron2.engine.SimpleTrainer) provides a minimal training loop for single-cost single-optimizer single-data-source training, with nothing else. Other tasks (checkpointing, logging, etc) can be implemented using [the hook system](../modules/engine.html#detectron2.engine.HookBase). * [DefaultTrainer](../modules/engine.html#detectron2.engine.defaults.DefaultTrainer) is a `SimpleTrainer` initialized from a yacs config, used by [tools/train_net.py](../../tools/train_net.py) and many scripts. It includes more standard default behaviors that one might want to opt in, including default configurations for optimizer, learning rate schedule, logging, evaluation, checkpointing etc. To customize a `DefaultTrainer`: 1. For simple customizations (e.g. change optimizer, evaluator, LR scheduler, data loader, etc.), overwrite [its methods](../modules/engine.html#detectron2.engine.defaults.DefaultTrainer) in a subclass, just like [tools/train_net.py](../../tools/train_net.py). 2. For extra tasks during training, check the [hook system](../modules/engine.html#detectron2.engine.HookBase) to see if it's supported. As an example, to print hello during training: ```python class HelloHook(HookBase): def after_step(self): if self.trainer.iter % 100 == 0: print(f"Hello at iteration {self.trainer.iter}!") ``` 3. Using a trainer+hook system means there will always be some non-standard behaviors that cannot be supported, especially in research. For this reason, we intentionally keep the trainer & hook system minimal, rather than powerful. If anything cannot be achieved by such a system, it's easier to start from [tools/plain_train_net.py](../../tools/plain_train_net.py) to implement custom training logic manually. ### Logging of Metrics During training, detectron2 models and trainer put metrics to a centralized [EventStorage](../modules/utils.html#detectron2.utils.events.EventStorage). You can use the following code to access it and log metrics to it: ```python from detectron2.utils.events import get_event_storage # inside the model: if self.training: value = # compute the value from inputs storage = get_event_storage() storage.put_scalar("some_accuracy", value) ``` Refer to its documentation for more details. Metrics are then written to various destinations with [EventWriter](../modules/utils.html#module-detectron2.utils.events). DefaultTrainer enables a few `EventWriter` with default configurations. See above for how to customize them. ================================================ FILE: detectron2/docs/tutorials/write-models.md ================================================ # Write Models If you are trying to do something completely new, you may wish to implement a model entirely from scratch. However, in many situations you may be interested in modifying or extending some components of an existing model. Therefore, we also provide mechanisms that let users override the behavior of certain internal components of standard models. ## Register New Components For common concepts that users often want to customize, such as "backbone feature extractor", "box head", we provide a registration mechanism for users to inject custom implementation that will be immediately available to use in config files. For example, to add a new backbone, import this code in your code: ```python from detectron2.modeling import BACKBONE_REGISTRY, Backbone, ShapeSpec @BACKBONE_REGISTRY.register() class ToyBackbone(Backbone): def __init__(self, cfg, input_shape): super().__init__() # create your own backbone self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=16, padding=3) def forward(self, image): return {"conv1": self.conv1(image)} def output_shape(self): return {"conv1": ShapeSpec(channels=64, stride=16)} ``` In this code, we implement a new backbone following the interface of the [Backbone](../modules/modeling.html#detectron2.modeling.Backbone) class, and register it into the [BACKBONE_REGISTRY](../modules/modeling.html#detectron2.modeling.BACKBONE_REGISTRY) which requires subclasses of `Backbone`. After importing this code, detectron2 can link the name of the class to its implementation. Therefore you can write the following code: ```python cfg = ... # read a config cfg.MODEL.BACKBONE.NAME = 'ToyBackbone' # or set it in the config file model = build_model(cfg) # it will find `ToyBackbone` defined above ``` As another example, to add new abilities to the ROI heads in the Generalized R-CNN meta-architecture, you can implement a new [ROIHeads](../modules/modeling.html#detectron2.modeling.ROIHeads) subclass and put it in the `ROI_HEADS_REGISTRY`. [DensePose](../../projects/DensePose) and [MeshRCNN](https://github.com/facebookresearch/meshrcnn) are two examples that implement new ROIHeads to perform new tasks. And [projects/](../../projects/) contains more examples that implement different architectures. A complete list of registries can be found in [API documentation](../modules/modeling.html#model-registries). You can register components in these registries to customize different parts of a model, or the entire model. ## Construct Models with Explicit Arguments Registry is a bridge to connect names in config files to the actual code. They are meant to cover a few main components that users frequently need to replace. However, the capability of a text-based config file is sometimes limited and some deeper customization may be available only through writing code. Most model components in detectron2 have a clear `__init__` interface that documents what input arguments it needs. Calling them with custom arguments will give you a custom variant of the model. As an example, to use __custom loss function__ in the box head of a Faster R-CNN, we can do the following: 1. Losses are currently computed in [FastRCNNOutputLayers](../modules/modeling.html#detectron2.modeling.FastRCNNOutputLayers). We need to implement a variant or a subclass of it, with custom loss functions, named `MyRCNNOutput`. 2. Call `StandardROIHeads` with `box_predictor=MyRCNNOutput()` argument instead of the builtin `FastRCNNOutputLayers`. If all other arguments should stay unchanged, this can be easily achieved by using the [configurable `__init__`](../modules/config.html#detectron2.config.configurable) mechanism: ```python roi_heads = StandardROIHeads( cfg, backbone.output_shape(), box_predictor=MyRCNNOutput(...) ) ``` 3. (optional) If we want to enable this new model from a config file, registration is needed: ```python @ROI_HEADS_REGISTRY.register() class MyStandardROIHeads(StandardROIHeads): def __init__(self, cfg, input_shape): super().__init__(cfg, input_shape, box_predictor=MyRCNNOutput(...)) ``` ================================================ FILE: detectron2/projects/DeepLab/README.md ================================================ # DeepLab in Detectron2 In this repository, we implement DeepLabV3 and DeepLabV3+ in Detectron2. ## Installation Install Detectron2 following [the instructions](https://detectron2.readthedocs.io/tutorials/install.html). ## Training To train a model with 8 GPUs run: ```bash cd /path/to/detectron2/projects/DeepLab python train_net.py --config-file configs/Cityscapes-SemanticSegmentation/deeplab_v3_plus_R_103_os16_mg124_poly_90k_bs16.yaml --num-gpus 8 ``` ## Evaluation Model evaluation can be done similarly: ```bash cd /path/to/detectron2/projects/DeepLab python train_net.py --config-file configs/Cityscapes-SemanticSegmentation/deeplab_v3_plus_R_103_os16_mg124_poly_90k_bs16.yaml --eval-only MODEL.WEIGHTS /path/to/model_checkpoint ``` ## Cityscapes Semantic Segmentation Cityscapes models are trained with ImageNet pretraining.
Method Backbone Output
resolution
mIoU model id download
DeepLabV3 R101-DC5 1024×2048 76.7 - -  |  -
DeepLabV3 R103-DC5 1024×2048 78.5 28041665 model | metrics
DeepLabV3+ R101-DC5 1024×2048 78.1 - -  |  -
DeepLabV3+ R103-DC5 1024×2048 80.0 28054032 model | metrics
Note: - [R103](https://dl.fbaipublicfiles.com/detectron2/DeepLab/R-103.pkl): a ResNet-101 with its first 7x7 convolution replaced by 3 3x3 convolutions. This modification has been used in most semantic segmentation papers. We pre-train this backbone on ImageNet using the default recipe of [pytorch examples](https://github.com/pytorch/examples/tree/master/imagenet). - DC5 means using dilated convolution in `res5`. ## Citing DeepLab If you use DeepLab, please use the following BibTeX entry. * DeepLabv3+: ``` @inproceedings{deeplabv3plus2018, title={Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation}, author={Liang-Chieh Chen and Yukun Zhu and George Papandreou and Florian Schroff and Hartwig Adam}, booktitle={ECCV}, year={2018} } ``` * DeepLabv3: ``` @article{deeplabv32018, title={Rethinking atrous convolution for semantic image segmentation}, author={Chen, Liang-Chieh and Papandreou, George and Schroff, Florian and Adam, Hartwig}, journal={arXiv:1706.05587}, year={2017} } ``` ================================================ FILE: detectron2/projects/DeepLab/configs/Cityscapes-SemanticSegmentation/Base-DeepLabV3-OS16-Semantic.yaml ================================================ _BASE_: "../../../../configs/Base-RCNN-DilatedC5.yaml" MODEL: META_ARCHITECTURE: "SemanticSegmentor" BACKBONE: FREEZE_AT: 0 SEM_SEG_HEAD: NAME: "DeepLabV3Head" IN_FEATURES: ["res5"] ASPP_CHANNELS: 256 ASPP_DILATIONS: [6, 12, 18] ASPP_DROPOUT: 0.1 CONVS_DIM: 256 COMMON_STRIDE: 16 NUM_CLASSES: 19 LOSS_TYPE: "hard_pixel_mining" DATASETS: TRAIN: ("cityscapes_fine_sem_seg_train",) TEST: ("cityscapes_fine_sem_seg_val",) SOLVER: BASE_LR: 0.01 MAX_ITER: 90000 LR_SCHEDULER_NAME: "WarmupPolyLR" IMS_PER_BATCH: 16 INPUT: MIN_SIZE_TRAIN: (512, 768, 1024, 1280, 1536, 1792, 2048) MIN_SIZE_TRAIN_SAMPLING: "choice" MIN_SIZE_TEST: 1024 MAX_SIZE_TRAIN: 4096 MAX_SIZE_TEST: 2048 CROP: ENABLED: True TYPE: "absolute" SIZE: (512, 1024) SINGLE_CATEGORY_MAX_AREA: 1.0 DATALOADER: NUM_WORKERS: 10 ================================================ FILE: detectron2/projects/DeepLab/configs/Cityscapes-SemanticSegmentation/deeplab_v3_R_103_os16_mg124_poly_90k_bs16.yaml ================================================ _BASE_: Base-DeepLabV3-OS16-Semantic.yaml MODEL: WEIGHTS: "detectron2://DeepLab/R-103.pkl" PIXEL_MEAN: [123.675, 116.280, 103.530] PIXEL_STD: [58.395, 57.120, 57.375] BACKBONE: NAME: "build_resnet_deeplab_backbone" RESNETS: DEPTH: 101 NORM: "SyncBN" RES5_MULTI_GRID: [1, 2, 4] STEM_TYPE: "deeplab" STEM_OUT_CHANNELS: 128 STRIDE_IN_1X1: False SEM_SEG_HEAD: NAME: "DeepLabV3Head" NORM: "SyncBN" INPUT: FORMAT: "RGB" ================================================ FILE: detectron2/projects/DeepLab/configs/Cityscapes-SemanticSegmentation/deeplab_v3_plus_R_103_os16_mg124_poly_90k_bs16.yaml ================================================ _BASE_: Base-DeepLabV3-OS16-Semantic.yaml MODEL: WEIGHTS: "detectron2://DeepLab/R-103.pkl" PIXEL_MEAN: [123.675, 116.280, 103.530] PIXEL_STD: [58.395, 57.120, 57.375] BACKBONE: NAME: "build_resnet_deeplab_backbone" RESNETS: DEPTH: 101 NORM: "SyncBN" OUT_FEATURES: ["res2", "res5"] RES5_MULTI_GRID: [1, 2, 4] STEM_TYPE: "deeplab" STEM_OUT_CHANNELS: 128 STRIDE_IN_1X1: False SEM_SEG_HEAD: NAME: "DeepLabV3PlusHead" IN_FEATURES: ["res2", "res5"] PROJECT_FEATURES: ["res2"] PROJECT_CHANNELS: [48] NORM: "SyncBN" COMMON_STRIDE: 4 INPUT: FORMAT: "RGB" ================================================ FILE: detectron2/projects/DeepLab/deeplab/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .build_solver import build_lr_scheduler from .config import add_deeplab_config from .resnet import build_resnet_deeplab_backbone from .semantic_seg import DeepLabV3Head, DeepLabV3PlusHead ================================================ FILE: detectron2/projects/DeepLab/deeplab/build_solver.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch from detectron2.config import CfgNode from detectron2.solver import build_lr_scheduler as build_d2_lr_scheduler from .lr_scheduler import WarmupPolyLR def build_lr_scheduler( cfg: CfgNode, optimizer: torch.optim.Optimizer ) -> torch.optim.lr_scheduler._LRScheduler: """ Build a LR scheduler from config. """ name = cfg.SOLVER.LR_SCHEDULER_NAME if name == "WarmupPolyLR": return WarmupPolyLR( optimizer, cfg.SOLVER.MAX_ITER, warmup_factor=cfg.SOLVER.WARMUP_FACTOR, warmup_iters=cfg.SOLVER.WARMUP_ITERS, warmup_method=cfg.SOLVER.WARMUP_METHOD, power=cfg.SOLVER.POLY_LR_POWER, constant_ending=cfg.SOLVER.POLY_LR_CONSTANT_ENDING, ) else: return build_d2_lr_scheduler(cfg, optimizer) ================================================ FILE: detectron2/projects/DeepLab/deeplab/config.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. def add_deeplab_config(cfg): """ Add config for DeepLab. """ # We retry random cropping until no single category in semantic segmentation GT occupies more # than `SINGLE_CATEGORY_MAX_AREA` part of the crop. cfg.INPUT.CROP.SINGLE_CATEGORY_MAX_AREA = 1.0 # Used for `poly` learning rate schedule. cfg.SOLVER.POLY_LR_POWER = 0.9 cfg.SOLVER.POLY_LR_CONSTANT_ENDING = 0.0 # Loss type, choose from `cross_entropy`, `hard_pixel_mining`. cfg.MODEL.SEM_SEG_HEAD.LOSS_TYPE = "hard_pixel_mining" # DeepLab settings cfg.MODEL.SEM_SEG_HEAD.PROJECT_FEATURES = ["res2"] cfg.MODEL.SEM_SEG_HEAD.PROJECT_CHANNELS = [48] cfg.MODEL.SEM_SEG_HEAD.ASPP_CHANNELS = 256 cfg.MODEL.SEM_SEG_HEAD.ASPP_DILATIONS = [6, 12, 18] cfg.MODEL.SEM_SEG_HEAD.ASPP_DROPOUT = 0.1 cfg.MODEL.SEM_SEG_HEAD.USE_DEPTHWISE_SEPARABLE_CONV = False # Backbone new configs cfg.MODEL.RESNETS.RES4_DILATION = 1 cfg.MODEL.RESNETS.RES5_MULTI_GRID = [1, 2, 4] # ResNet stem type from: `basic`, `deeplab` cfg.MODEL.RESNETS.STEM_TYPE = "deeplab" ================================================ FILE: detectron2/projects/DeepLab/deeplab/loss.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch import torch.nn as nn class DeepLabCE(nn.Module): """ Hard pixel mining with cross entropy loss, for semantic segmentation. This is used in TensorFlow DeepLab frameworks. Paper: DeeperLab: Single-Shot Image Parser Reference: https://github.com/tensorflow/models/blob/bd488858d610e44df69da6f89277e9de8a03722c/research/deeplab/utils/train_utils.py#L33 # noqa Arguments: ignore_label: Integer, label to ignore. top_k_percent_pixels: Float, the value lies in [0.0, 1.0]. When its value < 1.0, only compute the loss for the top k percent pixels (e.g., the top 20% pixels). This is useful for hard pixel mining. weight: Tensor, a manual rescaling weight given to each class. """ def __init__(self, ignore_label=-1, top_k_percent_pixels=1.0, weight=None): super(DeepLabCE, self).__init__() self.top_k_percent_pixels = top_k_percent_pixels self.ignore_label = ignore_label self.criterion = nn.CrossEntropyLoss( weight=weight, ignore_index=ignore_label, reduction="none" ) def forward(self, logits, labels, weights=None): if weights is None: pixel_losses = self.criterion(logits, labels).contiguous().view(-1) else: # Apply per-pixel loss weights. pixel_losses = self.criterion(logits, labels) * weights pixel_losses = pixel_losses.contiguous().view(-1) if self.top_k_percent_pixels == 1.0: return pixel_losses.mean() top_k_pixels = int(self.top_k_percent_pixels * pixel_losses.numel()) pixel_losses, _ = torch.topk(pixel_losses, top_k_pixels) return pixel_losses.mean() ================================================ FILE: detectron2/projects/DeepLab/deeplab/lr_scheduler.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import math from typing import List import torch from detectron2.solver.lr_scheduler import _get_warmup_factor_at_iter # NOTE: PyTorch's LR scheduler interface uses names that assume the LR changes # only on epoch boundaries. We typically use iteration based schedules instead. # As a result, "epoch" (e.g., as in self.last_epoch) should be understood to mean # "iteration" instead. # FIXME: ideally this would be achieved with a CombinedLRScheduler, separating # MultiStepLR with WarmupLR but the current LRScheduler design doesn't allow it. class WarmupPolyLR(torch.optim.lr_scheduler._LRScheduler): """ Poly learning rate schedule used to train DeepLab. Paper: DeepLab: Semantic Image Segmentation with Deep Convolutional Nets, Atrous Convolution, and Fully Connected CRFs. Reference: https://github.com/tensorflow/models/blob/21b73d22f3ed05b650e85ac50849408dd36de32e/research/deeplab/utils/train_utils.py#L337 # noqa """ def __init__( self, optimizer: torch.optim.Optimizer, max_iters: int, warmup_factor: float = 0.001, warmup_iters: int = 1000, warmup_method: str = "linear", last_epoch: int = -1, power: float = 0.9, constant_ending: float = 0.0, ): self.max_iters = max_iters self.warmup_factor = warmup_factor self.warmup_iters = warmup_iters self.warmup_method = warmup_method self.power = power self.constant_ending = constant_ending super().__init__(optimizer, last_epoch) def get_lr(self) -> List[float]: warmup_factor = _get_warmup_factor_at_iter( self.warmup_method, self.last_epoch, self.warmup_iters, self.warmup_factor ) if self.constant_ending > 0 and warmup_factor == 1.0: # Constant ending lr. if ( math.pow((1.0 - self.last_epoch / self.max_iters), self.power) < self.constant_ending ): return [base_lr * self.constant_ending for base_lr in self.base_lrs] return [ base_lr * warmup_factor * math.pow((1.0 - self.last_epoch / self.max_iters), self.power) for base_lr in self.base_lrs ] def _compute_values(self) -> List[float]: # The new interface return self.get_lr() ================================================ FILE: detectron2/projects/DeepLab/deeplab/resnet.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import fvcore.nn.weight_init as weight_init import torch.nn.functional as F from detectron2.layers import CNNBlockBase, Conv2d, get_norm from detectron2.modeling import BACKBONE_REGISTRY from detectron2.modeling.backbone.resnet import ( BasicStem, BottleneckBlock, DeformBottleneckBlock, ResNet, ) class DeepLabStem(CNNBlockBase): """ The DeepLab ResNet stem (layers before the first residual block). """ def __init__(self, in_channels=3, out_channels=128, norm="BN"): """ Args: norm (str or callable): norm after the first conv layer. See :func:`layers.get_norm` for supported format. """ super().__init__(in_channels, out_channels, 4) self.in_channels = in_channels self.conv1 = Conv2d( in_channels, out_channels // 2, kernel_size=3, stride=2, padding=1, bias=False, norm=get_norm(norm, out_channels // 2), ) self.conv2 = Conv2d( out_channels // 2, out_channels // 2, kernel_size=3, stride=1, padding=1, bias=False, norm=get_norm(norm, out_channels // 2), ) self.conv3 = Conv2d( out_channels // 2, out_channels, kernel_size=3, stride=1, padding=1, bias=False, norm=get_norm(norm, out_channels), ) weight_init.c2_msra_fill(self.conv1) weight_init.c2_msra_fill(self.conv2) weight_init.c2_msra_fill(self.conv3) def forward(self, x): x = self.conv1(x) x = F.relu_(x) x = self.conv2(x) x = F.relu_(x) x = self.conv3(x) x = F.relu_(x) x = F.max_pool2d(x, kernel_size=3, stride=2, padding=1) return x @BACKBONE_REGISTRY.register() def build_resnet_deeplab_backbone(cfg, input_shape): """ Create a ResNet instance from config. Returns: ResNet: a :class:`ResNet` instance. """ # need registration of new blocks/stems? norm = cfg.MODEL.RESNETS.NORM if cfg.MODEL.RESNETS.STEM_TYPE == "basic": stem = BasicStem( in_channels=input_shape.channels, out_channels=cfg.MODEL.RESNETS.STEM_OUT_CHANNELS, norm=norm, ) elif cfg.MODEL.RESNETS.STEM_TYPE == "deeplab": stem = DeepLabStem( in_channels=input_shape.channels, out_channels=cfg.MODEL.RESNETS.STEM_OUT_CHANNELS, norm=norm, ) else: raise ValueError("Unknown stem type: {}".format(cfg.MODEL.RESNETS.STEM_TYPE)) # fmt: off freeze_at = cfg.MODEL.BACKBONE.FREEZE_AT out_features = cfg.MODEL.RESNETS.OUT_FEATURES depth = cfg.MODEL.RESNETS.DEPTH num_groups = cfg.MODEL.RESNETS.NUM_GROUPS width_per_group = cfg.MODEL.RESNETS.WIDTH_PER_GROUP bottleneck_channels = num_groups * width_per_group in_channels = cfg.MODEL.RESNETS.STEM_OUT_CHANNELS out_channels = cfg.MODEL.RESNETS.RES2_OUT_CHANNELS stride_in_1x1 = cfg.MODEL.RESNETS.STRIDE_IN_1X1 res4_dilation = cfg.MODEL.RESNETS.RES4_DILATION res5_dilation = cfg.MODEL.RESNETS.RES5_DILATION deform_on_per_stage = cfg.MODEL.RESNETS.DEFORM_ON_PER_STAGE deform_modulated = cfg.MODEL.RESNETS.DEFORM_MODULATED deform_num_groups = cfg.MODEL.RESNETS.DEFORM_NUM_GROUPS res5_multi_grid = cfg.MODEL.RESNETS.RES5_MULTI_GRID # fmt: on assert res4_dilation in {1, 2}, "res4_dilation cannot be {}.".format(res4_dilation) assert res5_dilation in {1, 2, 4}, "res5_dilation cannot be {}.".format(res5_dilation) if res4_dilation == 2: # Always dilate res5 if res4 is dilated. assert res5_dilation == 4 num_blocks_per_stage = {50: [3, 4, 6, 3], 101: [3, 4, 23, 3], 152: [3, 8, 36, 3]}[depth] stages = [] # Avoid creating variables without gradients # It consumes extra memory and may cause allreduce to fail out_stage_idx = [{"res2": 2, "res3": 3, "res4": 4, "res5": 5}[f] for f in out_features] max_stage_idx = max(out_stage_idx) for idx, stage_idx in enumerate(range(2, max_stage_idx + 1)): if stage_idx == 4: dilation = res4_dilation elif stage_idx == 5: dilation = res5_dilation else: dilation = 1 first_stride = 1 if idx == 0 or dilation > 1 else 2 stage_kargs = { "num_blocks": num_blocks_per_stage[idx], "stride_per_block": [first_stride] + [1] * (num_blocks_per_stage[idx] - 1), "in_channels": in_channels, "out_channels": out_channels, "norm": norm, } stage_kargs["bottleneck_channels"] = bottleneck_channels stage_kargs["stride_in_1x1"] = stride_in_1x1 stage_kargs["dilation"] = dilation stage_kargs["num_groups"] = num_groups if deform_on_per_stage[idx]: stage_kargs["block_class"] = DeformBottleneckBlock stage_kargs["deform_modulated"] = deform_modulated stage_kargs["deform_num_groups"] = deform_num_groups else: stage_kargs["block_class"] = BottleneckBlock if stage_idx == 5: stage_kargs.pop("dilation") stage_kargs["dilation_per_block"] = [dilation * mg for mg in res5_multi_grid] blocks = ResNet.make_stage(**stage_kargs) in_channels = out_channels out_channels *= 2 bottleneck_channels *= 2 stages.append(blocks) return ResNet(stem, stages, out_features=out_features).freeze(freeze_at) ================================================ FILE: detectron2/projects/DeepLab/deeplab/semantic_seg.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Callable, Dict, List, Optional, Tuple, Union import fvcore.nn.weight_init as weight_init import torch from torch import nn from torch.nn import functional as F from detectron2.config import configurable from detectron2.layers import ASPP, Conv2d, DepthwiseSeparableConv2d, ShapeSpec, get_norm from detectron2.modeling import SEM_SEG_HEADS_REGISTRY from .loss import DeepLabCE @SEM_SEG_HEADS_REGISTRY.register() class DeepLabV3PlusHead(nn.Module): """ A semantic segmentation head described in :paper:`DeepLabV3+`. """ @configurable def __init__( self, input_shape: Dict[str, ShapeSpec], *, project_channels: List[int], aspp_dilations: List[int], aspp_dropout: float, decoder_channels: List[int], common_stride: int, norm: Union[str, Callable], train_size: Optional[Tuple], loss_weight: float = 1.0, loss_type: str = "cross_entropy", ignore_value: int = -1, num_classes: Optional[int] = None, use_depthwise_separable_conv: bool = False, ): """ NOTE: this interface is experimental. Args: input_shape: shape of the input features. They will be ordered by stride and the last one (with largest stride) is used as the input to the decoder (i.e. the ASPP module); the rest are low-level feature for the intermediate levels of decoder. project_channels (list[int]): a list of low-level feature channels. The length should be len(in_features) - 1. aspp_dilations (list(int)): a list of 3 dilations in ASPP. aspp_dropout (float): apply dropout on the output of ASPP. decoder_channels (list[int]): a list of output channels of each decoder stage. It should have the same length as "in_features" (each element in "in_features" corresponds to one decoder stage). common_stride (int): output stride of decoder. norm (str or callable): normalization for all conv layers. train_size (tuple): (height, width) of training images. loss_weight (float): loss weight. loss_type (str): type of loss function, 2 opptions: (1) "cross_entropy" is the standard cross entropy loss. (2) "hard_pixel_mining" is the loss in DeepLab that samples top k% hardest pixels. ignore_value (int): category to be ignored during training. num_classes (int): number of classes, if set to None, the decoder will not construct a predictor. use_depthwise_separable_conv (bool): use DepthwiseSeparableConv2d in ASPP and decoder. """ super().__init__() input_shape = sorted(input_shape.items(), key=lambda x: x[1].stride) # fmt: off self.in_features = [k for k, v in input_shape] # starting from "res2" to "res5" in_channels = [x[1].channels for x in input_shape] in_strides = [x[1].stride for x in input_shape] aspp_channels = decoder_channels[-1] self.ignore_value = ignore_value self.common_stride = common_stride # output stride self.loss_weight = loss_weight self.loss_type = loss_type self.decoder_only = num_classes is None self.use_depthwise_separable_conv = use_depthwise_separable_conv # fmt: on assert ( len(project_channels) == len(self.in_features) - 1 ), "Expected {} project_channels, got {}".format( len(self.in_features) - 1, len(project_channels) ) assert len(decoder_channels) == len( self.in_features ), "Expected {} decoder_channels, got {}".format( len(self.in_features), len(decoder_channels) ) self.decoder = nn.ModuleDict() use_bias = norm == "" for idx, in_channel in enumerate(in_channels): decoder_stage = nn.ModuleDict() if idx == len(self.in_features) - 1: # ASPP module if train_size is not None: train_h, train_w = train_size encoder_stride = in_strides[-1] if train_h % encoder_stride or train_w % encoder_stride: raise ValueError("Crop size need to be divisible by encoder stride.") pool_h = train_h // encoder_stride pool_w = train_w // encoder_stride pool_kernel_size = (pool_h, pool_w) else: pool_kernel_size = None project_conv = ASPP( in_channel, aspp_channels, aspp_dilations, norm=norm, activation=F.relu, pool_kernel_size=pool_kernel_size, dropout=aspp_dropout, use_depthwise_separable_conv=use_depthwise_separable_conv, ) fuse_conv = None else: project_conv = Conv2d( in_channel, project_channels[idx], kernel_size=1, bias=use_bias, norm=get_norm(norm, project_channels[idx]), activation=F.relu, ) weight_init.c2_xavier_fill(project_conv) if use_depthwise_separable_conv: # We use a single 5x5 DepthwiseSeparableConv2d to replace # 2 3x3 Conv2d since they have the same receptive field, # proposed in :paper:`Panoptic-DeepLab`. fuse_conv = DepthwiseSeparableConv2d( project_channels[idx] + decoder_channels[idx + 1], decoder_channels[idx], kernel_size=5, padding=2, norm1=norm, activation1=F.relu, norm2=norm, activation2=F.relu, ) else: fuse_conv = nn.Sequential( Conv2d( project_channels[idx] + decoder_channels[idx + 1], decoder_channels[idx], kernel_size=3, padding=1, bias=use_bias, norm=get_norm(norm, decoder_channels[idx]), activation=F.relu, ), Conv2d( decoder_channels[idx], decoder_channels[idx], kernel_size=3, padding=1, bias=use_bias, norm=get_norm(norm, decoder_channels[idx]), activation=F.relu, ), ) weight_init.c2_xavier_fill(fuse_conv[0]) weight_init.c2_xavier_fill(fuse_conv[1]) decoder_stage["project_conv"] = project_conv decoder_stage["fuse_conv"] = fuse_conv self.decoder[self.in_features[idx]] = decoder_stage if not self.decoder_only: self.predictor = Conv2d( decoder_channels[0], num_classes, kernel_size=1, stride=1, padding=0 ) nn.init.normal_(self.predictor.weight, 0, 0.001) nn.init.constant_(self.predictor.bias, 0) if self.loss_type == "cross_entropy": self.loss = nn.CrossEntropyLoss(reduction="mean", ignore_index=self.ignore_value) elif self.loss_type == "hard_pixel_mining": self.loss = DeepLabCE(ignore_label=self.ignore_value, top_k_percent_pixels=0.2) else: raise ValueError("Unexpected loss type: %s" % self.loss_type) @classmethod def from_config(cls, cfg, input_shape): if cfg.INPUT.CROP.ENABLED: assert cfg.INPUT.CROP.TYPE == "absolute" train_size = cfg.INPUT.CROP.SIZE else: train_size = None decoder_channels = [cfg.MODEL.SEM_SEG_HEAD.CONVS_DIM] * ( len(cfg.MODEL.SEM_SEG_HEAD.IN_FEATURES) - 1 ) + [cfg.MODEL.SEM_SEG_HEAD.ASPP_CHANNELS] ret = dict( input_shape={ k: v for k, v in input_shape.items() if k in cfg.MODEL.SEM_SEG_HEAD.IN_FEATURES }, project_channels=cfg.MODEL.SEM_SEG_HEAD.PROJECT_CHANNELS, aspp_dilations=cfg.MODEL.SEM_SEG_HEAD.ASPP_DILATIONS, aspp_dropout=cfg.MODEL.SEM_SEG_HEAD.ASPP_DROPOUT, decoder_channels=decoder_channels, common_stride=cfg.MODEL.SEM_SEG_HEAD.COMMON_STRIDE, norm=cfg.MODEL.SEM_SEG_HEAD.NORM, train_size=train_size, loss_weight=cfg.MODEL.SEM_SEG_HEAD.LOSS_WEIGHT, loss_type=cfg.MODEL.SEM_SEG_HEAD.LOSS_TYPE, ignore_value=cfg.MODEL.SEM_SEG_HEAD.IGNORE_VALUE, num_classes=cfg.MODEL.SEM_SEG_HEAD.NUM_CLASSES, use_depthwise_separable_conv=cfg.MODEL.SEM_SEG_HEAD.USE_DEPTHWISE_SEPARABLE_CONV, ) return ret def forward(self, features, targets=None): """ Returns: In training, returns (None, dict of losses) In inference, returns (CxHxW logits, {}) """ y = self.layers(features) if self.decoder_only: # Output from self.layers() only contains decoder feature. return y if self.training: return None, self.losses(y, targets) else: y = F.interpolate( y, scale_factor=self.common_stride, mode="bilinear", align_corners=False ) return y, {} def layers(self, features): # Reverse feature maps into top-down order (from low to high resolution) for f in self.in_features[::-1]: x = features[f] proj_x = self.decoder[f]["project_conv"](x) if self.decoder[f]["fuse_conv"] is None: # This is aspp module y = proj_x else: # Upsample y y = F.interpolate(y, size=proj_x.size()[2:], mode="bilinear", align_corners=False) y = torch.cat([proj_x, y], dim=1) y = self.decoder[f]["fuse_conv"](y) if not self.decoder_only: y = self.predictor(y) return y def losses(self, predictions, targets): predictions = F.interpolate( predictions, scale_factor=self.common_stride, mode="bilinear", align_corners=False ) loss = self.loss(predictions, targets) losses = {"loss_sem_seg": loss * self.loss_weight} return losses @SEM_SEG_HEADS_REGISTRY.register() class DeepLabV3Head(nn.Module): """ A semantic segmentation head described in :paper:`DeepLabV3`. """ def __init__(self, cfg, input_shape: Dict[str, ShapeSpec]): super().__init__() # fmt: off self.in_features = cfg.MODEL.SEM_SEG_HEAD.IN_FEATURES in_channels = [input_shape[f].channels for f in self.in_features] aspp_channels = cfg.MODEL.SEM_SEG_HEAD.ASPP_CHANNELS aspp_dilations = cfg.MODEL.SEM_SEG_HEAD.ASPP_DILATIONS self.ignore_value = cfg.MODEL.SEM_SEG_HEAD.IGNORE_VALUE num_classes = cfg.MODEL.SEM_SEG_HEAD.NUM_CLASSES conv_dims = cfg.MODEL.SEM_SEG_HEAD.CONVS_DIM self.common_stride = cfg.MODEL.SEM_SEG_HEAD.COMMON_STRIDE # output stride norm = cfg.MODEL.SEM_SEG_HEAD.NORM self.loss_weight = cfg.MODEL.SEM_SEG_HEAD.LOSS_WEIGHT self.loss_type = cfg.MODEL.SEM_SEG_HEAD.LOSS_TYPE train_crop_size = cfg.INPUT.CROP.SIZE aspp_dropout = cfg.MODEL.SEM_SEG_HEAD.ASPP_DROPOUT use_depthwise_separable_conv = cfg.MODEL.SEM_SEG_HEAD.USE_DEPTHWISE_SEPARABLE_CONV # fmt: on assert len(self.in_features) == 1 assert len(in_channels) == 1 # ASPP module if cfg.INPUT.CROP.ENABLED: assert cfg.INPUT.CROP.TYPE == "absolute" train_crop_h, train_crop_w = train_crop_size if train_crop_h % self.common_stride or train_crop_w % self.common_stride: raise ValueError("Crop size need to be divisible by output stride.") pool_h = train_crop_h // self.common_stride pool_w = train_crop_w // self.common_stride pool_kernel_size = (pool_h, pool_w) else: pool_kernel_size = None self.aspp = ASPP( in_channels[0], aspp_channels, aspp_dilations, norm=norm, activation=F.relu, pool_kernel_size=pool_kernel_size, dropout=aspp_dropout, use_depthwise_separable_conv=use_depthwise_separable_conv, ) self.predictor = Conv2d(conv_dims, num_classes, kernel_size=1, stride=1, padding=0) nn.init.normal_(self.predictor.weight, 0, 0.001) nn.init.constant_(self.predictor.bias, 0) if self.loss_type == "cross_entropy": self.loss = nn.CrossEntropyLoss(reduction="mean", ignore_index=self.ignore_value) elif self.loss_type == "hard_pixel_mining": self.loss = DeepLabCE(ignore_label=self.ignore_value, top_k_percent_pixels=0.2) else: raise ValueError("Unexpected loss type: %s" % self.loss_type) def forward(self, features, targets=None): """ Returns: In training, returns (None, dict of losses) In inference, returns (CxHxW logits, {}) """ x = features[self.in_features[0]] x = self.aspp(x) x = self.predictor(x) if self.training: return None, self.losses(x, targets) else: x = F.interpolate( x, scale_factor=self.common_stride, mode="bilinear", align_corners=False ) return x, {} def losses(self, predictions, targets): predictions = F.interpolate( predictions, scale_factor=self.common_stride, mode="bilinear", align_corners=False ) loss = self.loss(predictions, targets) losses = {"loss_sem_seg": loss * self.loss_weight} return losses ================================================ FILE: detectron2/projects/DeepLab/train_net.py ================================================ #!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. """ DeepLab Training Script. This script is a simplified version of the training script in detectron2/tools. """ import os import detectron2.data.transforms as T from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import get_cfg from detectron2.data import DatasetMapper, MetadataCatalog, build_detection_train_loader from detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, launch from detectron2.evaluation import CityscapesSemSegEvaluator, DatasetEvaluators, SemSegEvaluator from detectron2.projects.deeplab import add_deeplab_config, build_lr_scheduler def build_sem_seg_train_aug(cfg): augs = [ T.ResizeShortestEdge( cfg.INPUT.MIN_SIZE_TRAIN, cfg.INPUT.MAX_SIZE_TRAIN, cfg.INPUT.MIN_SIZE_TRAIN_SAMPLING ) ] if cfg.INPUT.CROP.ENABLED: augs.append( T.RandomCrop_CategoryAreaConstraint( cfg.INPUT.CROP.TYPE, cfg.INPUT.CROP.SIZE, cfg.INPUT.CROP.SINGLE_CATEGORY_MAX_AREA, cfg.MODEL.SEM_SEG_HEAD.IGNORE_VALUE, ) ) augs.append(T.RandomFlip()) return augs class Trainer(DefaultTrainer): """ We use the "DefaultTrainer" which contains a number pre-defined logic for standard training workflow. They may not work for you, especially if you are working on a new research project. In that case you can use the cleaner "SimpleTrainer", or write your own training loop. """ @classmethod def build_evaluator(cls, cfg, dataset_name, output_folder=None): """ Create evaluator(s) for a given dataset. This uses the special metadata "evaluator_type" associated with each builtin dataset. For your own dataset, you can simply create an evaluator manually in your script and do not have to worry about the hacky if-else logic here. """ if output_folder is None: output_folder = os.path.join(cfg.OUTPUT_DIR, "inference") evaluator_list = [] evaluator_type = MetadataCatalog.get(dataset_name).evaluator_type if evaluator_type == "sem_seg": return SemSegEvaluator( dataset_name, distributed=True, output_dir=output_folder, ) if evaluator_type == "cityscapes_sem_seg": return CityscapesSemSegEvaluator(dataset_name) if len(evaluator_list) == 0: raise NotImplementedError( "no Evaluator for the dataset {} with the type {}".format( dataset_name, evaluator_type ) ) if len(evaluator_list) == 1: return evaluator_list[0] return DatasetEvaluators(evaluator_list) @classmethod def build_train_loader(cls, cfg): if "SemanticSegmentor" in cfg.MODEL.META_ARCHITECTURE: mapper = DatasetMapper(cfg, is_train=True, augmentations=build_sem_seg_train_aug(cfg)) else: mapper = None return build_detection_train_loader(cfg, mapper=mapper) @classmethod def build_lr_scheduler(cls, cfg, optimizer): """ It now calls :func:`detectron2.solver.build_lr_scheduler`. Overwrite it if you'd like a different scheduler. """ return build_lr_scheduler(cfg, optimizer) def setup(args): """ Create configs and perform basic setups. """ cfg = get_cfg() add_deeplab_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.freeze() default_setup(cfg, args) return cfg def main(args): cfg = setup(args) if args.eval_only: model = Trainer.build_model(cfg) DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load( cfg.MODEL.WEIGHTS, resume=args.resume ) res = Trainer.test(cfg, model) return res trainer = Trainer(cfg) trainer.resume_or_load(resume=args.resume) return trainer.train() if __name__ == "__main__": args = default_argument_parser().parse_args() print("Command Line Args:", args) launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), ) ================================================ FILE: detectron2/projects/DensePose/README.md ================================================ # DensePose in Detectron2 DensePose aims at learning and establishing dense correspondences between image pixels and 3D object geometry for deformable objects, such as humans or animals. In this repository, we provide the code to train and evaluate DensePose R-CNN and various tools to visualize DensePose annotations and results. There are two main paradigms that are used within DensePose project. ## [Chart-based Dense Pose Estimation for Humans and Animals](doc/DENSEPOSE_IUV.md)
For chart-based estimation, 3D object mesh is split into charts and for each pixel the model estimates chart index `I` and local chart coordinates `(U, V)`. Please follow the link above to find a [detailed overview](doc/DENSEPOSE_IUV.md#Overview) of the method, links to trained models along with their performance evaluation in the [Model Zoo](doc/DENSEPOSE_IUV.md#ModelZoo) and [references](doc/DENSEPOSE_IUV.md#References) to the corresponding papers. ## [Continuous Surface Embeddings for Dense Pose Estimation for Humans and Animals](doc/DENSEPOSE_CSE.md)
To establish continuous surface embeddings, the model simultaneously learns descriptors for mesh vertices and for image pixels. The embeddings are put into correspondence, thus the location of each pixel on the 3D model is derived. Please follow the link above to find a [detailed overview](doc/DENSEPOSE_CSE.md#Overview) of the method, links to trained models along with their performance evaluation in the [Model Zoo](doc/DENSEPOSE_CSE.md#ModelZoo) and [references](doc/DENSEPOSE_CSE.md#References) to the corresponding papers. # Quick Start See [ Getting Started ](doc/GETTING_STARTED.md) # Model Zoo Please check the dedicated pages for [chart-based model zoo](doc/DENSEPOSE_IUV.md#ModelZoo) and for [continuous surface embeddings model zoo](doc/DENSEPOSE_CSE.md#ModelZoo). # What's New * June 2021: [DensePose CSE with Cycle Losses](doc/RELEASE_2021_06.md) * March 2021: [DensePose CSE (a framework to extend DensePose to various categories using 3D models) and DensePose Evolution (a framework to bootstrap DensePose on unlabeled data) released](doc/RELEASE_2021_03.md) * April 2020: [DensePose Confidence Estimation and Model Zoo Improvements](doc/RELEASE_2020_04.md) # License Detectron2 is released under the [Apache 2.0 license](../../LICENSE) ## Citing DensePose If you use DensePose, please refer to the BibTeX entries for [chart-based models](doc/DENSEPOSE_IUV.md#References) and for [continuous surface embeddings](doc/DENSEPOSE_CSE.md#References). ================================================ FILE: detectron2/projects/DensePose/apply_net.py ================================================ #!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. import argparse import glob import logging import os import pickle import sys from typing import Any, ClassVar, Dict, List import torch from detectron2.config import CfgNode, get_cfg from detectron2.data.detection_utils import read_image from detectron2.engine.defaults import DefaultPredictor from detectron2.structures.instances import Instances from detectron2.utils.logger import setup_logger from densepose import add_densepose_config from densepose.structures import DensePoseChartPredictorOutput, DensePoseEmbeddingPredictorOutput from densepose.utils.logger import verbosity_to_level from densepose.vis.base import CompoundVisualizer from densepose.vis.bounding_box import ScoredBoundingBoxVisualizer from densepose.vis.densepose_outputs_vertex import ( DensePoseOutputsTextureVisualizer, DensePoseOutputsVertexVisualizer, get_texture_atlases, ) from densepose.vis.densepose_results import ( DensePoseResultsContourVisualizer, DensePoseResultsFineSegmentationVisualizer, DensePoseResultsUVisualizer, DensePoseResultsVVisualizer, ) from densepose.vis.densepose_results_textures import ( DensePoseResultsVisualizerWithTexture, get_texture_atlas, ) from densepose.vis.extractor import ( CompoundExtractor, DensePoseOutputsExtractor, DensePoseResultExtractor, create_extractor, ) DOC = """Apply Net - a tool to print / visualize DensePose results """ LOGGER_NAME = "apply_net" logger = logging.getLogger(LOGGER_NAME) _ACTION_REGISTRY: Dict[str, "Action"] = {} class Action(object): @classmethod def add_arguments(cls: type, parser: argparse.ArgumentParser): parser.add_argument( "-v", "--verbosity", action="count", help="Verbose mode. Multiple -v options increase the verbosity.", ) def register_action(cls: type): """ Decorator for action classes to automate action registration """ global _ACTION_REGISTRY _ACTION_REGISTRY[cls.COMMAND] = cls return cls class InferenceAction(Action): @classmethod def add_arguments(cls: type, parser: argparse.ArgumentParser): super(InferenceAction, cls).add_arguments(parser) parser.add_argument("cfg", metavar="", help="Config file") parser.add_argument("model", metavar="", help="Model file") parser.add_argument("input", metavar="", help="Input data") parser.add_argument( "--opts", help="Modify config options using the command-line 'KEY VALUE' pairs", default=[], nargs=argparse.REMAINDER, ) @classmethod def execute(cls: type, args: argparse.Namespace): logger.info(f"Loading config from {args.cfg}") opts = [] cfg = cls.setup_config(args.cfg, args.model, args, opts) logger.info(f"Loading model from {args.model}") predictor = DefaultPredictor(cfg) logger.info(f"Loading data from {args.input}") file_list = cls._get_input_file_list(args.input) if len(file_list) == 0: logger.warning(f"No input images for {args.input}") return context = cls.create_context(args, cfg) for file_name in file_list: img = read_image(file_name, format="BGR") # predictor expects BGR image. with torch.no_grad(): outputs = predictor(img)["instances"] cls.execute_on_outputs(context, {"file_name": file_name, "image": img}, outputs) cls.postexecute(context) @classmethod def setup_config( cls: type, config_fpath: str, model_fpath: str, args: argparse.Namespace, opts: List[str] ): cfg = get_cfg() add_densepose_config(cfg) cfg.merge_from_file(config_fpath) cfg.merge_from_list(args.opts) if opts: cfg.merge_from_list(opts) cfg.MODEL.WEIGHTS = model_fpath cfg.freeze() return cfg @classmethod def _get_input_file_list(cls: type, input_spec: str): if os.path.isdir(input_spec): file_list = [ os.path.join(input_spec, fname) for fname in os.listdir(input_spec) if os.path.isfile(os.path.join(input_spec, fname)) ] elif os.path.isfile(input_spec): file_list = [input_spec] else: file_list = glob.glob(input_spec) return file_list @register_action class DumpAction(InferenceAction): """ Dump action that outputs results to a pickle file """ COMMAND: ClassVar[str] = "dump" @classmethod def add_parser(cls: type, subparsers: argparse._SubParsersAction): parser = subparsers.add_parser(cls.COMMAND, help="Dump model outputs to a file.") cls.add_arguments(parser) parser.set_defaults(func=cls.execute) @classmethod def add_arguments(cls: type, parser: argparse.ArgumentParser): super(DumpAction, cls).add_arguments(parser) parser.add_argument( "--output", metavar="", default="results.pkl", help="File name to save dump to", ) @classmethod def execute_on_outputs( cls: type, context: Dict[str, Any], entry: Dict[str, Any], outputs: Instances ): image_fpath = entry["file_name"] logger.info(f"Processing {image_fpath}") result = {"file_name": image_fpath} if outputs.has("scores"): result["scores"] = outputs.get("scores").cpu() if outputs.has("pred_boxes"): result["pred_boxes_XYXY"] = outputs.get("pred_boxes").tensor.cpu() if outputs.has("pred_densepose"): if isinstance(outputs.pred_densepose, DensePoseChartPredictorOutput): extractor = DensePoseResultExtractor() elif isinstance(outputs.pred_densepose, DensePoseEmbeddingPredictorOutput): extractor = DensePoseOutputsExtractor() result["pred_densepose"] = extractor(outputs)[0] context["results"].append(result) @classmethod def create_context(cls: type, args: argparse.Namespace, cfg: CfgNode): context = {"results": [], "out_fname": args.output} return context @classmethod def postexecute(cls: type, context: Dict[str, Any]): out_fname = context["out_fname"] out_dir = os.path.dirname(out_fname) if len(out_dir) > 0 and not os.path.exists(out_dir): os.makedirs(out_dir) with open(out_fname, "wb") as hFile: pickle.dump(context["results"], hFile) logger.info(f"Output saved to {out_fname}") @register_action class ShowAction(InferenceAction): """ Show action that visualizes selected entries on an image """ COMMAND: ClassVar[str] = "show" VISUALIZERS: ClassVar[Dict[str, object]] = { "dp_contour": DensePoseResultsContourVisualizer, "dp_segm": DensePoseResultsFineSegmentationVisualizer, "dp_u": DensePoseResultsUVisualizer, "dp_v": DensePoseResultsVVisualizer, "dp_iuv_texture": DensePoseResultsVisualizerWithTexture, "dp_cse_texture": DensePoseOutputsTextureVisualizer, "dp_vertex": DensePoseOutputsVertexVisualizer, "bbox": ScoredBoundingBoxVisualizer, } @classmethod def add_parser(cls: type, subparsers: argparse._SubParsersAction): parser = subparsers.add_parser(cls.COMMAND, help="Visualize selected entries") cls.add_arguments(parser) parser.set_defaults(func=cls.execute) @classmethod def add_arguments(cls: type, parser: argparse.ArgumentParser): super(ShowAction, cls).add_arguments(parser) parser.add_argument( "visualizations", metavar="", help="Comma separated list of visualizations, possible values: " "[{}]".format(",".join(sorted(cls.VISUALIZERS.keys()))), ) parser.add_argument( "--min_score", metavar="", default=0.8, type=float, help="Minimum detection score to visualize", ) parser.add_argument( "--nms_thresh", metavar="", default=None, type=float, help="NMS threshold" ) parser.add_argument( "--texture_atlas", metavar="", default=None, help="Texture atlas file (for IUV texture transfer)", ) parser.add_argument( "--texture_atlases_map", metavar="", default=None, help="JSON string of a dict containing texture atlas files for each mesh", ) parser.add_argument( "--output", metavar="", default="outputres.png", help="File name to save output to", ) @classmethod def setup_config( cls: type, config_fpath: str, model_fpath: str, args: argparse.Namespace, opts: List[str] ): opts.append("MODEL.ROI_HEADS.SCORE_THRESH_TEST") opts.append(str(args.min_score)) if args.nms_thresh is not None: opts.append("MODEL.ROI_HEADS.NMS_THRESH_TEST") opts.append(str(args.nms_thresh)) cfg = super(ShowAction, cls).setup_config(config_fpath, model_fpath, args, opts) return cfg @classmethod def execute_on_outputs( cls: type, context: Dict[str, Any], entry: Dict[str, Any], outputs: Instances ): import cv2 import numpy as np visualizer = context["visualizer"] extractor = context["extractor"] image_fpath = entry["file_name"] logger.info(f"Processing {image_fpath}") image = cv2.cvtColor(entry["image"], cv2.COLOR_BGR2GRAY) image = np.tile(image[:, :, np.newaxis], [1, 1, 3]) data = extractor(outputs) image_vis = visualizer.visualize(image, data) entry_idx = context["entry_idx"] + 1 out_fname = cls._get_out_fname(entry_idx, context["out_fname"]) out_dir = os.path.dirname(out_fname) if len(out_dir) > 0 and not os.path.exists(out_dir): os.makedirs(out_dir) cv2.imwrite(out_fname, image_vis) logger.info(f"Output saved to {out_fname}") context["entry_idx"] += 1 @classmethod def postexecute(cls: type, context: Dict[str, Any]): pass @classmethod def _get_out_fname(cls: type, entry_idx: int, fname_base: str): base, ext = os.path.splitext(fname_base) return base + ".{0:04d}".format(entry_idx) + ext @classmethod def create_context(cls: type, args: argparse.Namespace, cfg: CfgNode) -> Dict[str, Any]: vis_specs = args.visualizations.split(",") visualizers = [] extractors = [] for vis_spec in vis_specs: texture_atlas = get_texture_atlas(args.texture_atlas) texture_atlases_dict = get_texture_atlases(args.texture_atlases_map) vis = cls.VISUALIZERS[vis_spec]( cfg=cfg, texture_atlas=texture_atlas, texture_atlases_dict=texture_atlases_dict, ) visualizers.append(vis) extractor = create_extractor(vis) extractors.append(extractor) visualizer = CompoundVisualizer(visualizers) extractor = CompoundExtractor(extractors) context = { "extractor": extractor, "visualizer": visualizer, "out_fname": args.output, "entry_idx": 0, } return context def create_argument_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description=DOC, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=120), ) parser.set_defaults(func=lambda _: parser.print_help(sys.stdout)) subparsers = parser.add_subparsers(title="Actions") for _, action in _ACTION_REGISTRY.items(): action.add_parser(subparsers) return parser def main(): parser = create_argument_parser() args = parser.parse_args() verbosity = args.verbosity if hasattr(args, "verbosity") else None global logger logger = setup_logger(name=LOGGER_NAME) logger.setLevel(verbosity_to_level(verbosity)) args.func(args) if __name__ == "__main__": main() ================================================ FILE: detectron2/projects/DensePose/configs/Base-DensePose-RCNN-FPN.yaml ================================================ VERSION: 2 MODEL: META_ARCHITECTURE: "GeneralizedRCNN" BACKBONE: NAME: "build_resnet_fpn_backbone" RESNETS: OUT_FEATURES: ["res2", "res3", "res4", "res5"] FPN: IN_FEATURES: ["res2", "res3", "res4", "res5"] ANCHOR_GENERATOR: SIZES: [[32], [64], [128], [256], [512]] # One size for each in feature map ASPECT_RATIOS: [[0.5, 1.0, 2.0]] # Three aspect ratios (same for all in feature maps) RPN: IN_FEATURES: ["p2", "p3", "p4", "p5", "p6"] PRE_NMS_TOPK_TRAIN: 2000 # Per FPN level PRE_NMS_TOPK_TEST: 1000 # Per FPN level # Detectron1 uses 2000 proposals per-batch, # (See "modeling/rpn/rpn_outputs.py" for details of this legacy issue) # which is approximately 1000 proposals per-image since the default batch size for FPN is 2. POST_NMS_TOPK_TRAIN: 1000 POST_NMS_TOPK_TEST: 1000 DENSEPOSE_ON: True ROI_HEADS: NAME: "DensePoseROIHeads" IN_FEATURES: ["p2", "p3", "p4", "p5"] NUM_CLASSES: 1 ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_FC: 2 POOLER_RESOLUTION: 7 POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" POOLER_TYPE: "ROIAlign" NUM_COARSE_SEGM_CHANNELS: 2 DATASETS: TRAIN: ("densepose_coco_2014_train", "densepose_coco_2014_valminusminival") TEST: ("densepose_coco_2014_minival",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.01 STEPS: (60000, 80000) MAX_ITER: 90000 WARMUP_FACTOR: 0.1 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) ================================================ FILE: detectron2/projects/DensePose/configs/HRNet/densepose_rcnn_HRFPN_HRNet_w32_s1x.yaml ================================================ _BASE_: "../Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://1drv.ms/u/s!Aus8VCZ_C_33dYBMemi9xOUFR0w" BACKBONE: NAME: "build_hrfpn_backbone" RPN: IN_FEATURES: ['p1', 'p2', 'p3', 'p4', 'p5'] ROI_HEADS: IN_FEATURES: ['p1', 'p2', 'p3', 'p4', 'p5'] SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) CLIP_GRADIENTS: ENABLED: True CLIP_TYPE: "norm" BASE_LR: 0.03 ================================================ FILE: detectron2/projects/DensePose/configs/HRNet/densepose_rcnn_HRFPN_HRNet_w40_s1x.yaml ================================================ _BASE_: "../Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://1drv.ms/u/s!Aus8VCZ_C_33ck0gvo5jfoWBOPo" BACKBONE: NAME: "build_hrfpn_backbone" RPN: IN_FEATURES: ['p1', 'p2', 'p3', 'p4', 'p5'] ROI_HEADS: IN_FEATURES: ['p1', 'p2', 'p3', 'p4', 'p5'] HRNET: STAGE2: NUM_CHANNELS: [40, 80] STAGE3: NUM_CHANNELS: [40, 80, 160] STAGE4: NUM_CHANNELS: [40, 80, 160, 320] SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) CLIP_GRADIENTS: ENABLED: True CLIP_TYPE: "norm" BASE_LR: 0.03 ================================================ FILE: detectron2/projects/DensePose/configs/HRNet/densepose_rcnn_HRFPN_HRNet_w48_s1x.yaml ================================================ _BASE_: "../Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://1drv.ms/u/s!Aus8VCZ_C_33dKvqI6pBZlifgJk" BACKBONE: NAME: "build_hrfpn_backbone" RPN: IN_FEATURES: ['p1', 'p2', 'p3', 'p4', 'p5'] ROI_HEADS: IN_FEATURES: ['p1', 'p2', 'p3', 'p4', 'p5'] HRNET: STAGE2: NUM_CHANNELS: [48, 96] STAGE3: NUM_CHANNELS: [48, 96, 192] STAGE4: NUM_CHANNELS: [48, 96, 192, 384] SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) CLIP_GRADIENTS: ENABLED: True CLIP_TYPE: "norm" BASE_LR: 0.03 ================================================ FILE: detectron2/projects/DensePose/configs/cse/Base-DensePose-RCNN-FPN-Human.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: ROI_DENSEPOSE_HEAD: CSE: EMBEDDERS: "smpl_27554": TYPE: vertex_feature NUM_VERTICES: 27554 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_smpl_27554_256.pkl" DATASETS: TRAIN: - "densepose_coco_2014_train_cse" - "densepose_coco_2014_valminusminival_cse" TEST: - "densepose_coco_2014_minival_cse" CLASS_TO_MESH_NAME_MAPPING: "0": "smpl_27554" ================================================ FILE: detectron2/projects/DensePose/configs/cse/Base-DensePose-RCNN-FPN.yaml ================================================ VERSION: 2 MODEL: META_ARCHITECTURE: "GeneralizedRCNN" BACKBONE: NAME: "build_resnet_fpn_backbone" RESNETS: OUT_FEATURES: ["res2", "res3", "res4", "res5"] FPN: IN_FEATURES: ["res2", "res3", "res4", "res5"] ANCHOR_GENERATOR: SIZES: [[32], [64], [128], [256], [512]] # One size for each in feature map ASPECT_RATIOS: [[0.5, 1.0, 2.0]] # Three aspect ratios (same for all in feature maps) RPN: IN_FEATURES: ["p2", "p3", "p4", "p5", "p6"] PRE_NMS_TOPK_TRAIN: 2000 # Per FPN level PRE_NMS_TOPK_TEST: 1000 # Per FPN level # Detectron1 uses 2000 proposals per-batch, # (See "modeling/rpn/rpn_outputs.py" for details of this legacy issue) # which is approximately 1000 proposals per-image since the default batch size for FPN is 2. POST_NMS_TOPK_TRAIN: 1000 POST_NMS_TOPK_TEST: 1000 DENSEPOSE_ON: True ROI_HEADS: NAME: "DensePoseROIHeads" IN_FEATURES: ["p2", "p3", "p4", "p5"] NUM_CLASSES: 1 ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_FC: 2 POOLER_RESOLUTION: 7 POOLER_SAMPLING_RATIO: 2 POOLER_TYPE: "ROIAlign" ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" POOLER_TYPE: "ROIAlign" NUM_COARSE_SEGM_CHANNELS: 2 PREDICTOR_NAME: "DensePoseEmbeddingPredictor" LOSS_NAME: "DensePoseCseLoss" CSE: # embedding loss, possible values: # - "EmbeddingLoss" # - "SoftEmbeddingLoss" EMBED_LOSS_NAME: "EmbeddingLoss" SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.01 STEPS: (60000, 80000) MAX_ITER: 90000 WARMUP_FACTOR: 0.1 CLIP_GRADIENTS: CLIP_TYPE: norm CLIP_VALUE: 1.0 ENABLED: true NORM_TYPE: 2.0 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) DENSEPOSE_EVALUATION: TYPE: cse STORAGE: file ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_101_FPN_DL_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN-Human.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" CSE: EMBED_LOSS_NAME: "EmbeddingLoss" SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_101_FPN_DL_soft_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN-Human.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_101_FPN_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN-Human.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" CSE: EMBED_LOSS_NAME: "EmbeddingLoss" SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_101_FPN_soft_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN-Human.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_DL_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN-Human.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" CSE: EMBED_LOSS_NAME: "EmbeddingLoss" SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_DL_soft_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN-Human.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN-Human.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" CSE: EMBED_LOSS_NAME: "EmbeddingLoss" SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_soft_animals_CA_finetune_16k.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://dl.fbaipublicfiles.com/densepose/cse/densepose_rcnn_R_50_FPN_soft_s1x/250533982/model_final_2c4512.pkl" RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 1 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" COARSE_SEGM_TRAINED_BY_MASKS: True CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" EMBEDDING_DIST_GAUSS_SIGMA: 0.1 GEODESIC_DIST_GAUSS_SIGMA: 0.1 EMBEDDERS: "cat_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cat_7466_256.pkl" "dog_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_dog_7466_256.pkl" "sheep_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_sheep_5004_256.pkl" "horse_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_horse_5004_256.pkl" "zebra_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_zebra_5002_256.pkl" "giraffe_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_giraffe_5002_256.pkl" "elephant_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_elephant_5002_256.pkl" "cow_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cow_5002_256.pkl" "bear_4936": TYPE: vertex_feature NUM_VERTICES: 4936 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_bear_4936_256.pkl" DATASETS: TRAIN: - "densepose_lvis_v1_ds2_train_v1" TEST: - "densepose_lvis_v1_ds2_val_v1" WHITELISTED_CATEGORIES: "densepose_lvis_v1_ds2_train_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog "densepose_lvis_v1_ds2_val_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog CATEGORY_MAPS: "densepose_lvis_v1_ds2_train_v1": "1202": 943 # zebra -> sheep "569": 943 # horse -> sheep "496": 943 # giraffe -> sheep "422": 943 # elephant -> sheep "80": 943 # cow -> sheep "76": 943 # bear -> sheep "225": 943 # cat -> sheep "378": 943 # dog -> sheep "densepose_lvis_v1_ds2_val_v1": "1202": 943 # zebra -> sheep "569": 943 # horse -> sheep "496": 943 # giraffe -> sheep "422": 943 # elephant -> sheep "80": 943 # cow -> sheep "76": 943 # bear -> sheep "225": 943 # cat -> sheep "378": 943 # dog -> sheep CLASS_TO_MESH_NAME_MAPPING: # Note: different classes are mapped to a single class # mesh is chosen based on GT data, so this is just some # value which has no particular meaning "0": "sheep_5004" SOLVER: MAX_ITER: 16000 STEPS: (12000, 14000) DENSEPOSE_EVALUATION: EVALUATE_MESH_ALIGNMENT: True ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_soft_animals_CA_finetune_4k.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://dl.fbaipublicfiles.com/densepose/cse/densepose_rcnn_R_50_FPN_soft_s1x/250533982/model_final_2c4512.pkl" RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 1 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" COARSE_SEGM_TRAINED_BY_MASKS: True CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" EMBEDDING_DIST_GAUSS_SIGMA: 0.1 GEODESIC_DIST_GAUSS_SIGMA: 0.1 EMBEDDERS: "cat_5001": TYPE: vertex_feature NUM_VERTICES: 5001 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cat_5001_256.pkl" "dog_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_dog_5002_256.pkl" "sheep_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_sheep_5004_256.pkl" "horse_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_horse_5004_256.pkl" "zebra_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_zebra_5002_256.pkl" "giraffe_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_giraffe_5002_256.pkl" "elephant_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_elephant_5002_256.pkl" "cow_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cow_5002_256.pkl" "bear_4936": TYPE: vertex_feature NUM_VERTICES: 4936 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_bear_4936_256.pkl" DATASETS: TRAIN: - "densepose_lvis_v1_ds1_train_v1" TEST: - "densepose_lvis_v1_ds1_val_v1" WHITELISTED_CATEGORIES: "densepose_lvis_v1_ds1_train_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog "densepose_lvis_v1_ds1_val_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog CATEGORY_MAPS: "densepose_lvis_v1_ds1_train_v1": "1202": 943 # zebra -> sheep "569": 943 # horse -> sheep "496": 943 # giraffe -> sheep "422": 943 # elephant -> sheep "80": 943 # cow -> sheep "76": 943 # bear -> sheep "225": 943 # cat -> sheep "378": 943 # dog -> sheep "densepose_lvis_v1_ds1_val_v1": "1202": 943 # zebra -> sheep "569": 943 # horse -> sheep "496": 943 # giraffe -> sheep "422": 943 # elephant -> sheep "80": 943 # cow -> sheep "76": 943 # bear -> sheep "225": 943 # cat -> sheep "378": 943 # dog -> sheep CLASS_TO_MESH_NAME_MAPPING: # Note: different classes are mapped to a single class # mesh is chosen based on GT data, so this is just some # value which has no particular meaning "0": "sheep_5004" SOLVER: MAX_ITER: 4000 STEPS: (3000, 3500) DENSEPOSE_EVALUATION: EVALUATE_MESH_ALIGNMENT: True ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_soft_animals_I0_finetune_16k.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://dl.fbaipublicfiles.com/densepose/cse/densepose_rcnn_R_50_FPN_soft_animals_finetune_maskonly_24k/270668502/model_final_21b1d2.pkl" RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 9 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" COARSE_SEGM_TRAINED_BY_MASKS: True CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" EMBEDDING_DIST_GAUSS_SIGMA: 0.1 GEODESIC_DIST_GAUSS_SIGMA: 0.1 EMBEDDERS: "cat_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cat_7466_256.pkl" "dog_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_dog_7466_256.pkl" "sheep_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_sheep_5004_256.pkl" "horse_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_horse_5004_256.pkl" "zebra_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_zebra_5002_256.pkl" "giraffe_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_giraffe_5002_256.pkl" "elephant_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_elephant_5002_256.pkl" "cow_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cow_5002_256.pkl" "bear_4936": TYPE: vertex_feature NUM_VERTICES: 4936 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_bear_4936_256.pkl" DATASETS: TRAIN: - "densepose_lvis_v1_ds2_train_v1" TEST: - "densepose_lvis_v1_ds2_val_v1" WHITELISTED_CATEGORIES: "densepose_lvis_v1_ds2_train_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog "densepose_lvis_v1_ds2_val_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog CLASS_TO_MESH_NAME_MAPPING: "0": "bear_4936" "1": "cow_5002" "2": "cat_7466" "3": "dog_7466" "4": "elephant_5002" "5": "giraffe_5002" "6": "horse_5004" "7": "sheep_5004" "8": "zebra_5002" SOLVER: MAX_ITER: 16000 STEPS: (12000, 14000) DENSEPOSE_EVALUATION: EVALUATE_MESH_ALIGNMENT: True ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_soft_animals_I0_finetune_i2m_16k.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://dl.fbaipublicfiles.com/densepose/cse/densepose_rcnn_R_50_FPN_soft_animals_finetune_maskonly_24k/270668502/model_final_21b1d2.pkl" RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 9 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" COARSE_SEGM_TRAINED_BY_MASKS: True CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" EMBEDDING_DIST_GAUSS_SIGMA: 0.1 GEODESIC_DIST_GAUSS_SIGMA: 0.1 PIX_TO_SHAPE_CYCLE_LOSS: ENABLED: True EMBEDDERS: "cat_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cat_7466_256.pkl" "dog_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_dog_7466_256.pkl" "sheep_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_sheep_5004_256.pkl" "horse_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_horse_5004_256.pkl" "zebra_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_zebra_5002_256.pkl" "giraffe_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_giraffe_5002_256.pkl" "elephant_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_elephant_5002_256.pkl" "cow_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cow_5002_256.pkl" "bear_4936": TYPE: vertex_feature NUM_VERTICES: 4936 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_bear_4936_256.pkl" DATASETS: TRAIN: - "densepose_lvis_v1_ds2_train_v1" TEST: - "densepose_lvis_v1_ds2_val_v1" WHITELISTED_CATEGORIES: "densepose_lvis_v1_ds2_train_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog "densepose_lvis_v1_ds2_val_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog CLASS_TO_MESH_NAME_MAPPING: "0": "bear_4936" "1": "cow_5002" "2": "cat_7466" "3": "dog_7466" "4": "elephant_5002" "5": "giraffe_5002" "6": "horse_5004" "7": "sheep_5004" "8": "zebra_5002" SOLVER: MAX_ITER: 16000 STEPS: (12000, 14000) DENSEPOSE_EVALUATION: EVALUATE_MESH_ALIGNMENT: True ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_soft_animals_I0_finetune_m2m_16k.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://dl.fbaipublicfiles.com/densepose/cse/densepose_rcnn_R_50_FPN_soft_animals_finetune_maskonly_24k/267687159/model_final_354e61.pkl" RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 9 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" COARSE_SEGM_TRAINED_BY_MASKS: True CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" EMBEDDING_DIST_GAUSS_SIGMA: 0.1 GEODESIC_DIST_GAUSS_SIGMA: 0.1 SHAPE_TO_SHAPE_CYCLE_LOSS: ENABLED: True EMBEDDERS: "cat_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cat_7466_256.pkl" "dog_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_dog_7466_256.pkl" "sheep_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_sheep_5004_256.pkl" "horse_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_horse_5004_256.pkl" "zebra_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_zebra_5002_256.pkl" "giraffe_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_giraffe_5002_256.pkl" "elephant_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_elephant_5002_256.pkl" "cow_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cow_5002_256.pkl" "bear_4936": TYPE: vertex_feature NUM_VERTICES: 4936 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_bear_4936_256.pkl" "smpl_27554": TYPE: vertex_feature NUM_VERTICES: 27554 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_smpl_27554_256.pkl" DATASETS: TRAIN: - "densepose_lvis_v1_ds2_train_v1" TEST: - "densepose_lvis_v1_ds2_val_v1" WHITELISTED_CATEGORIES: "densepose_lvis_v1_ds2_train_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog "densepose_lvis_v1_ds2_val_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog CLASS_TO_MESH_NAME_MAPPING: "0": "bear_4936" "1": "cow_5002" "2": "cat_7466" "3": "dog_7466" "4": "elephant_5002" "5": "giraffe_5002" "6": "horse_5004" "7": "sheep_5004" "8": "zebra_5002" SOLVER: MAX_ITER: 16000 STEPS: (12000, 14000) DENSEPOSE_EVALUATION: EVALUATE_MESH_ALIGNMENT: True MESH_ALIGNMENT_MESH_NAMES: - bear_4936 - cow_5002 - cat_7466 - dog_7466 - elephant_5002 - giraffe_5002 - horse_5004 - sheep_5004 - zebra_5002 ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_soft_animals_finetune_16k.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://dl.fbaipublicfiles.com/densepose/cse/densepose_rcnn_R_50_FPN_soft_s1x/250533982/model_final_2c4512.pkl" RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 9 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" COARSE_SEGM_TRAINED_BY_MASKS: True CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" EMBEDDING_DIST_GAUSS_SIGMA: 0.1 GEODESIC_DIST_GAUSS_SIGMA: 0.1 EMBEDDERS: "cat_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cat_7466_256.pkl" "dog_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_dog_7466_256.pkl" "sheep_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_sheep_5004_256.pkl" "horse_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_horse_5004_256.pkl" "zebra_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_zebra_5002_256.pkl" "giraffe_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_giraffe_5002_256.pkl" "elephant_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_elephant_5002_256.pkl" "cow_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cow_5002_256.pkl" "bear_4936": TYPE: vertex_feature NUM_VERTICES: 4936 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_bear_4936_256.pkl" DATASETS: TRAIN: - "densepose_lvis_v1_ds2_train_v1" TEST: - "densepose_lvis_v1_ds2_val_v1" WHITELISTED_CATEGORIES: "densepose_lvis_v1_ds2_train_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog "densepose_lvis_v1_ds2_val_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog CLASS_TO_MESH_NAME_MAPPING: "0": "bear_4936" "1": "cow_5002" "2": "cat_7466" "3": "dog_7466" "4": "elephant_5002" "5": "giraffe_5002" "6": "horse_5004" "7": "sheep_5004" "8": "zebra_5002" SOLVER: MAX_ITER: 16000 STEPS: (12000, 14000) DENSEPOSE_EVALUATION: EVALUATE_MESH_ALIGNMENT: True ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_soft_animals_finetune_4k.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://dl.fbaipublicfiles.com/densepose/cse/densepose_rcnn_R_50_FPN_soft_s1x/250533982/model_final_2c4512.pkl" RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 9 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" COARSE_SEGM_TRAINED_BY_MASKS: True CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" EMBEDDING_DIST_GAUSS_SIGMA: 0.1 GEODESIC_DIST_GAUSS_SIGMA: 0.1 EMBEDDERS: "cat_5001": TYPE: vertex_feature NUM_VERTICES: 5001 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cat_5001_256.pkl" "dog_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_dog_5002_256.pkl" "sheep_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_sheep_5004_256.pkl" "horse_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_horse_5004_256.pkl" "zebra_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_zebra_5002_256.pkl" "giraffe_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_giraffe_5002_256.pkl" "elephant_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_elephant_5002_256.pkl" "cow_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cow_5002_256.pkl" "bear_4936": TYPE: vertex_feature NUM_VERTICES: 4936 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_bear_4936_256.pkl" DATASETS: TRAIN: - "densepose_lvis_v1_ds1_train_v1" TEST: - "densepose_lvis_v1_ds1_val_v1" WHITELISTED_CATEGORIES: "densepose_lvis_v1_ds1_train_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog "densepose_lvis_v1_ds1_val_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog CLASS_TO_MESH_NAME_MAPPING: "0": "bear_4936" "1": "cow_5002" "2": "cat_5001" "3": "dog_5002" "4": "elephant_5002" "5": "giraffe_5002" "6": "horse_5004" "7": "sheep_5004" "8": "zebra_5002" SOLVER: MAX_ITER: 4000 STEPS: (3000, 3500) DENSEPOSE_EVALUATION: EVALUATE_MESH_ALIGNMENT: True ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_soft_animals_finetune_maskonly_24k.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://dl.fbaipublicfiles.com/densepose/cse/densepose_rcnn_R_50_FPN_soft_s1x/250533982/model_final_2c4512.pkl" RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 9 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" COARSE_SEGM_TRAINED_BY_MASKS: True CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" EMBED_LOSS_WEIGHT: 0.0 EMBEDDING_DIST_GAUSS_SIGMA: 0.1 GEODESIC_DIST_GAUSS_SIGMA: 0.1 EMBEDDERS: "cat_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cat_7466_256.pkl" "dog_7466": TYPE: vertex_feature NUM_VERTICES: 7466 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_dog_7466_256.pkl" "sheep_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_sheep_5004_256.pkl" "horse_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_horse_5004_256.pkl" "zebra_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_zebra_5002_256.pkl" "giraffe_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_giraffe_5002_256.pkl" "elephant_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_elephant_5002_256.pkl" "cow_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cow_5002_256.pkl" "bear_4936": TYPE: vertex_feature NUM_VERTICES: 4936 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_bear_4936_256.pkl" DATASETS: TRAIN: - "densepose_lvis_v1_ds2_train_v1" TEST: - "densepose_lvis_v1_ds2_val_v1" WHITELISTED_CATEGORIES: "densepose_lvis_v1_ds2_train_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog "densepose_lvis_v1_ds2_val_v1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog CLASS_TO_MESH_NAME_MAPPING: "0": "bear_4936" "1": "cow_5002" "2": "cat_7466" "3": "dog_7466" "4": "elephant_5002" "5": "giraffe_5002" "6": "horse_5004" "7": "sheep_5004" "8": "zebra_5002" SOLVER: MAX_ITER: 24000 STEPS: (20000, 22000) ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_soft_chimps_finetune_4k.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "https://dl.fbaipublicfiles.com/densepose/cse/densepose_rcnn_R_50_FPN_soft_s1x/250533982/model_final_2c4512.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" EMBEDDING_DIST_GAUSS_SIGMA: 0.1 GEODESIC_DIST_GAUSS_SIGMA: 0.1 EMBEDDERS: "chimp_5029": TYPE: vertex_feature NUM_VERTICES: 5029 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_chimp_5029_256.pkl" DATASETS: TRAIN: - "densepose_chimps_cse_train" TEST: - "densepose_chimps_cse_val" CLASS_TO_MESH_NAME_MAPPING: "0": "chimp_5029" SOLVER: MAX_ITER: 4000 STEPS: (3000, 3500) ================================================ FILE: detectron2/projects/DensePose/configs/cse/densepose_rcnn_R_50_FPN_soft_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN-Human.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_101_FPN_DL_WC1M_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_101_FPN_DL_WC1_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_101_FPN_DL_WC2M_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "indep_aniso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_101_FPN_DL_WC2_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "indep_aniso" POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_101_FPN_DL_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_101_FPN_WC1M_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) WARMUP_FACTOR: 0.025 ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_101_FPN_WC1_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) WARMUP_FACTOR: 0.025 ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_101_FPN_WC2M_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: UV_CONFIDENCE: ENABLED: True TYPE: "indep_aniso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) WARMUP_FACTOR: 0.025 ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_101_FPN_WC2_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: UV_CONFIDENCE: ENABLED: True TYPE: "indep_aniso" POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) WARMUP_FACTOR: 0.025 ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_101_FPN_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_101_FPN_s1x_legacy.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" RESNETS: DEPTH: 101 ROI_DENSEPOSE_HEAD: NUM_COARSE_SEGM_CHANNELS: 15 POOLER_RESOLUTION: 14 HEATMAP_SIZE: 56 INDEX_WEIGHTS: 2.0 PART_WEIGHTS: 0.3 POINT_REGRESSION_WEIGHTS: 0.1 DECODER_ON: False SOLVER: BASE_LR: 0.002 MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_50_FPN_DL_WC1M_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_50_FPN_DL_WC1_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_50_FPN_DL_WC2M_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "indep_aniso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_50_FPN_DL_WC2_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "indep_aniso" POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_50_FPN_DL_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_50_FPN_WC1M_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True CLIP_TYPE: norm CLIP_VALUE: 100.0 MAX_ITER: 130000 STEPS: (100000, 120000) WARMUP_FACTOR: 0.025 ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_50_FPN_WC1_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) WARMUP_FACTOR: 0.025 ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_50_FPN_WC2M_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: UV_CONFIDENCE: ENABLED: True TYPE: "indep_aniso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) WARMUP_FACTOR: 0.025 ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_50_FPN_WC2_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: UV_CONFIDENCE: ENABLED: True TYPE: "indep_aniso" POINT_REGRESSION_WEIGHTS: 0.0005 SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 130000 STEPS: (100000, 120000) WARMUP_FACTOR: 0.025 ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_50_FPN_s1x.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 SOLVER: MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/densepose_rcnn_R_50_FPN_s1x_legacy.yaml ================================================ _BASE_: "Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: NUM_COARSE_SEGM_CHANNELS: 15 POOLER_RESOLUTION: 14 HEATMAP_SIZE: 56 INDEX_WEIGHTS: 2.0 PART_WEIGHTS: 0.3 POINT_REGRESSION_WEIGHTS: 0.1 DECODER_ON: False SOLVER: BASE_LR: 0.002 MAX_ITER: 130000 STEPS: (100000, 120000) ================================================ FILE: detectron2/projects/DensePose/configs/evolution/Base-RCNN-FPN-Atop10P_CA.yaml ================================================ MODEL: META_ARCHITECTURE: "GeneralizedRCNN" BACKBONE: NAME: "build_resnet_fpn_backbone" RESNETS: OUT_FEATURES: ["res2", "res3", "res4", "res5"] FPN: IN_FEATURES: ["res2", "res3", "res4", "res5"] ANCHOR_GENERATOR: SIZES: [[32], [64], [128], [256], [512]] # One size for each in feature map ASPECT_RATIOS: [[0.5, 1.0, 2.0]] # Three aspect ratios (same for all in feature maps) RPN: IN_FEATURES: ["p2", "p3", "p4", "p5", "p6"] PRE_NMS_TOPK_TRAIN: 2000 # Per FPN level PRE_NMS_TOPK_TEST: 1000 # Per FPN level # Detectron1 uses 2000 proposals per-batch, # (See "modeling/rpn/rpn_outputs.py" for details of this legacy issue) # which is approximately 1000 proposals per-image since the default batch size for FPN is 2. POST_NMS_TOPK_TRAIN: 1000 POST_NMS_TOPK_TEST: 1000 ROI_HEADS: NAME: "StandardROIHeads" IN_FEATURES: ["p2", "p3", "p4", "p5"] NUM_CLASSES: 1 ROI_BOX_HEAD: NAME: "FastRCNNConvFCHead" NUM_FC: 2 POOLER_RESOLUTION: 7 ROI_MASK_HEAD: NAME: "MaskRCNNConvUpsampleHead" NUM_CONV: 4 POOLER_RESOLUTION: 14 DATASETS: TRAIN: ("base_coco_2017_train", "densepose_coco_2014_train") TEST: ("densepose_chimps",) CATEGORY_MAPS: "base_coco_2017_train": "16": 1 # bird -> person "17": 1 # cat -> person "18": 1 # dog -> person "19": 1 # horse -> person "20": 1 # sheep -> person "21": 1 # cow -> person "22": 1 # elephant -> person "23": 1 # bear -> person "24": 1 # zebra -> person "25": 1 # girafe -> person "base_coco_2017_val": "16": 1 # bird -> person "17": 1 # cat -> person "18": 1 # dog -> person "19": 1 # horse -> person "20": 1 # sheep -> person "21": 1 # cow -> person "22": 1 # elephant -> person "23": 1 # bear -> person "24": 1 # zebra -> person "25": 1 # girafe -> person WHITELISTED_CATEGORIES: "base_coco_2017_train": - 1 # person - 16 # bird - 17 # cat - 18 # dog - 19 # horse - 20 # sheep - 21 # cow - 22 # elephant - 23 # bear - 24 # zebra - 25 # girafe "base_coco_2017_val": - 1 # person - 16 # bird - 17 # cat - 18 # dog - 19 # horse - 20 # sheep - 21 # cow - 22 # elephant - 23 # bear - 24 # zebra - 25 # girafe SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.02 STEPS: (60000, 80000) MAX_ITER: 90000 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) VERSION: 2 ================================================ FILE: detectron2/projects/DensePose/configs/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA.yaml ================================================ _BASE_: "Base-RCNN-FPN-Atop10P_CA.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 DENSEPOSE_ON: True ROI_HEADS: NAME: "DensePoseROIHeads" IN_FEATURES: ["p2", "p3", "p4", "p5"] NUM_CLASSES: 1 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 POOLER_TYPE: "ROIAlign" NUM_COARSE_SEGM_CHANNELS: 2 COARSE_SEGM_TRAINED_BY_MASKS: True INDEX_WEIGHTS: 1.0 SOLVER: CLIP_GRADIENTS: ENABLED: True WARMUP_FACTOR: 0.025 MAX_ITER: 270000 STEPS: (210000, 250000) ================================================ FILE: detectron2/projects/DensePose/configs/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_coarsesegm.yaml ================================================ _BASE_: "Base-RCNN-FPN-Atop10P_CA.yaml" MODEL: WEIGHTS: https://dl.fbaipublicfiles.com/densepose/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA/217578784/model_final_9fe1cc.pkl RESNETS: DEPTH: 50 DENSEPOSE_ON: True ROI_HEADS: NAME: "DensePoseROIHeads" IN_FEATURES: ["p2", "p3", "p4", "p5"] NUM_CLASSES: 1 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 POOLER_TYPE: "ROIAlign" NUM_COARSE_SEGM_CHANNELS: 2 COARSE_SEGM_TRAINED_BY_MASKS: True BOOTSTRAP_DATASETS: - DATASET: "chimpnsee" RATIO: 1.0 IMAGE_LOADER: TYPE: "video_keyframe" SELECT: STRATEGY: "random_k" NUM_IMAGES: 4 TRANSFORM: TYPE: "resize" MIN_SIZE: 800 MAX_SIZE: 1333 BATCH_SIZE: 8 NUM_WORKERS: 1 INFERENCE: INPUT_BATCH_SIZE: 1 OUTPUT_BATCH_SIZE: 1 DATA_SAMPLER: # supported types: # densepose_uniform # densepose_UV_confidence # densepose_fine_segm_confidence # densepose_coarse_segm_confidence TYPE: "densepose_coarse_segm_confidence" COUNT_PER_CLASS: 8 FILTER: TYPE: "detection_score" MIN_VALUE: 0.8 BOOTSTRAP_MODEL: WEIGHTS: https://dl.fbaipublicfiles.com/densepose/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA/217578784/model_final_9fe1cc.pkl SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 270000 STEPS: (210000, 250000) ================================================ FILE: detectron2/projects/DensePose/configs/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_finesegm.yaml ================================================ _BASE_: "Base-RCNN-FPN-Atop10P_CA.yaml" MODEL: WEIGHTS: https://dl.fbaipublicfiles.com/densepose/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA/217578784/model_final_9fe1cc.pkl RESNETS: DEPTH: 50 DENSEPOSE_ON: True ROI_HEADS: NAME: "DensePoseROIHeads" IN_FEATURES: ["p2", "p3", "p4", "p5"] NUM_CLASSES: 1 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 POOLER_TYPE: "ROIAlign" NUM_COARSE_SEGM_CHANNELS: 2 COARSE_SEGM_TRAINED_BY_MASKS: True BOOTSTRAP_DATASETS: - DATASET: "chimpnsee" RATIO: 1.0 IMAGE_LOADER: TYPE: "video_keyframe" SELECT: STRATEGY: "random_k" NUM_IMAGES: 4 TRANSFORM: TYPE: "resize" MIN_SIZE: 800 MAX_SIZE: 1333 BATCH_SIZE: 8 NUM_WORKERS: 1 INFERENCE: INPUT_BATCH_SIZE: 1 OUTPUT_BATCH_SIZE: 1 DATA_SAMPLER: # supported types: # densepose_uniform # densepose_UV_confidence # densepose_fine_segm_confidence # densepose_coarse_segm_confidence TYPE: "densepose_fine_segm_confidence" COUNT_PER_CLASS: 8 FILTER: TYPE: "detection_score" MIN_VALUE: 0.8 BOOTSTRAP_MODEL: WEIGHTS: https://dl.fbaipublicfiles.com/densepose/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA/217578784/model_final_9fe1cc.pkl SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 270000 STEPS: (210000, 250000) ================================================ FILE: detectron2/projects/DensePose/configs/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_uniform.yaml ================================================ _BASE_: "Base-RCNN-FPN-Atop10P_CA.yaml" MODEL: WEIGHTS: https://dl.fbaipublicfiles.com/densepose/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA/217578784/model_final_9fe1cc.pkl RESNETS: DEPTH: 50 DENSEPOSE_ON: True ROI_HEADS: NAME: "DensePoseROIHeads" IN_FEATURES: ["p2", "p3", "p4", "p5"] NUM_CLASSES: 1 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 POOLER_TYPE: "ROIAlign" NUM_COARSE_SEGM_CHANNELS: 2 COARSE_SEGM_TRAINED_BY_MASKS: True BOOTSTRAP_DATASETS: - DATASET: "chimpnsee" RATIO: 1.0 IMAGE_LOADER: TYPE: "video_keyframe" SELECT: STRATEGY: "random_k" NUM_IMAGES: 4 TRANSFORM: TYPE: "resize" MIN_SIZE: 800 MAX_SIZE: 1333 BATCH_SIZE: 8 NUM_WORKERS: 1 INFERENCE: INPUT_BATCH_SIZE: 1 OUTPUT_BATCH_SIZE: 1 DATA_SAMPLER: # supported types: # densepose_uniform # densepose_UV_confidence # densepose_fine_segm_confidence # densepose_coarse_segm_confidence TYPE: "densepose_uniform" COUNT_PER_CLASS: 8 FILTER: TYPE: "detection_score" MIN_VALUE: 0.8 BOOTSTRAP_MODEL: WEIGHTS: https://dl.fbaipublicfiles.com/densepose/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA/217578784/model_final_9fe1cc.pkl SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 270000 STEPS: (210000, 250000) ================================================ FILE: detectron2/projects/DensePose/configs/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_uv.yaml ================================================ _BASE_: "Base-RCNN-FPN-Atop10P_CA.yaml" MODEL: WEIGHTS: https://dl.fbaipublicfiles.com/densepose/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA/217578784/model_final_9fe1cc.pkl RESNETS: DEPTH: 50 DENSEPOSE_ON: True ROI_HEADS: NAME: "DensePoseROIHeads" IN_FEATURES: ["p2", "p3", "p4", "p5"] NUM_CLASSES: 1 ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" SEGM_CONFIDENCE: ENABLED: True POINT_REGRESSION_WEIGHTS: 0.0005 POOLER_TYPE: "ROIAlign" NUM_COARSE_SEGM_CHANNELS: 2 COARSE_SEGM_TRAINED_BY_MASKS: True BOOTSTRAP_DATASETS: - DATASET: "chimpnsee" RATIO: 1.0 IMAGE_LOADER: TYPE: "video_keyframe" SELECT: STRATEGY: "random_k" NUM_IMAGES: 4 TRANSFORM: TYPE: "resize" MIN_SIZE: 800 MAX_SIZE: 1333 BATCH_SIZE: 8 NUM_WORKERS: 1 INFERENCE: INPUT_BATCH_SIZE: 1 OUTPUT_BATCH_SIZE: 1 DATA_SAMPLER: # supported types: # densepose_uniform # densepose_UV_confidence # densepose_fine_segm_confidence # densepose_coarse_segm_confidence TYPE: "densepose_UV_confidence" COUNT_PER_CLASS: 8 FILTER: TYPE: "detection_score" MIN_VALUE: 0.8 BOOTSTRAP_MODEL: WEIGHTS: https://dl.fbaipublicfiles.com/densepose/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA/217578784/model_final_9fe1cc.pkl SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 270000 STEPS: (210000, 250000) ================================================ FILE: detectron2/projects/DensePose/configs/quick_schedules/cse/densepose_rcnn_R_50_FPN_DL_instant_test.yaml ================================================ _BASE_: "../../cse/Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" DATASETS: TRAIN: ("densepose_coco_2014_minival_100_cse",) TEST: ("densepose_coco_2014_minival_100_cse",) SOLVER: MAX_ITER: 40 STEPS: (30,) ================================================ FILE: detectron2/projects/DensePose/configs/quick_schedules/cse/densepose_rcnn_R_50_FPN_soft_animals_finetune_instant_test.yaml ================================================ _BASE_: "../../cse/Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 9 ROI_DENSEPOSE_HEAD: NAME: "DensePoseV1ConvXHead" CSE: EMBED_LOSS_NAME: "SoftEmbeddingLoss" EMBEDDING_DIST_GAUSS_SIGMA: 0.1 EMBEDDERS: "cat_5001": TYPE: vertex_feature NUM_VERTICES: 5001 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cat_5001_256.pkl" "dog_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_dog_5002_256.pkl" "sheep_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_sheep_5004_256.pkl" "horse_5004": TYPE: vertex_feature NUM_VERTICES: 5004 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_horse_5004_256.pkl" "zebra_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_zebra_5002_256.pkl" "giraffe_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_giraffe_5002_256.pkl" "elephant_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_elephant_5002_256.pkl" "cow_5002": TYPE: vertex_feature NUM_VERTICES: 5002 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_cow_5002_256.pkl" "bear_4936": TYPE: vertex_feature NUM_VERTICES: 4936 FEATURE_DIM: 256 FEATURES_TRAINABLE: False IS_TRAINABLE: True INIT_FILE: "https://dl.fbaipublicfiles.com/densepose/data/cse/lbo/phi_bear_4936_256.pkl" DATASETS: TRAIN: - "densepose_lvis_v1_train1" - "densepose_lvis_v1_train2" TEST: - "densepose_lvis_v1_val_animals_100" WHITELISTED_CATEGORIES: "densepose_lvis_v1_train1": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog "densepose_lvis_v1_train2": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog "densepose_lvis_v1_val_animals_100": - 943 # sheep - 1202 # zebra - 569 # horse - 496 # giraffe - 422 # elephant - 80 # cow - 76 # bear - 225 # cat - 378 # dog CLASS_TO_MESH_NAME_MAPPING: "0": "bear_4936" "1": "cow_5002" "2": "cat_5001" "3": "dog_5002" "4": "elephant_5002" "5": "giraffe_5002" "6": "horse_5004" "7": "sheep_5004" "8": "zebra_5002" SOLVER: MAX_ITER: 40 STEPS: (30,) ================================================ FILE: detectron2/projects/DensePose/configs/quick_schedules/densepose_rcnn_HRFPN_HRNet_w32_instant_test.yaml ================================================ _BASE_: "../HRNet/densepose_rcnn_HRFPN_HRNet_w32_s1x.yaml" DATASETS: TRAIN: ("densepose_coco_2014_minival_100",) TEST: ("densepose_coco_2014_minival_100",) SOLVER: MAX_ITER: 40 STEPS: (30,) IMS_PER_BATCH: 2 ================================================ FILE: detectron2/projects/DensePose/configs/quick_schedules/densepose_rcnn_R_50_FPN_DL_instant_test.yaml ================================================ _BASE_: "../Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ROI_DENSEPOSE_HEAD: NAME: "DensePoseDeepLabHead" DATASETS: TRAIN: ("densepose_coco_2014_minival_100",) TEST: ("densepose_coco_2014_minival_100",) SOLVER: MAX_ITER: 40 STEPS: (30,) ================================================ FILE: detectron2/projects/DensePose/configs/quick_schedules/densepose_rcnn_R_50_FPN_TTA_inference_acc_test.yaml ================================================ _BASE_: "../densepose_rcnn_R_50_FPN_s1x.yaml" MODEL: WEIGHTS: "https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl" DATASETS: TRAIN: () TEST: ("densepose_coco_2014_minival_100",) TEST: AUG: ENABLED: True MIN_SIZES: (400, 500, 600, 700, 800, 900, 1000, 1100, 1200) MAX_SIZE: 4000 FLIP: True EXPECTED_RESULTS: [["bbox_TTA", "AP", 61.74, 0.03], ["densepose_gps_TTA", "AP", 60.22, 0.03], ["densepose_gpsm_TTA", "AP", 63.59, 0.03]] ================================================ FILE: detectron2/projects/DensePose/configs/quick_schedules/densepose_rcnn_R_50_FPN_WC1_instant_test.yaml ================================================ _BASE_: "../Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: UV_CONFIDENCE: ENABLED: True TYPE: "iid_iso" POINT_REGRESSION_WEIGHTS: 0.0005 DATASETS: TRAIN: ("densepose_coco_2014_minival_100",) TEST: ("densepose_coco_2014_minival_100",) SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 40 STEPS: (30,) WARMUP_FACTOR: 0.025 ================================================ FILE: detectron2/projects/DensePose/configs/quick_schedules/densepose_rcnn_R_50_FPN_WC2_instant_test.yaml ================================================ _BASE_: "../Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ROI_DENSEPOSE_HEAD: UV_CONFIDENCE: ENABLED: True TYPE: "indep_aniso" POINT_REGRESSION_WEIGHTS: 0.0005 DATASETS: TRAIN: ("densepose_coco_2014_minival_100",) TEST: ("densepose_coco_2014_minival_100",) SOLVER: CLIP_GRADIENTS: ENABLED: True MAX_ITER: 40 STEPS: (30,) WARMUP_FACTOR: 0.025 ================================================ FILE: detectron2/projects/DensePose/configs/quick_schedules/densepose_rcnn_R_50_FPN_inference_acc_test.yaml ================================================ _BASE_: "../densepose_rcnn_R_50_FPN_s1x.yaml" MODEL: WEIGHTS: "https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl" DATASETS: TRAIN: () TEST: ("densepose_coco_2014_minival_100",) TEST: EXPECTED_RESULTS: [["bbox", "AP", 59.27, 0.025], ["densepose_gps", "AP", 60.11, 0.02], ["densepose_gpsm", "AP", 64.09, 0.02]] ================================================ FILE: detectron2/projects/DensePose/configs/quick_schedules/densepose_rcnn_R_50_FPN_instant_test.yaml ================================================ _BASE_: "../Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" DATASETS: TRAIN: ("densepose_coco_2014_minival_100",) TEST: ("densepose_coco_2014_minival_100",) SOLVER: MAX_ITER: 40 STEPS: (30,) ================================================ FILE: detectron2/projects/DensePose/configs/quick_schedules/densepose_rcnn_R_50_FPN_training_acc_test.yaml ================================================ _BASE_: "../Base-DensePose-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" ROI_HEADS: NUM_CLASSES: 1 DATASETS: TRAIN: ("densepose_coco_2014_minival",) TEST: ("densepose_coco_2014_minival",) SOLVER: CLIP_GRADIENTS: ENABLED: True CLIP_TYPE: norm CLIP_VALUE: 1.0 MAX_ITER: 6000 STEPS: (5500, 5800) TEST: EXPECTED_RESULTS: [["bbox", "AP", 76.2477, 1.0], ["densepose_gps", "AP", 79.6090, 1.5], ["densepose_gpsm", "AP", 80.0061, 1.5]] ================================================ FILE: detectron2/projects/DensePose/densepose/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .data.datasets import builtin # just to register data from .converters import builtin as builtin_converters # register converters from .config import ( add_densepose_config, add_densepose_head_config, add_hrnet_config, add_dataset_category_config, add_bootstrap_config, load_bootstrap_config, ) from .structures import DensePoseDataRelative, DensePoseList, DensePoseTransformData from .evaluation import DensePoseCOCOEvaluator from .modeling.roi_heads import DensePoseROIHeads from .modeling.test_time_augmentation import ( DensePoseGeneralizedRCNNWithTTA, DensePoseDatasetMapperTTA, ) from .utils.transform import load_from_cfg from .modeling.hrfpn import build_hrfpn_backbone ================================================ FILE: detectron2/projects/DensePose/densepose/config.py ================================================ # -*- coding = utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. # pyre-ignore-all-errors from detectron2.config import CfgNode as CN def add_dataset_category_config(cfg: CN) -> None: """ Add config for additional category-related dataset options - category whitelisting - category mapping """ _C = cfg _C.DATASETS.CATEGORY_MAPS = CN(new_allowed=True) _C.DATASETS.WHITELISTED_CATEGORIES = CN(new_allowed=True) # class to mesh mapping _C.DATASETS.CLASS_TO_MESH_NAME_MAPPING = CN(new_allowed=True) def add_evaluation_config(cfg: CN) -> None: _C = cfg _C.DENSEPOSE_EVALUATION = CN() # evaluator type, possible values: # - "iou": evaluator for models that produce iou data # - "cse": evaluator for models that produce cse data _C.DENSEPOSE_EVALUATION.TYPE = "iou" # storage for DensePose results, possible values: # - "none": no explicit storage, all the results are stored in the # dictionary with predictions, memory intensive; # historically the default storage type # - "ram": RAM storage, uses per-process RAM storage, which is # reduced to a single process storage on later stages, # less memory intensive # - "file": file storage, uses per-process file-based storage, # the least memory intensive, but may create bottlenecks # on file system accesses _C.DENSEPOSE_EVALUATION.STORAGE = "none" # minimum threshold for IOU values: the lower its values is, # the more matches are produced (and the higher the AP score) _C.DENSEPOSE_EVALUATION.MIN_IOU_THRESHOLD = 0.5 # Non-distributed inference is slower (at inference time) but can avoid RAM OOM _C.DENSEPOSE_EVALUATION.DISTRIBUTED_INFERENCE = True # evaluate mesh alignment based on vertex embeddings, only makes sense in CSE context _C.DENSEPOSE_EVALUATION.EVALUATE_MESH_ALIGNMENT = False # meshes to compute mesh alignment for _C.DENSEPOSE_EVALUATION.MESH_ALIGNMENT_MESH_NAMES = [] def add_bootstrap_config(cfg: CN) -> None: """ """ _C = cfg _C.BOOTSTRAP_DATASETS = [] _C.BOOTSTRAP_MODEL = CN() _C.BOOTSTRAP_MODEL.WEIGHTS = "" _C.BOOTSTRAP_MODEL.DEVICE = "cuda" def get_bootstrap_dataset_config() -> CN: _C = CN() _C.DATASET = "" # ratio used to mix data loaders _C.RATIO = 0.1 # image loader _C.IMAGE_LOADER = CN(new_allowed=True) _C.IMAGE_LOADER.TYPE = "" _C.IMAGE_LOADER.BATCH_SIZE = 4 _C.IMAGE_LOADER.NUM_WORKERS = 4 _C.IMAGE_LOADER.CATEGORIES = [] _C.IMAGE_LOADER.MAX_COUNT_PER_CATEGORY = 1_000_000 _C.IMAGE_LOADER.CATEGORY_TO_CLASS_MAPPING = CN(new_allowed=True) # inference _C.INFERENCE = CN() # batch size for model inputs _C.INFERENCE.INPUT_BATCH_SIZE = 4 # batch size to group model outputs _C.INFERENCE.OUTPUT_BATCH_SIZE = 2 # sampled data _C.DATA_SAMPLER = CN(new_allowed=True) _C.DATA_SAMPLER.TYPE = "" _C.DATA_SAMPLER.USE_GROUND_TRUTH_CATEGORIES = False # filter _C.FILTER = CN(new_allowed=True) _C.FILTER.TYPE = "" return _C def load_bootstrap_config(cfg: CN) -> None: """ Bootstrap datasets are given as a list of `dict` that are not automatically converted into CfgNode. This method processes all bootstrap dataset entries and ensures that they are in CfgNode format and comply with the specification """ if not cfg.BOOTSTRAP_DATASETS: return bootstrap_datasets_cfgnodes = [] for dataset_cfg in cfg.BOOTSTRAP_DATASETS: _C = get_bootstrap_dataset_config().clone() _C.merge_from_other_cfg(CN(dataset_cfg)) bootstrap_datasets_cfgnodes.append(_C) cfg.BOOTSTRAP_DATASETS = bootstrap_datasets_cfgnodes def add_densepose_head_cse_config(cfg: CN) -> None: """ Add configuration options for Continuous Surface Embeddings (CSE) """ _C = cfg _C.MODEL.ROI_DENSEPOSE_HEAD.CSE = CN() # Dimensionality D of the embedding space _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_SIZE = 16 # Embedder specifications for various mesh IDs _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDERS = CN(new_allowed=True) # normalization coefficient for embedding distances _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDING_DIST_GAUSS_SIGMA = 0.01 # normalization coefficient for geodesic distances _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.GEODESIC_DIST_GAUSS_SIGMA = 0.01 # embedding loss weight _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_LOSS_WEIGHT = 0.6 # embedding loss name, currently the following options are supported: # - EmbeddingLoss: cross-entropy on vertex labels # - SoftEmbeddingLoss: cross-entropy on vertex label combined with # Gaussian penalty on distance between vertices _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_LOSS_NAME = "EmbeddingLoss" # optimizer hyperparameters _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.FEATURES_LR_FACTOR = 1.0 _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDING_LR_FACTOR = 1.0 # Shape to shape cycle consistency loss parameters: _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS = CN({"ENABLED": False}) # shape to shape cycle consistency loss weight _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.WEIGHT = 0.025 # norm type used for loss computation _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.NORM_P = 2 # normalization term for embedding similarity matrices _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.TEMPERATURE = 0.05 # maximum number of vertices to include into shape to shape cycle loss # if negative or zero, all vertices are considered # if positive, random subset of vertices of given size is considered _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.MAX_NUM_VERTICES = 4936 # Pixel to shape cycle consistency loss parameters: _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS = CN({"ENABLED": False}) # pixel to shape cycle consistency loss weight _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.WEIGHT = 0.0001 # norm type used for loss computation _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.NORM_P = 2 # map images to all meshes and back (if false, use only gt meshes from the batch) _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.USE_ALL_MESHES_NOT_GT_ONLY = False # Randomly select at most this number of pixels from every instance # if negative or zero, all vertices are considered _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.NUM_PIXELS_TO_SAMPLE = 100 # normalization factor for pixel to pixel distances (higher value = smoother distribution) _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.PIXEL_SIGMA = 5.0 _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.TEMPERATURE_PIXEL_TO_VERTEX = 0.05 _C.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.TEMPERATURE_VERTEX_TO_PIXEL = 0.05 def add_densepose_head_config(cfg: CN) -> None: """ Add config for densepose head. """ _C = cfg _C.MODEL.DENSEPOSE_ON = True _C.MODEL.ROI_DENSEPOSE_HEAD = CN() _C.MODEL.ROI_DENSEPOSE_HEAD.NAME = "" _C.MODEL.ROI_DENSEPOSE_HEAD.NUM_STACKED_CONVS = 8 # Number of parts used for point labels _C.MODEL.ROI_DENSEPOSE_HEAD.NUM_PATCHES = 24 _C.MODEL.ROI_DENSEPOSE_HEAD.DECONV_KERNEL = 4 _C.MODEL.ROI_DENSEPOSE_HEAD.CONV_HEAD_DIM = 512 _C.MODEL.ROI_DENSEPOSE_HEAD.CONV_HEAD_KERNEL = 3 _C.MODEL.ROI_DENSEPOSE_HEAD.UP_SCALE = 2 _C.MODEL.ROI_DENSEPOSE_HEAD.HEATMAP_SIZE = 112 _C.MODEL.ROI_DENSEPOSE_HEAD.POOLER_TYPE = "ROIAlignV2" _C.MODEL.ROI_DENSEPOSE_HEAD.POOLER_RESOLUTION = 28 _C.MODEL.ROI_DENSEPOSE_HEAD.POOLER_SAMPLING_RATIO = 2 _C.MODEL.ROI_DENSEPOSE_HEAD.NUM_COARSE_SEGM_CHANNELS = 2 # 15 or 2 # Overlap threshold for an RoI to be considered foreground (if >= FG_IOU_THRESHOLD) _C.MODEL.ROI_DENSEPOSE_HEAD.FG_IOU_THRESHOLD = 0.7 # Loss weights for annotation masks.(14 Parts) _C.MODEL.ROI_DENSEPOSE_HEAD.INDEX_WEIGHTS = 5.0 # Loss weights for surface parts. (24 Parts) _C.MODEL.ROI_DENSEPOSE_HEAD.PART_WEIGHTS = 1.0 # Loss weights for UV regression. _C.MODEL.ROI_DENSEPOSE_HEAD.POINT_REGRESSION_WEIGHTS = 0.01 # Coarse segmentation is trained using instance segmentation task data _C.MODEL.ROI_DENSEPOSE_HEAD.COARSE_SEGM_TRAINED_BY_MASKS = False # For Decoder _C.MODEL.ROI_DENSEPOSE_HEAD.DECODER_ON = True _C.MODEL.ROI_DENSEPOSE_HEAD.DECODER_NUM_CLASSES = 256 _C.MODEL.ROI_DENSEPOSE_HEAD.DECODER_CONV_DIMS = 256 _C.MODEL.ROI_DENSEPOSE_HEAD.DECODER_NORM = "" _C.MODEL.ROI_DENSEPOSE_HEAD.DECODER_COMMON_STRIDE = 4 # For DeepLab head _C.MODEL.ROI_DENSEPOSE_HEAD.DEEPLAB = CN() _C.MODEL.ROI_DENSEPOSE_HEAD.DEEPLAB.NORM = "GN" _C.MODEL.ROI_DENSEPOSE_HEAD.DEEPLAB.NONLOCAL_ON = 0 # Predictor class name, must be registered in DENSEPOSE_PREDICTOR_REGISTRY # Some registered predictors: # "DensePoseChartPredictor": predicts segmentation and UV coordinates for predefined charts # "DensePoseChartWithConfidencePredictor": predicts segmentation, UV coordinates # and associated confidences for predefined charts (default) # "DensePoseEmbeddingWithConfidencePredictor": predicts segmentation, embeddings # and associated confidences for CSE _C.MODEL.ROI_DENSEPOSE_HEAD.PREDICTOR_NAME = "DensePoseChartWithConfidencePredictor" # Loss class name, must be registered in DENSEPOSE_LOSS_REGISTRY # Some registered losses: # "DensePoseChartLoss": loss for chart-based models that estimate # segmentation and UV coordinates # "DensePoseChartWithConfidenceLoss": loss for chart-based models that estimate # segmentation, UV coordinates and the corresponding confidences (default) _C.MODEL.ROI_DENSEPOSE_HEAD.LOSS_NAME = "DensePoseChartWithConfidenceLoss" # Confidences # Enable learning UV confidences (variances) along with the actual values _C.MODEL.ROI_DENSEPOSE_HEAD.UV_CONFIDENCE = CN({"ENABLED": False}) # UV confidence lower bound _C.MODEL.ROI_DENSEPOSE_HEAD.UV_CONFIDENCE.EPSILON = 0.01 # Enable learning segmentation confidences (variances) along with the actual values _C.MODEL.ROI_DENSEPOSE_HEAD.SEGM_CONFIDENCE = CN({"ENABLED": False}) # Segmentation confidence lower bound _C.MODEL.ROI_DENSEPOSE_HEAD.SEGM_CONFIDENCE.EPSILON = 0.01 # Statistical model type for confidence learning, possible values: # - "iid_iso": statistically independent identically distributed residuals # with isotropic covariance # - "indep_aniso": statistically independent residuals with anisotropic # covariances _C.MODEL.ROI_DENSEPOSE_HEAD.UV_CONFIDENCE.TYPE = "iid_iso" # List of angles for rotation in data augmentation during training _C.INPUT.ROTATION_ANGLES = [0] _C.TEST.AUG.ROTATION_ANGLES = () # Rotation TTA add_densepose_head_cse_config(cfg) def add_hrnet_config(cfg: CN) -> None: """ Add config for HRNet backbone. """ _C = cfg # For HigherHRNet w32 _C.MODEL.HRNET = CN() _C.MODEL.HRNET.STEM_INPLANES = 64 _C.MODEL.HRNET.STAGE2 = CN() _C.MODEL.HRNET.STAGE2.NUM_MODULES = 1 _C.MODEL.HRNET.STAGE2.NUM_BRANCHES = 2 _C.MODEL.HRNET.STAGE2.BLOCK = "BASIC" _C.MODEL.HRNET.STAGE2.NUM_BLOCKS = [4, 4] _C.MODEL.HRNET.STAGE2.NUM_CHANNELS = [32, 64] _C.MODEL.HRNET.STAGE2.FUSE_METHOD = "SUM" _C.MODEL.HRNET.STAGE3 = CN() _C.MODEL.HRNET.STAGE3.NUM_MODULES = 4 _C.MODEL.HRNET.STAGE3.NUM_BRANCHES = 3 _C.MODEL.HRNET.STAGE3.BLOCK = "BASIC" _C.MODEL.HRNET.STAGE3.NUM_BLOCKS = [4, 4, 4] _C.MODEL.HRNET.STAGE3.NUM_CHANNELS = [32, 64, 128] _C.MODEL.HRNET.STAGE3.FUSE_METHOD = "SUM" _C.MODEL.HRNET.STAGE4 = CN() _C.MODEL.HRNET.STAGE4.NUM_MODULES = 3 _C.MODEL.HRNET.STAGE4.NUM_BRANCHES = 4 _C.MODEL.HRNET.STAGE4.BLOCK = "BASIC" _C.MODEL.HRNET.STAGE4.NUM_BLOCKS = [4, 4, 4, 4] _C.MODEL.HRNET.STAGE4.NUM_CHANNELS = [32, 64, 128, 256] _C.MODEL.HRNET.STAGE4.FUSE_METHOD = "SUM" _C.MODEL.HRNET.HRFPN = CN() _C.MODEL.HRNET.HRFPN.OUT_CHANNELS = 256 def add_densepose_config(cfg: CN) -> None: add_densepose_head_config(cfg) add_hrnet_config(cfg) add_bootstrap_config(cfg) add_dataset_category_config(cfg) add_evaluation_config(cfg) ================================================ FILE: detectron2/projects/DensePose/densepose/converters/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .hflip import HFlipConverter from .to_mask import ToMaskConverter from .to_chart_result import ToChartResultConverter, ToChartResultConverterWithConfidences from .segm_to_mask import ( predictor_output_with_fine_and_coarse_segm_to_mask, predictor_output_with_coarse_segm_to_mask, resample_fine_and_coarse_segm_to_bbox, ) from .chart_output_to_chart_result import ( densepose_chart_predictor_output_to_result, densepose_chart_predictor_output_to_result_with_confidences, ) from .chart_output_hflip import densepose_chart_predictor_output_hflip ================================================ FILE: detectron2/projects/DensePose/densepose/converters/base.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any, Tuple, Type import torch class BaseConverter: """ Converter base class to be reused by various converters. Converter allows one to convert data from various source types to a particular destination type. Each source type needs to register its converter. The registration for each source type is valid for all descendants of that type. """ @classmethod def register(cls, from_type: Type, converter: Any = None): """ Registers a converter for the specified type. Can be used as a decorator (if converter is None), or called as a method. Args: from_type (type): type to register the converter for; all instances of this type will use the same converter converter (callable): converter to be registered for the given type; if None, this method is assumed to be a decorator for the converter """ if converter is not None: cls._do_register(from_type, converter) def wrapper(converter: Any) -> Any: cls._do_register(from_type, converter) return converter return wrapper @classmethod def _do_register(cls, from_type: Type, converter: Any): cls.registry[from_type] = converter # pyre-ignore[16] @classmethod def _lookup_converter(cls, from_type: Type) -> Any: """ Perform recursive lookup for the given type to find registered converter. If a converter was found for some base class, it gets registered for this class to save on further lookups. Args: from_type: type for which to find a converter Return: callable or None - registered converter or None if no suitable entry was found in the registry """ if from_type in cls.registry: # pyre-ignore[16] return cls.registry[from_type] for base in from_type.__bases__: converter = cls._lookup_converter(base) if converter is not None: cls._do_register(from_type, converter) return converter return None @classmethod def convert(cls, instance: Any, *args, **kwargs): """ Convert an instance to the destination type using some registered converter. Does recursive lookup for base classes, so there's no need for explicit registration for derived classes. Args: instance: source instance to convert to the destination type Return: An instance of the destination type obtained from the source instance Raises KeyError, if no suitable converter found """ instance_type = type(instance) converter = cls._lookup_converter(instance_type) if converter is None: if cls.dst_type is None: # pyre-ignore[16] output_type_str = "itself" else: output_type_str = cls.dst_type raise KeyError(f"Could not find converter from {instance_type} to {output_type_str}") return converter(instance, *args, **kwargs) IntTupleBox = Tuple[int, int, int, int] def make_int_box(box: torch.Tensor) -> IntTupleBox: int_box = [0, 0, 0, 0] int_box[0], int_box[1], int_box[2], int_box[3] = tuple(box.long().tolist()) return int_box[0], int_box[1], int_box[2], int_box[3] ================================================ FILE: detectron2/projects/DensePose/densepose/converters/builtin.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from ..structures import DensePoseChartPredictorOutput, DensePoseEmbeddingPredictorOutput from . import ( HFlipConverter, ToChartResultConverter, ToChartResultConverterWithConfidences, ToMaskConverter, densepose_chart_predictor_output_hflip, densepose_chart_predictor_output_to_result, densepose_chart_predictor_output_to_result_with_confidences, predictor_output_with_coarse_segm_to_mask, predictor_output_with_fine_and_coarse_segm_to_mask, ) ToMaskConverter.register( DensePoseChartPredictorOutput, predictor_output_with_fine_and_coarse_segm_to_mask ) ToMaskConverter.register( DensePoseEmbeddingPredictorOutput, predictor_output_with_coarse_segm_to_mask ) ToChartResultConverter.register( DensePoseChartPredictorOutput, densepose_chart_predictor_output_to_result ) ToChartResultConverterWithConfidences.register( DensePoseChartPredictorOutput, densepose_chart_predictor_output_to_result_with_confidences ) HFlipConverter.register(DensePoseChartPredictorOutput, densepose_chart_predictor_output_hflip) ================================================ FILE: detectron2/projects/DensePose/densepose/converters/chart_output_hflip.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from dataclasses import fields import torch from densepose.structures import DensePoseChartPredictorOutput, DensePoseTransformData def densepose_chart_predictor_output_hflip( densepose_predictor_output: DensePoseChartPredictorOutput, transform_data: DensePoseTransformData, ) -> DensePoseChartPredictorOutput: """ Change to take into account a Horizontal flip. """ if len(densepose_predictor_output) > 0: PredictorOutput = type(densepose_predictor_output) output_dict = {} for field in fields(densepose_predictor_output): field_value = getattr(densepose_predictor_output, field.name) # flip tensors if isinstance(field_value, torch.Tensor): setattr(densepose_predictor_output, field.name, torch.flip(field_value, [3])) densepose_predictor_output = _flip_iuv_semantics_tensor( densepose_predictor_output, transform_data ) densepose_predictor_output = _flip_segm_semantics_tensor( densepose_predictor_output, transform_data ) for field in fields(densepose_predictor_output): output_dict[field.name] = getattr(densepose_predictor_output, field.name) return PredictorOutput(**output_dict) else: return densepose_predictor_output def _flip_iuv_semantics_tensor( densepose_predictor_output: DensePoseChartPredictorOutput, dp_transform_data: DensePoseTransformData, ) -> DensePoseChartPredictorOutput: point_label_symmetries = dp_transform_data.point_label_symmetries uv_symmetries = dp_transform_data.uv_symmetries N, C, H, W = densepose_predictor_output.u.shape u_loc = (densepose_predictor_output.u[:, 1:, :, :].clamp(0, 1) * 255).long() v_loc = (densepose_predictor_output.v[:, 1:, :, :].clamp(0, 1) * 255).long() Iindex = torch.arange(C - 1, device=densepose_predictor_output.u.device)[ None, :, None, None ].expand(N, C - 1, H, W) densepose_predictor_output.u[:, 1:, :, :] = uv_symmetries["U_transforms"][Iindex, v_loc, u_loc] densepose_predictor_output.v[:, 1:, :, :] = uv_symmetries["V_transforms"][Iindex, v_loc, u_loc] for el in ["fine_segm", "u", "v"]: densepose_predictor_output.__dict__[el] = densepose_predictor_output.__dict__[el][ :, point_label_symmetries, :, : ] return densepose_predictor_output def _flip_segm_semantics_tensor( densepose_predictor_output: DensePoseChartPredictorOutput, dp_transform_data ): if densepose_predictor_output.coarse_segm.shape[1] > 2: densepose_predictor_output.coarse_segm = densepose_predictor_output.coarse_segm[ :, dp_transform_data.mask_label_symmetries, :, : ] return densepose_predictor_output ================================================ FILE: detectron2/projects/DensePose/densepose/converters/chart_output_to_chart_result.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Dict import torch from torch.nn import functional as F from detectron2.structures.boxes import Boxes, BoxMode from ..structures import ( DensePoseChartPredictorOutput, DensePoseChartResult, DensePoseChartResultWithConfidences, ) from . import resample_fine_and_coarse_segm_to_bbox from .base import IntTupleBox, make_int_box def resample_uv_tensors_to_bbox( u: torch.Tensor, v: torch.Tensor, labels: torch.Tensor, box_xywh_abs: IntTupleBox, ) -> torch.Tensor: """ Resamples U and V coordinate estimates for the given bounding box Args: u (tensor [1, C, H, W] of float): U coordinates v (tensor [1, C, H, W] of float): V coordinates labels (tensor [H, W] of long): labels obtained by resampling segmentation outputs for the given bounding box box_xywh_abs (tuple of 4 int): bounding box that corresponds to predictor outputs Return: Resampled U and V coordinates - a tensor [2, H, W] of float """ x, y, w, h = box_xywh_abs w = max(int(w), 1) h = max(int(h), 1) u_bbox = F.interpolate(u, (h, w), mode="bilinear", align_corners=False) v_bbox = F.interpolate(v, (h, w), mode="bilinear", align_corners=False) uv = torch.zeros([2, h, w], dtype=torch.float32, device=u.device) for part_id in range(1, u_bbox.size(1)): uv[0][labels == part_id] = u_bbox[0, part_id][labels == part_id] uv[1][labels == part_id] = v_bbox[0, part_id][labels == part_id] return uv def resample_uv_to_bbox( predictor_output: DensePoseChartPredictorOutput, labels: torch.Tensor, box_xywh_abs: IntTupleBox, ) -> torch.Tensor: """ Resamples U and V coordinate estimates for the given bounding box Args: predictor_output (DensePoseChartPredictorOutput): DensePose predictor output to be resampled labels (tensor [H, W] of long): labels obtained by resampling segmentation outputs for the given bounding box box_xywh_abs (tuple of 4 int): bounding box that corresponds to predictor outputs Return: Resampled U and V coordinates - a tensor [2, H, W] of float """ return resample_uv_tensors_to_bbox( predictor_output.u, predictor_output.v, labels, box_xywh_abs, ) def densepose_chart_predictor_output_to_result( predictor_output: DensePoseChartPredictorOutput, boxes: Boxes ) -> DensePoseChartResult: """ Convert densepose chart predictor outputs to results Args: predictor_output (DensePoseChartPredictorOutput): DensePose predictor output to be converted to results, must contain only 1 output boxes (Boxes): bounding box that corresponds to the predictor output, must contain only 1 bounding box Return: DensePose chart-based result (DensePoseChartResult) """ assert len(predictor_output) == 1 and len(boxes) == 1, ( f"Predictor output to result conversion can operate only single outputs" f", got {len(predictor_output)} predictor outputs and {len(boxes)} boxes" ) boxes_xyxy_abs = boxes.tensor.clone() boxes_xywh_abs = BoxMode.convert(boxes_xyxy_abs, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS) box_xywh = make_int_box(boxes_xywh_abs[0]) labels = resample_fine_and_coarse_segm_to_bbox(predictor_output, box_xywh).squeeze(0) uv = resample_uv_to_bbox(predictor_output, labels, box_xywh) return DensePoseChartResult(labels=labels, uv=uv) def resample_confidences_to_bbox( predictor_output: DensePoseChartPredictorOutput, labels: torch.Tensor, box_xywh_abs: IntTupleBox, ) -> Dict[str, torch.Tensor]: """ Resamples confidences for the given bounding box Args: predictor_output (DensePoseChartPredictorOutput): DensePose predictor output to be resampled labels (tensor [H, W] of long): labels obtained by resampling segmentation outputs for the given bounding box box_xywh_abs (tuple of 4 int): bounding box that corresponds to predictor outputs Return: Resampled confidences - a dict of [H, W] tensors of float """ x, y, w, h = box_xywh_abs w = max(int(w), 1) h = max(int(h), 1) confidence_names = [ "sigma_1", "sigma_2", "kappa_u", "kappa_v", "fine_segm_confidence", "coarse_segm_confidence", ] confidence_results = {key: None for key in confidence_names} confidence_names = [ key for key in confidence_names if getattr(predictor_output, key) is not None ] confidence_base = torch.zeros([h, w], dtype=torch.float32, device=predictor_output.u.device) # assign data from channels that correspond to the labels for key in confidence_names: resampled_confidence = F.interpolate( getattr(predictor_output, key), (h, w), mode="bilinear", align_corners=False, ) result = confidence_base.clone() for part_id in range(1, predictor_output.u.size(1)): if resampled_confidence.size(1) != predictor_output.u.size(1): # confidence is not part-based, don't try to fill it part by part continue result[labels == part_id] = resampled_confidence[0, part_id][labels == part_id] if resampled_confidence.size(1) != predictor_output.u.size(1): # confidence is not part-based, fill the data with the first channel # (targeted for segmentation confidences that have only 1 channel) result = resampled_confidence[0, 0] confidence_results[key] = result return confidence_results # pyre-ignore[7] def densepose_chart_predictor_output_to_result_with_confidences( predictor_output: DensePoseChartPredictorOutput, boxes: Boxes ) -> DensePoseChartResultWithConfidences: """ Convert densepose chart predictor outputs to results Args: predictor_output (DensePoseChartPredictorOutput): DensePose predictor output with confidences to be converted to results, must contain only 1 output boxes (Boxes): bounding box that corresponds to the predictor output, must contain only 1 bounding box Return: DensePose chart-based result with confidences (DensePoseChartResultWithConfidences) """ assert len(predictor_output) == 1 and len(boxes) == 1, ( f"Predictor output to result conversion can operate only single outputs" f", got {len(predictor_output)} predictor outputs and {len(boxes)} boxes" ) boxes_xyxy_abs = boxes.tensor.clone() boxes_xywh_abs = BoxMode.convert(boxes_xyxy_abs, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS) box_xywh = make_int_box(boxes_xywh_abs[0]) labels = resample_fine_and_coarse_segm_to_bbox(predictor_output, box_xywh).squeeze(0) uv = resample_uv_to_bbox(predictor_output, labels, box_xywh) confidences = resample_confidences_to_bbox(predictor_output, labels, box_xywh) return DensePoseChartResultWithConfidences(labels=labels, uv=uv, **confidences) ================================================ FILE: detectron2/projects/DensePose/densepose/converters/hflip.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any from .base import BaseConverter class HFlipConverter(BaseConverter): """ Converts various DensePose predictor outputs to DensePose results. Each DensePose predictor output type has to register its convertion strategy. """ registry = {} dst_type = None @classmethod def convert(cls, predictor_outputs: Any, transform_data: Any, *args, **kwargs): """ Performs an horizontal flip on DensePose predictor outputs. Does recursive lookup for base classes, so there's no need for explicit registration for derived classes. Args: predictor_outputs: DensePose predictor output to be converted to BitMasks transform_data: Anything useful for the flip Return: An instance of the same type as predictor_outputs """ return super(HFlipConverter, cls).convert( predictor_outputs, transform_data, *args, **kwargs ) ================================================ FILE: detectron2/projects/DensePose/densepose/converters/segm_to_mask.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any import torch from torch.nn import functional as F from detectron2.structures import BitMasks, Boxes, BoxMode from .base import IntTupleBox, make_int_box from .to_mask import ImageSizeType def resample_coarse_segm_tensor_to_bbox(coarse_segm: torch.Tensor, box_xywh_abs: IntTupleBox): """ Resample coarse segmentation tensor to the given bounding box and derive labels for each pixel of the bounding box Args: coarse_segm: float tensor of shape [1, K, Hout, Wout] box_xywh_abs (tuple of 4 int): bounding box given by its upper-left corner coordinates, width (W) and height (H) Return: Labels for each pixel of the bounding box, a long tensor of size [1, H, W] """ x, y, w, h = box_xywh_abs w = max(int(w), 1) h = max(int(h), 1) labels = F.interpolate(coarse_segm, (h, w), mode="bilinear", align_corners=False).argmax(dim=1) return labels def resample_fine_and_coarse_segm_tensors_to_bbox( fine_segm: torch.Tensor, coarse_segm: torch.Tensor, box_xywh_abs: IntTupleBox ): """ Resample fine and coarse segmentation tensors to the given bounding box and derive labels for each pixel of the bounding box Args: fine_segm: float tensor of shape [1, C, Hout, Wout] coarse_segm: float tensor of shape [1, K, Hout, Wout] box_xywh_abs (tuple of 4 int): bounding box given by its upper-left corner coordinates, width (W) and height (H) Return: Labels for each pixel of the bounding box, a long tensor of size [1, H, W] """ x, y, w, h = box_xywh_abs w = max(int(w), 1) h = max(int(h), 1) # coarse segmentation coarse_segm_bbox = F.interpolate( coarse_segm, (h, w), mode="bilinear", align_corners=False, ).argmax(dim=1) # combined coarse and fine segmentation labels = ( F.interpolate(fine_segm, (h, w), mode="bilinear", align_corners=False).argmax(dim=1) * (coarse_segm_bbox > 0).long() ) return labels def resample_fine_and_coarse_segm_to_bbox(predictor_output: Any, box_xywh_abs: IntTupleBox): """ Resample fine and coarse segmentation outputs from a predictor to the given bounding box and derive labels for each pixel of the bounding box Args: predictor_output: DensePose predictor output that contains segmentation results to be resampled box_xywh_abs (tuple of 4 int): bounding box given by its upper-left corner coordinates, width (W) and height (H) Return: Labels for each pixel of the bounding box, a long tensor of size [1, H, W] """ return resample_fine_and_coarse_segm_tensors_to_bbox( predictor_output.fine_segm, predictor_output.coarse_segm, box_xywh_abs, ) def predictor_output_with_coarse_segm_to_mask( predictor_output: Any, boxes: Boxes, image_size_hw: ImageSizeType ) -> BitMasks: """ Convert predictor output with coarse and fine segmentation to a mask. Assumes that predictor output has the following attributes: - coarse_segm (tensor of size [N, D, H, W]): coarse segmentation unnormalized scores for N instances; D is the number of coarse segmentation labels, H and W is the resolution of the estimate Args: predictor_output: DensePose predictor output to be converted to mask boxes (Boxes): bounding boxes that correspond to the DensePose predictor outputs image_size_hw (tuple [int, int]): image height Himg and width Wimg Return: BitMasks that contain a bool tensor of size [N, Himg, Wimg] with a mask of the size of the image for each instance """ H, W = image_size_hw boxes_xyxy_abs = boxes.tensor.clone() boxes_xywh_abs = BoxMode.convert(boxes_xyxy_abs, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS) N = len(boxes_xywh_abs) masks = torch.zeros((N, H, W), dtype=torch.bool, device=boxes.tensor.device) for i in range(len(boxes_xywh_abs)): box_xywh = make_int_box(boxes_xywh_abs[i]) box_mask = resample_coarse_segm_tensor_to_bbox(predictor_output[i].coarse_segm, box_xywh) x, y, w, h = box_xywh masks[i, y : y + h, x : x + w] = box_mask return BitMasks(masks) def predictor_output_with_fine_and_coarse_segm_to_mask( predictor_output: Any, boxes: Boxes, image_size_hw: ImageSizeType ) -> BitMasks: """ Convert predictor output with coarse and fine segmentation to a mask. Assumes that predictor output has the following attributes: - coarse_segm (tensor of size [N, D, H, W]): coarse segmentation unnormalized scores for N instances; D is the number of coarse segmentation labels, H and W is the resolution of the estimate - fine_segm (tensor of size [N, C, H, W]): fine segmentation unnormalized scores for N instances; C is the number of fine segmentation labels, H and W is the resolution of the estimate Args: predictor_output: DensePose predictor output to be converted to mask boxes (Boxes): bounding boxes that correspond to the DensePose predictor outputs image_size_hw (tuple [int, int]): image height Himg and width Wimg Return: BitMasks that contain a bool tensor of size [N, Himg, Wimg] with a mask of the size of the image for each instance """ H, W = image_size_hw boxes_xyxy_abs = boxes.tensor.clone() boxes_xywh_abs = BoxMode.convert(boxes_xyxy_abs, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS) N = len(boxes_xywh_abs) masks = torch.zeros((N, H, W), dtype=torch.bool, device=boxes.tensor.device) for i in range(len(boxes_xywh_abs)): box_xywh = make_int_box(boxes_xywh_abs[i]) labels_i = resample_fine_and_coarse_segm_to_bbox(predictor_output[i], box_xywh) x, y, w, h = box_xywh masks[i, y : y + h, x : x + w] = labels_i > 0 return BitMasks(masks) ================================================ FILE: detectron2/projects/DensePose/densepose/converters/to_chart_result.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any from detectron2.structures import Boxes from ..structures import DensePoseChartResult, DensePoseChartResultWithConfidences from .base import BaseConverter class ToChartResultConverter(BaseConverter): """ Converts various DensePose predictor outputs to DensePose results. Each DensePose predictor output type has to register its convertion strategy. """ registry = {} dst_type = DensePoseChartResult @classmethod def convert(cls, predictor_outputs: Any, boxes: Boxes, *args, **kwargs) -> DensePoseChartResult: """ Convert DensePose predictor outputs to DensePoseResult using some registered converter. Does recursive lookup for base classes, so there's no need for explicit registration for derived classes. Args: densepose_predictor_outputs: DensePose predictor output to be converted to BitMasks boxes (Boxes): bounding boxes that correspond to the DensePose predictor outputs Return: An instance of DensePoseResult. If no suitable converter was found, raises KeyError """ return super(ToChartResultConverter, cls).convert(predictor_outputs, boxes, *args, **kwargs) class ToChartResultConverterWithConfidences(BaseConverter): """ Converts various DensePose predictor outputs to DensePose results. Each DensePose predictor output type has to register its convertion strategy. """ registry = {} dst_type = DensePoseChartResultWithConfidences @classmethod def convert( cls, predictor_outputs: Any, boxes: Boxes, *args, **kwargs ) -> DensePoseChartResultWithConfidences: """ Convert DensePose predictor outputs to DensePoseResult with confidences using some registered converter. Does recursive lookup for base classes, so there's no need for explicit registration for derived classes. Args: densepose_predictor_outputs: DensePose predictor output with confidences to be converted to BitMasks boxes (Boxes): bounding boxes that correspond to the DensePose predictor outputs Return: An instance of DensePoseResult. If no suitable converter was found, raises KeyError """ return super(ToChartResultConverterWithConfidences, cls).convert( predictor_outputs, boxes, *args, **kwargs ) ================================================ FILE: detectron2/projects/DensePose/densepose/converters/to_mask.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any, Tuple from detectron2.structures import BitMasks, Boxes from .base import BaseConverter ImageSizeType = Tuple[int, int] class ToMaskConverter(BaseConverter): """ Converts various DensePose predictor outputs to masks in bit mask format (see `BitMasks`). Each DensePose predictor output type has to register its convertion strategy. """ registry = {} dst_type = BitMasks @classmethod def convert( cls, densepose_predictor_outputs: Any, boxes: Boxes, image_size_hw: ImageSizeType, *args, **kwargs ) -> BitMasks: """ Convert DensePose predictor outputs to BitMasks using some registered converter. Does recursive lookup for base classes, so there's no need for explicit registration for derived classes. Args: densepose_predictor_outputs: DensePose predictor output to be converted to BitMasks boxes (Boxes): bounding boxes that correspond to the DensePose predictor outputs image_size_hw (tuple [int, int]): image height and width Return: An instance of `BitMasks`. If no suitable converter was found, raises KeyError """ return super(ToMaskConverter, cls).convert( densepose_predictor_outputs, boxes, image_size_hw, *args, **kwargs ) ================================================ FILE: detectron2/projects/DensePose/densepose/data/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .meshes import builtin from .build import ( build_detection_test_loader, build_detection_train_loader, build_combined_loader, build_frame_selector, build_inference_based_loaders, has_inference_based_loaders, BootstrapDatasetFactoryCatalog, ) from .combined_loader import CombinedDataLoader from .dataset_mapper import DatasetMapper from .inference_based_loader import InferenceBasedLoader, ScoreBasedFilter from .image_list_dataset import ImageListDataset from .utils import is_relative_local_path, maybe_prepend_base_path # ensure the builtin datasets are registered from . import datasets # ensure the bootstrap datasets builders are registered from . import build __all__ = [k for k in globals().keys() if not k.startswith("_")] ================================================ FILE: detectron2/projects/DensePose/densepose/data/build.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import itertools import logging import numpy as np from collections import UserDict, defaultdict from dataclasses import dataclass from typing import Any, Callable, Collection, Dict, Iterable, List, Optional, Sequence, Tuple import torch from torch.utils.data.dataset import Dataset from detectron2.config import CfgNode from detectron2.data.build import build_detection_test_loader as d2_build_detection_test_loader from detectron2.data.build import build_detection_train_loader as d2_build_detection_train_loader from detectron2.data.build import ( load_proposals_into_dataset, print_instances_class_histogram, trivial_batch_collator, worker_init_reset_seed, ) from detectron2.data.catalog import DatasetCatalog, Metadata, MetadataCatalog from detectron2.data.samplers import TrainingSampler from detectron2.utils.comm import get_world_size from densepose.config import get_bootstrap_dataset_config from densepose.modeling import build_densepose_embedder from .combined_loader import CombinedDataLoader, Loader from .dataset_mapper import DatasetMapper from .datasets.coco import DENSEPOSE_CSE_KEYS_WITHOUT_MASK, DENSEPOSE_IUV_KEYS_WITHOUT_MASK from .datasets.dataset_type import DatasetType from .inference_based_loader import InferenceBasedLoader, ScoreBasedFilter from .samplers import ( DensePoseConfidenceBasedSampler, DensePoseCSEConfidenceBasedSampler, DensePoseCSEUniformSampler, DensePoseUniformSampler, MaskFromDensePoseSampler, PredictionToGroundTruthSampler, ) from .transform import ImageResizeTransform from .utils import get_category_to_class_mapping, get_class_to_mesh_name_mapping from .video import ( FirstKFramesSelector, FrameSelectionStrategy, LastKFramesSelector, RandomKFramesSelector, VideoKeyframeDataset, video_list_from_file, ) __all__ = ["build_detection_train_loader", "build_detection_test_loader"] Instance = Dict[str, Any] InstancePredicate = Callable[[Instance], bool] def _compute_num_images_per_worker(cfg: CfgNode) -> int: num_workers = get_world_size() images_per_batch = cfg.SOLVER.IMS_PER_BATCH assert ( images_per_batch % num_workers == 0 ), "SOLVER.IMS_PER_BATCH ({}) must be divisible by the number of workers ({}).".format( images_per_batch, num_workers ) assert ( images_per_batch >= num_workers ), "SOLVER.IMS_PER_BATCH ({}) must be larger than the number of workers ({}).".format( images_per_batch, num_workers ) images_per_worker = images_per_batch // num_workers return images_per_worker def _map_category_id_to_contiguous_id(dataset_name: str, dataset_dicts: Iterable[Instance]) -> None: meta = MetadataCatalog.get(dataset_name) for dataset_dict in dataset_dicts: for ann in dataset_dict["annotations"]: ann["category_id"] = meta.thing_dataset_id_to_contiguous_id[ann["category_id"]] @dataclass class _DatasetCategory: """ Class representing category data in a dataset: - id: category ID, as specified in the dataset annotations file - name: category name, as specified in the dataset annotations file - mapped_id: category ID after applying category maps (DATASETS.CATEGORY_MAPS config option) - mapped_name: category name after applying category maps - dataset_name: dataset in which the category is defined For example, when training models in a class-agnostic manner, one could take LVIS 1.0 dataset and map the animal categories to the same category as human data from COCO: id = 225 name = "cat" mapped_id = 1 mapped_name = "person" dataset_name = "lvis_v1_animals_dp_train" """ id: int name: str mapped_id: int mapped_name: str dataset_name: str _MergedCategoriesT = Dict[int, List[_DatasetCategory]] def _add_category_id_to_contiguous_id_maps_to_metadata( merged_categories: _MergedCategoriesT, ) -> None: merged_categories_per_dataset = {} for contiguous_cat_id, cat_id in enumerate(sorted(merged_categories.keys())): for cat in merged_categories[cat_id]: if cat.dataset_name not in merged_categories_per_dataset: merged_categories_per_dataset[cat.dataset_name] = defaultdict(list) merged_categories_per_dataset[cat.dataset_name][cat_id].append( ( contiguous_cat_id, cat, ) ) logger = logging.getLogger(__name__) for dataset_name, merged_categories in merged_categories_per_dataset.items(): meta = MetadataCatalog.get(dataset_name) if not hasattr(meta, "thing_classes"): meta.thing_classes = [] meta.thing_dataset_id_to_contiguous_id = {} meta.thing_dataset_id_to_merged_id = {} else: meta.thing_classes.clear() meta.thing_dataset_id_to_contiguous_id.clear() meta.thing_dataset_id_to_merged_id.clear() logger.info(f"Dataset {dataset_name}: category ID to contiguous ID mapping:") for _cat_id, categories in sorted(merged_categories.items()): added_to_thing_classes = False for contiguous_cat_id, cat in categories: if not added_to_thing_classes: meta.thing_classes.append(cat.mapped_name) added_to_thing_classes = True meta.thing_dataset_id_to_contiguous_id[cat.id] = contiguous_cat_id meta.thing_dataset_id_to_merged_id[cat.id] = cat.mapped_id logger.info(f"{cat.id} ({cat.name}) -> {contiguous_cat_id}") def _maybe_create_general_keep_instance_predicate(cfg: CfgNode) -> Optional[InstancePredicate]: def has_annotations(instance: Instance) -> bool: return "annotations" in instance def has_only_crowd_anotations(instance: Instance) -> bool: for ann in instance["annotations"]: if ann.get("is_crowd", 0) == 0: return False return True def general_keep_instance_predicate(instance: Instance) -> bool: return has_annotations(instance) and not has_only_crowd_anotations(instance) if not cfg.DATALOADER.FILTER_EMPTY_ANNOTATIONS: return None return general_keep_instance_predicate def _maybe_create_keypoints_keep_instance_predicate(cfg: CfgNode) -> Optional[InstancePredicate]: min_num_keypoints = cfg.MODEL.ROI_KEYPOINT_HEAD.MIN_KEYPOINTS_PER_IMAGE def has_sufficient_num_keypoints(instance: Instance) -> bool: num_kpts = sum( (np.array(ann["keypoints"][2::3]) > 0).sum() for ann in instance["annotations"] if "keypoints" in ann ) return num_kpts >= min_num_keypoints if cfg.MODEL.KEYPOINT_ON and (min_num_keypoints > 0): return has_sufficient_num_keypoints return None def _maybe_create_mask_keep_instance_predicate(cfg: CfgNode) -> Optional[InstancePredicate]: if not cfg.MODEL.MASK_ON: return None def has_mask_annotations(instance: Instance) -> bool: return any("segmentation" in ann for ann in instance["annotations"]) return has_mask_annotations def _maybe_create_densepose_keep_instance_predicate(cfg: CfgNode) -> Optional[InstancePredicate]: if not cfg.MODEL.DENSEPOSE_ON: return None use_masks = cfg.MODEL.ROI_DENSEPOSE_HEAD.COARSE_SEGM_TRAINED_BY_MASKS def has_densepose_annotations(instance: Instance) -> bool: for ann in instance["annotations"]: if all(key in ann for key in DENSEPOSE_IUV_KEYS_WITHOUT_MASK) or all( key in ann for key in DENSEPOSE_CSE_KEYS_WITHOUT_MASK ): return True if use_masks and "segmentation" in ann: return True return False return has_densepose_annotations def _maybe_create_specific_keep_instance_predicate(cfg: CfgNode) -> Optional[InstancePredicate]: specific_predicate_creators = [ _maybe_create_keypoints_keep_instance_predicate, _maybe_create_mask_keep_instance_predicate, _maybe_create_densepose_keep_instance_predicate, ] predicates = [creator(cfg) for creator in specific_predicate_creators] predicates = [p for p in predicates if p is not None] if not predicates: return None def combined_predicate(instance: Instance) -> bool: return any(p(instance) for p in predicates) return combined_predicate def _get_train_keep_instance_predicate(cfg: CfgNode): general_keep_predicate = _maybe_create_general_keep_instance_predicate(cfg) combined_specific_keep_predicate = _maybe_create_specific_keep_instance_predicate(cfg) def combined_general_specific_keep_predicate(instance: Instance) -> bool: return general_keep_predicate(instance) and combined_specific_keep_predicate(instance) if (general_keep_predicate is None) and (combined_specific_keep_predicate is None): return None if general_keep_predicate is None: return combined_specific_keep_predicate if combined_specific_keep_predicate is None: return general_keep_predicate return combined_general_specific_keep_predicate def _get_test_keep_instance_predicate(cfg: CfgNode): general_keep_predicate = _maybe_create_general_keep_instance_predicate(cfg) return general_keep_predicate def _maybe_filter_and_map_categories( dataset_name: str, dataset_dicts: List[Instance] ) -> List[Instance]: meta = MetadataCatalog.get(dataset_name) category_id_map = meta.thing_dataset_id_to_contiguous_id filtered_dataset_dicts = [] for dataset_dict in dataset_dicts: anns = [] for ann in dataset_dict["annotations"]: cat_id = ann["category_id"] if cat_id not in category_id_map: continue ann["category_id"] = category_id_map[cat_id] anns.append(ann) dataset_dict["annotations"] = anns filtered_dataset_dicts.append(dataset_dict) return filtered_dataset_dicts def _add_category_whitelists_to_metadata(cfg: CfgNode) -> None: for dataset_name, whitelisted_cat_ids in cfg.DATASETS.WHITELISTED_CATEGORIES.items(): meta = MetadataCatalog.get(dataset_name) meta.whitelisted_categories = whitelisted_cat_ids logger = logging.getLogger(__name__) logger.info( "Whitelisted categories for dataset {}: {}".format( dataset_name, meta.whitelisted_categories ) ) def _add_category_maps_to_metadata(cfg: CfgNode) -> None: for dataset_name, category_map in cfg.DATASETS.CATEGORY_MAPS.items(): category_map = { int(cat_id_src): int(cat_id_dst) for cat_id_src, cat_id_dst in category_map.items() } meta = MetadataCatalog.get(dataset_name) meta.category_map = category_map logger = logging.getLogger(__name__) logger.info("Category maps for dataset {}: {}".format(dataset_name, meta.category_map)) def _add_category_info_to_bootstrapping_metadata(dataset_name: str, dataset_cfg: CfgNode) -> None: meta = MetadataCatalog.get(dataset_name) meta.category_to_class_mapping = get_category_to_class_mapping(dataset_cfg) meta.categories = dataset_cfg.CATEGORIES meta.max_count_per_category = dataset_cfg.MAX_COUNT_PER_CATEGORY logger = logging.getLogger(__name__) logger.info( "Category to class mapping for dataset {}: {}".format( dataset_name, meta.category_to_class_mapping ) ) def _maybe_add_class_to_mesh_name_map_to_metadata(dataset_names: List[str], cfg: CfgNode) -> None: for dataset_name in dataset_names: meta = MetadataCatalog.get(dataset_name) if not hasattr(meta, "class_to_mesh_name"): meta.class_to_mesh_name = get_class_to_mesh_name_mapping(cfg) def _merge_categories(dataset_names: Collection[str]) -> _MergedCategoriesT: merged_categories = defaultdict(list) category_names = {} for dataset_name in dataset_names: meta = MetadataCatalog.get(dataset_name) whitelisted_categories = meta.get("whitelisted_categories") category_map = meta.get("category_map", {}) cat_ids = ( whitelisted_categories if whitelisted_categories is not None else meta.categories.keys() ) for cat_id in cat_ids: cat_name = meta.categories[cat_id] cat_id_mapped = category_map.get(cat_id, cat_id) if cat_id_mapped == cat_id or cat_id_mapped in cat_ids: category_names[cat_id] = cat_name else: category_names[cat_id] = str(cat_id_mapped) # assign temporary mapped category name, this name can be changed # during the second pass, since mapped ID can correspond to a category # from a different dataset cat_name_mapped = meta.categories[cat_id_mapped] merged_categories[cat_id_mapped].append( _DatasetCategory( id=cat_id, name=cat_name, mapped_id=cat_id_mapped, mapped_name=cat_name_mapped, dataset_name=dataset_name, ) ) # second pass to assign proper mapped category names for cat_id, categories in merged_categories.items(): for cat in categories: if cat_id in category_names and cat.mapped_name != category_names[cat_id]: cat.mapped_name = category_names[cat_id] return merged_categories def _warn_if_merged_different_categories(merged_categories: _MergedCategoriesT) -> None: logger = logging.getLogger(__name__) for cat_id in merged_categories: merged_categories_i = merged_categories[cat_id] first_cat_name = merged_categories_i[0].name if len(merged_categories_i) > 1 and not all( cat.name == first_cat_name for cat in merged_categories_i[1:] ): cat_summary_str = ", ".join( [f"{cat.id} ({cat.name}) from {cat.dataset_name}" for cat in merged_categories_i] ) logger.warning( f"Merged category {cat_id} corresponds to the following categories: " f"{cat_summary_str}" ) def combine_detection_dataset_dicts( dataset_names: Collection[str], keep_instance_predicate: Optional[InstancePredicate] = None, proposal_files: Optional[Collection[str]] = None, ) -> List[Instance]: """ Load and prepare dataset dicts for training / testing Args: dataset_names (Collection[str]): a list of dataset names keep_instance_predicate (Callable: Dict[str, Any] -> bool): predicate applied to instance dicts which defines whether to keep the instance proposal_files (Collection[str]): if given, a list of object proposal files that match each dataset in `dataset_names`. """ assert len(dataset_names) if proposal_files is None: proposal_files = [None] * len(dataset_names) assert len(dataset_names) == len(proposal_files) # load datasets and metadata dataset_name_to_dicts = {} for dataset_name in dataset_names: dataset_name_to_dicts[dataset_name] = DatasetCatalog.get(dataset_name) assert len(dataset_name_to_dicts), f"Dataset '{dataset_name}' is empty!" # merge categories, requires category metadata to be loaded # cat_id -> [(orig_cat_id, cat_name, dataset_name)] merged_categories = _merge_categories(dataset_names) _warn_if_merged_different_categories(merged_categories) merged_category_names = [ merged_categories[cat_id][0].mapped_name for cat_id in sorted(merged_categories) ] # map to contiguous category IDs _add_category_id_to_contiguous_id_maps_to_metadata(merged_categories) # load annotations and dataset metadata for dataset_name, proposal_file in zip(dataset_names, proposal_files): dataset_dicts = dataset_name_to_dicts[dataset_name] assert len(dataset_dicts), f"Dataset '{dataset_name}' is empty!" if proposal_file is not None: dataset_dicts = load_proposals_into_dataset(dataset_dicts, proposal_file) dataset_dicts = _maybe_filter_and_map_categories(dataset_name, dataset_dicts) print_instances_class_histogram(dataset_dicts, merged_category_names) dataset_name_to_dicts[dataset_name] = dataset_dicts if keep_instance_predicate is not None: all_datasets_dicts_plain = [ d for d in itertools.chain.from_iterable(dataset_name_to_dicts.values()) if keep_instance_predicate(d) ] else: all_datasets_dicts_plain = list( itertools.chain.from_iterable(dataset_name_to_dicts.values()) ) return all_datasets_dicts_plain def build_detection_train_loader(cfg: CfgNode, mapper=None): """ A data loader is created in a way similar to that of Detectron2. The main differences are: - it allows to combine datasets with different but compatible object category sets The data loader is created by the following steps: 1. Use the dataset names in config to query :class:`DatasetCatalog`, and obtain a list of dicts. 2. Start workers to work on the dicts. Each worker will: * Map each metadata dict into another format to be consumed by the model. * Batch them by simply putting dicts into a list. The batched ``list[mapped_dict]`` is what this dataloader will return. Args: cfg (CfgNode): the config mapper (callable): a callable which takes a sample (dict) from dataset and returns the format to be consumed by the model. By default it will be `DatasetMapper(cfg, True)`. Returns: an infinite iterator of training data """ _add_category_whitelists_to_metadata(cfg) _add_category_maps_to_metadata(cfg) _maybe_add_class_to_mesh_name_map_to_metadata(cfg.DATASETS.TRAIN, cfg) dataset_dicts = combine_detection_dataset_dicts( cfg.DATASETS.TRAIN, keep_instance_predicate=_get_train_keep_instance_predicate(cfg), proposal_files=cfg.DATASETS.PROPOSAL_FILES_TRAIN if cfg.MODEL.LOAD_PROPOSALS else None, ) if mapper is None: mapper = DatasetMapper(cfg, True) return d2_build_detection_train_loader(cfg, dataset=dataset_dicts, mapper=mapper) def build_detection_test_loader(cfg, dataset_name, mapper=None): """ Similar to `build_detection_train_loader`. But this function uses the given `dataset_name` argument (instead of the names in cfg), and uses batch size 1. Args: cfg: a detectron2 CfgNode dataset_name (str): a name of the dataset that's available in the DatasetCatalog mapper (callable): a callable which takes a sample (dict) from dataset and returns the format to be consumed by the model. By default it will be `DatasetMapper(cfg, False)`. Returns: DataLoader: a torch DataLoader, that loads the given detection dataset, with test-time transformation and batching. """ _add_category_whitelists_to_metadata(cfg) _add_category_maps_to_metadata(cfg) _maybe_add_class_to_mesh_name_map_to_metadata([dataset_name], cfg) dataset_dicts = combine_detection_dataset_dicts( [dataset_name], keep_instance_predicate=_get_test_keep_instance_predicate(cfg), proposal_files=[ cfg.DATASETS.PROPOSAL_FILES_TEST[list(cfg.DATASETS.TEST).index(dataset_name)] ] if cfg.MODEL.LOAD_PROPOSALS else None, ) sampler = None if not cfg.DENSEPOSE_EVALUATION.DISTRIBUTED_INFERENCE: sampler = torch.utils.data.SequentialSampler(dataset_dicts) if mapper is None: mapper = DatasetMapper(cfg, False) return d2_build_detection_test_loader( dataset_dicts, mapper=mapper, num_workers=cfg.DATALOADER.NUM_WORKERS, sampler=sampler ) def build_frame_selector(cfg: CfgNode): strategy = FrameSelectionStrategy(cfg.STRATEGY) if strategy == FrameSelectionStrategy.RANDOM_K: frame_selector = RandomKFramesSelector(cfg.NUM_IMAGES) elif strategy == FrameSelectionStrategy.FIRST_K: frame_selector = FirstKFramesSelector(cfg.NUM_IMAGES) elif strategy == FrameSelectionStrategy.LAST_K: frame_selector = LastKFramesSelector(cfg.NUM_IMAGES) elif strategy == FrameSelectionStrategy.ALL: frame_selector = None # pyre-fixme[61]: `frame_selector` may not be initialized here. return frame_selector def build_transform(cfg: CfgNode, data_type: str): if cfg.TYPE == "resize": if data_type == "image": return ImageResizeTransform(cfg.MIN_SIZE, cfg.MAX_SIZE) raise ValueError(f"Unknown transform {cfg.TYPE} for data type {data_type}") def build_combined_loader(cfg: CfgNode, loaders: Collection[Loader], ratios: Sequence[float]): images_per_worker = _compute_num_images_per_worker(cfg) return CombinedDataLoader(loaders, images_per_worker, ratios) def build_bootstrap_dataset(dataset_name: str, cfg: CfgNode) -> Sequence[torch.Tensor]: """ Build dataset that provides data to bootstrap on Args: dataset_name (str): Name of the dataset, needs to have associated metadata to load the data cfg (CfgNode): bootstrapping config Returns: Sequence[Tensor] - dataset that provides image batches, Tensors of size [N, C, H, W] of type float32 """ logger = logging.getLogger(__name__) _add_category_info_to_bootstrapping_metadata(dataset_name, cfg) meta = MetadataCatalog.get(dataset_name) factory = BootstrapDatasetFactoryCatalog.get(meta.dataset_type) dataset = None if factory is not None: dataset = factory(meta, cfg) if dataset is None: logger.warning(f"Failed to create dataset {dataset_name} of type {meta.dataset_type}") return dataset def build_data_sampler(cfg: CfgNode, sampler_cfg: CfgNode, embedder: Optional[torch.nn.Module]): if sampler_cfg.TYPE == "densepose_uniform": data_sampler = PredictionToGroundTruthSampler() # transform densepose pred -> gt data_sampler.register_sampler( "pred_densepose", "gt_densepose", DensePoseUniformSampler(count_per_class=sampler_cfg.COUNT_PER_CLASS), ) data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler()) return data_sampler elif sampler_cfg.TYPE == "densepose_UV_confidence": data_sampler = PredictionToGroundTruthSampler() # transform densepose pred -> gt data_sampler.register_sampler( "pred_densepose", "gt_densepose", DensePoseConfidenceBasedSampler( confidence_channel="sigma_2", count_per_class=sampler_cfg.COUNT_PER_CLASS, search_proportion=0.5, ), ) data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler()) return data_sampler elif sampler_cfg.TYPE == "densepose_fine_segm_confidence": data_sampler = PredictionToGroundTruthSampler() # transform densepose pred -> gt data_sampler.register_sampler( "pred_densepose", "gt_densepose", DensePoseConfidenceBasedSampler( confidence_channel="fine_segm_confidence", count_per_class=sampler_cfg.COUNT_PER_CLASS, search_proportion=0.5, ), ) data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler()) return data_sampler elif sampler_cfg.TYPE == "densepose_coarse_segm_confidence": data_sampler = PredictionToGroundTruthSampler() # transform densepose pred -> gt data_sampler.register_sampler( "pred_densepose", "gt_densepose", DensePoseConfidenceBasedSampler( confidence_channel="coarse_segm_confidence", count_per_class=sampler_cfg.COUNT_PER_CLASS, search_proportion=0.5, ), ) data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler()) return data_sampler elif sampler_cfg.TYPE == "densepose_cse_uniform": assert embedder is not None data_sampler = PredictionToGroundTruthSampler() # transform densepose pred -> gt data_sampler.register_sampler( "pred_densepose", "gt_densepose", DensePoseCSEUniformSampler( cfg=cfg, use_gt_categories=sampler_cfg.USE_GROUND_TRUTH_CATEGORIES, embedder=embedder, count_per_class=sampler_cfg.COUNT_PER_CLASS, ), ) data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler()) return data_sampler elif sampler_cfg.TYPE == "densepose_cse_coarse_segm_confidence": assert embedder is not None data_sampler = PredictionToGroundTruthSampler() # transform densepose pred -> gt data_sampler.register_sampler( "pred_densepose", "gt_densepose", DensePoseCSEConfidenceBasedSampler( cfg=cfg, use_gt_categories=sampler_cfg.USE_GROUND_TRUTH_CATEGORIES, embedder=embedder, confidence_channel="coarse_segm_confidence", count_per_class=sampler_cfg.COUNT_PER_CLASS, search_proportion=0.5, ), ) data_sampler.register_sampler("pred_densepose", "gt_masks", MaskFromDensePoseSampler()) return data_sampler raise ValueError(f"Unknown data sampler type {sampler_cfg.TYPE}") def build_data_filter(cfg: CfgNode): if cfg.TYPE == "detection_score": min_score = cfg.MIN_VALUE return ScoreBasedFilter(min_score=min_score) raise ValueError(f"Unknown data filter type {cfg.TYPE}") def build_inference_based_loader( cfg: CfgNode, dataset_cfg: CfgNode, model: torch.nn.Module, embedder: Optional[torch.nn.Module] = None, ) -> InferenceBasedLoader: """ Constructs data loader based on inference results of a model. """ dataset = build_bootstrap_dataset(dataset_cfg.DATASET, dataset_cfg.IMAGE_LOADER) meta = MetadataCatalog.get(dataset_cfg.DATASET) training_sampler = TrainingSampler(len(dataset)) data_loader = torch.utils.data.DataLoader( dataset, # pyre-ignore[6] batch_size=dataset_cfg.IMAGE_LOADER.BATCH_SIZE, sampler=training_sampler, num_workers=dataset_cfg.IMAGE_LOADER.NUM_WORKERS, collate_fn=trivial_batch_collator, worker_init_fn=worker_init_reset_seed, ) return InferenceBasedLoader( model, data_loader=data_loader, data_sampler=build_data_sampler(cfg, dataset_cfg.DATA_SAMPLER, embedder), data_filter=build_data_filter(dataset_cfg.FILTER), shuffle=True, batch_size=dataset_cfg.INFERENCE.OUTPUT_BATCH_SIZE, inference_batch_size=dataset_cfg.INFERENCE.INPUT_BATCH_SIZE, category_to_class_mapping=meta.category_to_class_mapping, ) def has_inference_based_loaders(cfg: CfgNode) -> bool: """ Returns True, if at least one inferense-based loader must be instantiated for training """ return len(cfg.BOOTSTRAP_DATASETS) > 0 def build_inference_based_loaders( cfg: CfgNode, model: torch.nn.Module ) -> Tuple[List[InferenceBasedLoader], List[float]]: loaders = [] ratios = [] embedder = build_densepose_embedder(cfg).to(device=model.device) # pyre-ignore[16] for dataset_spec in cfg.BOOTSTRAP_DATASETS: dataset_cfg = get_bootstrap_dataset_config().clone() dataset_cfg.merge_from_other_cfg(CfgNode(dataset_spec)) loader = build_inference_based_loader(cfg, dataset_cfg, model, embedder) loaders.append(loader) ratios.append(dataset_cfg.RATIO) return loaders, ratios def build_video_list_dataset(meta: Metadata, cfg: CfgNode): video_list_fpath = meta.video_list_fpath video_base_path = meta.video_base_path category = meta.category if cfg.TYPE == "video_keyframe": frame_selector = build_frame_selector(cfg.SELECT) transform = build_transform(cfg.TRANSFORM, data_type="image") video_list = video_list_from_file(video_list_fpath, video_base_path) keyframe_helper_fpath = cfg.KEYFRAME_HELPER if hasattr(cfg, "KEYFRAME_HELPER") else None return VideoKeyframeDataset( video_list, category, frame_selector, transform, keyframe_helper_fpath ) class _BootstrapDatasetFactoryCatalog(UserDict): """ A global dictionary that stores information about bootstrapped datasets creation functions from metadata and config, for diverse DatasetType """ def register(self, dataset_type: DatasetType, factory: Callable[[Metadata, CfgNode], Dataset]): """ Args: dataset_type (DatasetType): a DatasetType e.g. DatasetType.VIDEO_LIST factory (Callable[Metadata, CfgNode]): a callable which takes Metadata and cfg arguments and returns a dataset object. """ assert dataset_type not in self, "Dataset '{}' is already registered!".format(dataset_type) self[dataset_type] = factory BootstrapDatasetFactoryCatalog = _BootstrapDatasetFactoryCatalog() BootstrapDatasetFactoryCatalog.register(DatasetType.VIDEO_LIST, build_video_list_dataset) ================================================ FILE: detectron2/projects/DensePose/densepose/data/combined_loader.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import random from collections import deque from typing import Any, Collection, Deque, Iterable, Iterator, List, Sequence Loader = Iterable[Any] def _pooled_next(iterator: Iterator[Any], pool: Deque[Any]): if not pool: pool.extend(next(iterator)) return pool.popleft() class CombinedDataLoader: """ Combines data loaders using the provided sampling ratios """ BATCH_COUNT = 100 def __init__(self, loaders: Collection[Loader], batch_size: int, ratios: Sequence[float]): self.loaders = loaders self.batch_size = batch_size self.ratios = ratios def __iter__(self) -> Iterator[List[Any]]: iters = [iter(loader) for loader in self.loaders] indices = [] pool = [deque()] * len(iters) # infinite iterator, as in D2 while True: if not indices: # just a buffer of indices, its size doesn't matter # as long as it's a multiple of batch_size k = self.batch_size * self.BATCH_COUNT indices = random.choices(range(len(self.loaders)), self.ratios, k=k) try: batch = [_pooled_next(iters[i], pool[i]) for i in indices[: self.batch_size]] except StopIteration: break indices = indices[self.batch_size :] yield batch ================================================ FILE: detectron2/projects/DensePose/densepose/data/dataset_mapper.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import copy import logging from typing import Any, Dict, List, Tuple import torch from detectron2.data import MetadataCatalog from detectron2.data import detection_utils as utils from detectron2.data import transforms as T from detectron2.layers import ROIAlign from detectron2.structures import BoxMode from detectron2.utils.file_io import PathManager from densepose.structures import DensePoseDataRelative, DensePoseList, DensePoseTransformData def build_augmentation(cfg, is_train): logger = logging.getLogger(__name__) result = utils.build_augmentation(cfg, is_train) if is_train: random_rotation = T.RandomRotation( cfg.INPUT.ROTATION_ANGLES, expand=False, sample_style="choice" ) result.append(random_rotation) logger.info("DensePose-specific augmentation used in training: " + str(random_rotation)) return result class DatasetMapper: """ A customized version of `detectron2.data.DatasetMapper` """ def __init__(self, cfg, is_train=True): self.augmentation = build_augmentation(cfg, is_train) # fmt: off self.img_format = cfg.INPUT.FORMAT self.mask_on = ( cfg.MODEL.MASK_ON or ( cfg.MODEL.DENSEPOSE_ON and cfg.MODEL.ROI_DENSEPOSE_HEAD.COARSE_SEGM_TRAINED_BY_MASKS) ) self.keypoint_on = cfg.MODEL.KEYPOINT_ON self.densepose_on = cfg.MODEL.DENSEPOSE_ON assert not cfg.MODEL.LOAD_PROPOSALS, "not supported yet" # fmt: on if self.keypoint_on and is_train: # Flip only makes sense in training self.keypoint_hflip_indices = utils.create_keypoint_hflip_indices(cfg.DATASETS.TRAIN) else: self.keypoint_hflip_indices = None if self.densepose_on: densepose_transform_srcs = [ MetadataCatalog.get(ds).densepose_transform_src for ds in cfg.DATASETS.TRAIN + cfg.DATASETS.TEST ] assert len(densepose_transform_srcs) > 0 # TODO: check that DensePose transformation data is the same for # all the datasets. Otherwise one would have to pass DB ID with # each entry to select proper transformation data. For now, since # all DensePose annotated data uses the same data semantics, we # omit this check. densepose_transform_data_fpath = PathManager.get_local_path(densepose_transform_srcs[0]) self.densepose_transform_data = DensePoseTransformData.load( densepose_transform_data_fpath ) self.is_train = is_train def __call__(self, dataset_dict): """ Args: dataset_dict (dict): Metadata of one image, in Detectron2 Dataset format. Returns: dict: a format that builtin models in detectron2 accept """ dataset_dict = copy.deepcopy(dataset_dict) # it will be modified by code below image = utils.read_image(dataset_dict["file_name"], format=self.img_format) utils.check_image_size(dataset_dict, image) image, transforms = T.apply_transform_gens(self.augmentation, image) image_shape = image.shape[:2] # h, w dataset_dict["image"] = torch.as_tensor(image.transpose(2, 0, 1).astype("float32")) if not self.is_train: dataset_dict.pop("annotations", None) return dataset_dict for anno in dataset_dict["annotations"]: if not self.mask_on: anno.pop("segmentation", None) if not self.keypoint_on: anno.pop("keypoints", None) # USER: Implement additional transformations if you have other types of data # USER: Don't call transpose_densepose if you don't need annos = [ self._transform_densepose( utils.transform_instance_annotations( obj, transforms, image_shape, keypoint_hflip_indices=self.keypoint_hflip_indices ), transforms, ) for obj in dataset_dict.pop("annotations") if obj.get("iscrowd", 0) == 0 ] if self.mask_on: self._add_densepose_masks_as_segmentation(annos, image_shape) instances = utils.annotations_to_instances(annos, image_shape, mask_format="bitmask") densepose_annotations = [obj.get("densepose") for obj in annos] if densepose_annotations and not all(v is None for v in densepose_annotations): instances.gt_densepose = DensePoseList( densepose_annotations, instances.gt_boxes, image_shape ) dataset_dict["instances"] = instances[instances.gt_boxes.nonempty()] return dataset_dict def _transform_densepose(self, annotation, transforms): if not self.densepose_on: return annotation # Handle densepose annotations is_valid, reason_not_valid = DensePoseDataRelative.validate_annotation(annotation) if is_valid: densepose_data = DensePoseDataRelative(annotation, cleanup=True) densepose_data.apply_transform(transforms, self.densepose_transform_data) annotation["densepose"] = densepose_data else: # logger = logging.getLogger(__name__) # logger.debug("Could not load DensePose annotation: {}".format(reason_not_valid)) DensePoseDataRelative.cleanup_annotation(annotation) # NOTE: annotations for certain instances may be unavailable. # 'None' is accepted by the DensePostList data structure. annotation["densepose"] = None return annotation def _add_densepose_masks_as_segmentation( self, annotations: List[Dict[str, Any]], image_shape_hw: Tuple[int, int] ): for obj in annotations: if ("densepose" not in obj) or ("segmentation" in obj): continue # DP segmentation: torch.Tensor [S, S] of float32, S=256 segm_dp = torch.zeros_like(obj["densepose"].segm) segm_dp[obj["densepose"].segm > 0] = 1 segm_h, segm_w = segm_dp.shape bbox_segm_dp = torch.tensor((0, 0, segm_h - 1, segm_w - 1), dtype=torch.float32) # image bbox x0, y0, x1, y1 = ( v.item() for v in BoxMode.convert(obj["bbox"], obj["bbox_mode"], BoxMode.XYXY_ABS) ) segm_aligned = ( ROIAlign((y1 - y0, x1 - x0), 1.0, 0, aligned=True) .forward(segm_dp.view(1, 1, *segm_dp.shape), bbox_segm_dp) .squeeze() ) image_mask = torch.zeros(*image_shape_hw, dtype=torch.float32) image_mask[y0:y1, x0:x1] = segm_aligned # segmentation for BitMask: np.array [H, W] of np.bool obj["segmentation"] = image_mask >= 0.5 ================================================ FILE: detectron2/projects/DensePose/densepose/data/datasets/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from . import builtin # ensure the builtin datasets are registered __all__ = [k for k in globals().keys() if "builtin" not in k and not k.startswith("_")] ================================================ FILE: detectron2/projects/DensePose/densepose/data/datasets/builtin.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .chimpnsee import register_dataset as register_chimpnsee_dataset from .coco import BASE_DATASETS as BASE_COCO_DATASETS from .coco import DATASETS as COCO_DATASETS from .coco import register_datasets as register_coco_datasets from .lvis import DATASETS as LVIS_DATASETS from .lvis import register_datasets as register_lvis_datasets DEFAULT_DATASETS_ROOT = "datasets" register_coco_datasets(COCO_DATASETS, DEFAULT_DATASETS_ROOT) register_coco_datasets(BASE_COCO_DATASETS, DEFAULT_DATASETS_ROOT) register_lvis_datasets(LVIS_DATASETS, DEFAULT_DATASETS_ROOT) register_chimpnsee_dataset(DEFAULT_DATASETS_ROOT) # pyre-ignore[19] ================================================ FILE: detectron2/projects/DensePose/densepose/data/datasets/chimpnsee.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Optional from detectron2.data import DatasetCatalog, MetadataCatalog from ..utils import maybe_prepend_base_path from .dataset_type import DatasetType CHIMPNSEE_DATASET_NAME = "chimpnsee" def register_dataset(datasets_root: Optional[str] = None) -> None: def empty_load_callback(): pass video_list_fpath = maybe_prepend_base_path( datasets_root, "chimpnsee/cdna.eva.mpg.de/video_list.txt", ) video_base_path = maybe_prepend_base_path(datasets_root, "chimpnsee/cdna.eva.mpg.de") DatasetCatalog.register(CHIMPNSEE_DATASET_NAME, empty_load_callback) MetadataCatalog.get(CHIMPNSEE_DATASET_NAME).set( dataset_type=DatasetType.VIDEO_LIST, video_list_fpath=video_list_fpath, video_base_path=video_base_path, category="chimpanzee", ) ================================================ FILE: detectron2/projects/DensePose/densepose/data/datasets/coco.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import contextlib import io import logging import os from collections import defaultdict from dataclasses import dataclass from typing import Any, Dict, Iterable, List, Optional from fvcore.common.timer import Timer from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.structures import BoxMode from detectron2.utils.file_io import PathManager from ..utils import maybe_prepend_base_path DENSEPOSE_MASK_KEY = "dp_masks" DENSEPOSE_IUV_KEYS_WITHOUT_MASK = ["dp_x", "dp_y", "dp_I", "dp_U", "dp_V"] DENSEPOSE_CSE_KEYS_WITHOUT_MASK = ["dp_x", "dp_y", "dp_vertex", "ref_model"] DENSEPOSE_ALL_POSSIBLE_KEYS = set( DENSEPOSE_IUV_KEYS_WITHOUT_MASK + DENSEPOSE_CSE_KEYS_WITHOUT_MASK + [DENSEPOSE_MASK_KEY] ) DENSEPOSE_METADATA_URL_PREFIX = "https://dl.fbaipublicfiles.com/densepose/data/" @dataclass class CocoDatasetInfo: name: str images_root: str annotations_fpath: str DATASETS = [ CocoDatasetInfo( name="densepose_coco_2014_train", images_root="coco/train2014", annotations_fpath="coco/annotations/densepose_train2014.json", ), CocoDatasetInfo( name="densepose_coco_2014_minival", images_root="coco/val2014", annotations_fpath="coco/annotations/densepose_minival2014.json", ), CocoDatasetInfo( name="densepose_coco_2014_minival_100", images_root="coco/val2014", annotations_fpath="coco/annotations/densepose_minival2014_100.json", ), CocoDatasetInfo( name="densepose_coco_2014_valminusminival", images_root="coco/val2014", annotations_fpath="coco/annotations/densepose_valminusminival2014.json", ), CocoDatasetInfo( name="densepose_coco_2014_train_cse", images_root="coco/train2014", annotations_fpath="coco_cse/densepose_train2014_cse.json", ), CocoDatasetInfo( name="densepose_coco_2014_minival_cse", images_root="coco/val2014", annotations_fpath="coco_cse/densepose_minival2014_cse.json", ), CocoDatasetInfo( name="densepose_coco_2014_minival_100_cse", images_root="coco/val2014", annotations_fpath="coco_cse/densepose_minival2014_100_cse.json", ), CocoDatasetInfo( name="densepose_coco_2014_valminusminival_cse", images_root="coco/val2014", annotations_fpath="coco_cse/densepose_valminusminival2014_cse.json", ), CocoDatasetInfo( name="densepose_chimps", images_root="densepose_chimps/images", annotations_fpath="densepose_chimps/densepose_chimps_densepose.json", ), CocoDatasetInfo( name="densepose_chimps_cse_train", images_root="densepose_chimps/images", annotations_fpath="densepose_chimps/densepose_chimps_cse_train.json", ), CocoDatasetInfo( name="densepose_chimps_cse_val", images_root="densepose_chimps/images", annotations_fpath="densepose_chimps/densepose_chimps_cse_val.json", ), CocoDatasetInfo( name="posetrack2017_train", images_root="posetrack2017/posetrack_data_2017", annotations_fpath="posetrack2017/densepose_posetrack_train2017.json", ), CocoDatasetInfo( name="posetrack2017_val", images_root="posetrack2017/posetrack_data_2017", annotations_fpath="posetrack2017/densepose_posetrack_val2017.json", ), CocoDatasetInfo( name="lvis_v05_train", images_root="coco/train2017", annotations_fpath="lvis/lvis_v0.5_plus_dp_train.json", ), CocoDatasetInfo( name="lvis_v05_val", images_root="coco/val2017", annotations_fpath="lvis/lvis_v0.5_plus_dp_val.json", ), ] BASE_DATASETS = [ CocoDatasetInfo( name="base_coco_2017_train", images_root="coco/train2017", annotations_fpath="coco/annotations/instances_train2017.json", ), CocoDatasetInfo( name="base_coco_2017_val", images_root="coco/val2017", annotations_fpath="coco/annotations/instances_val2017.json", ), CocoDatasetInfo( name="base_coco_2017_val_100", images_root="coco/val2017", annotations_fpath="coco/annotations/instances_val2017_100.json", ), ] def get_metadata(base_path: Optional[str]) -> Dict[str, Any]: """ Returns metadata associated with COCO DensePose datasets Args: base_path: Optional[str] Base path used to load metadata from Returns: Dict[str, Any] Metadata in the form of a dictionary """ meta = { "densepose_transform_src": maybe_prepend_base_path(base_path, "UV_symmetry_transforms.mat"), "densepose_smpl_subdiv": maybe_prepend_base_path(base_path, "SMPL_subdiv.mat"), "densepose_smpl_subdiv_transform": maybe_prepend_base_path( base_path, "SMPL_SUBDIV_TRANSFORM.mat", ), } return meta def _load_coco_annotations(json_file: str): """ Load COCO annotations from a JSON file Args: json_file: str Path to the file to load annotations from Returns: Instance of `pycocotools.coco.COCO` that provides access to annotations data """ from pycocotools.coco import COCO logger = logging.getLogger(__name__) timer = Timer() with contextlib.redirect_stdout(io.StringIO()): coco_api = COCO(json_file) if timer.seconds() > 1: logger.info("Loading {} takes {:.2f} seconds.".format(json_file, timer.seconds())) return coco_api def _add_categories_metadata(dataset_name: str, categories: List[Dict[str, Any]]): meta = MetadataCatalog.get(dataset_name) meta.categories = {c["id"]: c["name"] for c in categories} logger = logging.getLogger(__name__) logger.info("Dataset {} categories: {}".format(dataset_name, meta.categories)) def _verify_annotations_have_unique_ids(json_file: str, anns: List[List[Dict[str, Any]]]): if "minival" in json_file: # Skip validation on COCO2014 valminusminival and minival annotations # The ratio of buggy annotations there is tiny and does not affect accuracy # Therefore we explicitly white-list them return ann_ids = [ann["id"] for anns_per_image in anns for ann in anns_per_image] assert len(set(ann_ids)) == len(ann_ids), "Annotation ids in '{}' are not unique!".format( json_file ) def _maybe_add_bbox(obj: Dict[str, Any], ann_dict: Dict[str, Any]): if "bbox" not in ann_dict: return obj["bbox"] = ann_dict["bbox"] obj["bbox_mode"] = BoxMode.XYWH_ABS def _maybe_add_segm(obj: Dict[str, Any], ann_dict: Dict[str, Any]): if "segmentation" not in ann_dict: return segm = ann_dict["segmentation"] if not isinstance(segm, dict): # filter out invalid polygons (< 3 points) segm = [poly for poly in segm if len(poly) % 2 == 0 and len(poly) >= 6] if len(segm) == 0: return obj["segmentation"] = segm def _maybe_add_keypoints(obj: Dict[str, Any], ann_dict: Dict[str, Any]): if "keypoints" not in ann_dict: return keypts = ann_dict["keypoints"] # list[int] for idx, v in enumerate(keypts): if idx % 3 != 2: # COCO's segmentation coordinates are floating points in [0, H or W], # but keypoint coordinates are integers in [0, H-1 or W-1] # Therefore we assume the coordinates are "pixel indices" and # add 0.5 to convert to floating point coordinates. keypts[idx] = v + 0.5 obj["keypoints"] = keypts def _maybe_add_densepose(obj: Dict[str, Any], ann_dict: Dict[str, Any]): for key in DENSEPOSE_ALL_POSSIBLE_KEYS: if key in ann_dict: obj[key] = ann_dict[key] def _combine_images_with_annotations( dataset_name: str, image_root: str, img_datas: Iterable[Dict[str, Any]], ann_datas: Iterable[Iterable[Dict[str, Any]]], ): ann_keys = ["iscrowd", "category_id"] dataset_dicts = [] contains_video_frame_info = False for img_dict, ann_dicts in zip(img_datas, ann_datas): record = {} record["file_name"] = os.path.join(image_root, img_dict["file_name"]) record["height"] = img_dict["height"] record["width"] = img_dict["width"] record["image_id"] = img_dict["id"] record["dataset"] = dataset_name if "frame_id" in img_dict: record["frame_id"] = img_dict["frame_id"] record["video_id"] = img_dict.get("vid_id", None) contains_video_frame_info = True objs = [] for ann_dict in ann_dicts: assert ann_dict["image_id"] == record["image_id"] assert ann_dict.get("ignore", 0) == 0 obj = {key: ann_dict[key] for key in ann_keys if key in ann_dict} _maybe_add_bbox(obj, ann_dict) _maybe_add_segm(obj, ann_dict) _maybe_add_keypoints(obj, ann_dict) _maybe_add_densepose(obj, ann_dict) objs.append(obj) record["annotations"] = objs dataset_dicts.append(record) if contains_video_frame_info: create_video_frame_mapping(dataset_name, dataset_dicts) return dataset_dicts def get_contiguous_id_to_category_id_map(metadata): cat_id_2_cont_id = metadata.thing_dataset_id_to_contiguous_id cont_id_2_cat_id = {} for cat_id, cont_id in cat_id_2_cont_id.items(): if cont_id in cont_id_2_cat_id: continue cont_id_2_cat_id[cont_id] = cat_id return cont_id_2_cat_id def maybe_filter_categories_cocoapi(dataset_name, coco_api): meta = MetadataCatalog.get(dataset_name) cont_id_2_cat_id = get_contiguous_id_to_category_id_map(meta) cat_id_2_cont_id = meta.thing_dataset_id_to_contiguous_id # filter categories cats = [] for cat in coco_api.dataset["categories"]: cat_id = cat["id"] if cat_id not in cat_id_2_cont_id: continue cont_id = cat_id_2_cont_id[cat_id] if (cont_id in cont_id_2_cat_id) and (cont_id_2_cat_id[cont_id] == cat_id): cats.append(cat) coco_api.dataset["categories"] = cats # filter annotations, if multiple categories are mapped to a single # contiguous ID, use only one category ID and map all annotations to that category ID anns = [] for ann in coco_api.dataset["annotations"]: cat_id = ann["category_id"] if cat_id not in cat_id_2_cont_id: continue cont_id = cat_id_2_cont_id[cat_id] ann["category_id"] = cont_id_2_cat_id[cont_id] anns.append(ann) coco_api.dataset["annotations"] = anns # recreate index coco_api.createIndex() def maybe_filter_and_map_categories_cocoapi(dataset_name, coco_api): meta = MetadataCatalog.get(dataset_name) category_id_map = meta.thing_dataset_id_to_contiguous_id # map categories cats = [] for cat in coco_api.dataset["categories"]: cat_id = cat["id"] if cat_id not in category_id_map: continue cat["id"] = category_id_map[cat_id] cats.append(cat) coco_api.dataset["categories"] = cats # map annotation categories anns = [] for ann in coco_api.dataset["annotations"]: cat_id = ann["category_id"] if cat_id not in category_id_map: continue ann["category_id"] = category_id_map[cat_id] anns.append(ann) coco_api.dataset["annotations"] = anns # recreate index coco_api.createIndex() def create_video_frame_mapping(dataset_name, dataset_dicts): mapping = defaultdict(dict) for d in dataset_dicts: video_id = d.get("video_id") if video_id is None: continue mapping[video_id].update({d["frame_id"]: d["file_name"]}) MetadataCatalog.get(dataset_name).set(video_frame_mapping=mapping) def load_coco_json(annotations_json_file: str, image_root: str, dataset_name: str): """ Loads a JSON file with annotations in COCO instances format. Replaces `detectron2.data.datasets.coco.load_coco_json` to handle metadata in a more flexible way. Postpones category mapping to a later stage to be able to combine several datasets with different (but coherent) sets of categories. Args: annotations_json_file: str Path to the JSON file with annotations in COCO instances format. image_root: str directory that contains all the images dataset_name: str the name that identifies a dataset, e.g. "densepose_coco_2014_train" extra_annotation_keys: Optional[List[str]] If provided, these keys are used to extract additional data from the annotations. """ coco_api = _load_coco_annotations(PathManager.get_local_path(annotations_json_file)) _add_categories_metadata(dataset_name, coco_api.loadCats(coco_api.getCatIds())) # sort indices for reproducible results img_ids = sorted(coco_api.imgs.keys()) # imgs is a list of dicts, each looks something like: # {'license': 4, # 'url': 'http://farm6.staticflickr.com/5454/9413846304_881d5e5c3b_z.jpg', # 'file_name': 'COCO_val2014_000000001268.jpg', # 'height': 427, # 'width': 640, # 'date_captured': '2013-11-17 05:57:24', # 'id': 1268} imgs = coco_api.loadImgs(img_ids) logger = logging.getLogger(__name__) logger.info("Loaded {} images in COCO format from {}".format(len(imgs), annotations_json_file)) # anns is a list[list[dict]], where each dict is an annotation # record for an object. The inner list enumerates the objects in an image # and the outer list enumerates over images. anns = [coco_api.imgToAnns[img_id] for img_id in img_ids] _verify_annotations_have_unique_ids(annotations_json_file, anns) dataset_records = _combine_images_with_annotations(dataset_name, image_root, imgs, anns) return dataset_records def register_dataset(dataset_data: CocoDatasetInfo, datasets_root: Optional[str] = None): """ Registers provided COCO DensePose dataset Args: dataset_data: CocoDatasetInfo Dataset data datasets_root: Optional[str] Datasets root folder (default: None) """ annotations_fpath = maybe_prepend_base_path(datasets_root, dataset_data.annotations_fpath) images_root = maybe_prepend_base_path(datasets_root, dataset_data.images_root) def load_annotations(): return load_coco_json( annotations_json_file=annotations_fpath, image_root=images_root, dataset_name=dataset_data.name, ) DatasetCatalog.register(dataset_data.name, load_annotations) MetadataCatalog.get(dataset_data.name).set( json_file=annotations_fpath, image_root=images_root, **get_metadata(DENSEPOSE_METADATA_URL_PREFIX) ) def register_datasets( datasets_data: Iterable[CocoDatasetInfo], datasets_root: Optional[str] = None ): """ Registers provided COCO DensePose datasets Args: datasets_data: Iterable[CocoDatasetInfo] An iterable of dataset datas datasets_root: Optional[str] Datasets root folder (default: None) """ for dataset_data in datasets_data: register_dataset(dataset_data, datasets_root) ================================================ FILE: detectron2/projects/DensePose/densepose/data/datasets/dataset_type.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from enum import Enum class DatasetType(Enum): """ Dataset type, mostly used for datasets that contain data to bootstrap models on """ VIDEO_LIST = "video_list" ================================================ FILE: detectron2/projects/DensePose/densepose/data/datasets/lvis.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import os from typing import Any, Dict, Iterable, List, Optional from fvcore.common.timer import Timer from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.data.datasets.lvis import get_lvis_instances_meta from detectron2.structures import BoxMode from detectron2.utils.file_io import PathManager from ..utils import maybe_prepend_base_path from .coco import ( DENSEPOSE_ALL_POSSIBLE_KEYS, DENSEPOSE_METADATA_URL_PREFIX, CocoDatasetInfo, get_metadata, ) DATASETS = [ CocoDatasetInfo( name="densepose_lvis_v1_ds1_train_v1", images_root="coco_", annotations_fpath="lvis/densepose_lvis_v1_ds1_train_v1.json", ), CocoDatasetInfo( name="densepose_lvis_v1_ds1_val_v1", images_root="coco_", annotations_fpath="lvis/densepose_lvis_v1_ds1_val_v1.json", ), CocoDatasetInfo( name="densepose_lvis_v1_ds2_train_v1", images_root="coco_", annotations_fpath="lvis/densepose_lvis_v1_ds2_train_v1.json", ), CocoDatasetInfo( name="densepose_lvis_v1_ds2_val_v1", images_root="coco_", annotations_fpath="lvis/densepose_lvis_v1_ds2_val_v1.json", ), CocoDatasetInfo( name="densepose_lvis_v1_ds1_val_animals_100", images_root="coco_", annotations_fpath="lvis/densepose_lvis_v1_val_animals_100_v2.json", ), ] def _load_lvis_annotations(json_file: str): """ Load COCO annotations from a JSON file Args: json_file: str Path to the file to load annotations from Returns: Instance of `pycocotools.coco.COCO` that provides access to annotations data """ from lvis import LVIS json_file = PathManager.get_local_path(json_file) logger = logging.getLogger(__name__) timer = Timer() lvis_api = LVIS(json_file) if timer.seconds() > 1: logger.info("Loading {} takes {:.2f} seconds.".format(json_file, timer.seconds())) return lvis_api def _add_categories_metadata(dataset_name: str) -> None: metadict = get_lvis_instances_meta(dataset_name) categories = metadict["thing_classes"] metadata = MetadataCatalog.get(dataset_name) metadata.categories = {i + 1: categories[i] for i in range(len(categories))} logger = logging.getLogger(__name__) logger.info(f"Dataset {dataset_name} has {len(categories)} categories") def _verify_annotations_have_unique_ids(json_file: str, anns: List[List[Dict[str, Any]]]) -> None: ann_ids = [ann["id"] for anns_per_image in anns for ann in anns_per_image] assert len(set(ann_ids)) == len(ann_ids), "Annotation ids in '{}' are not unique!".format( json_file ) def _maybe_add_bbox(obj: Dict[str, Any], ann_dict: Dict[str, Any]) -> None: if "bbox" not in ann_dict: return obj["bbox"] = ann_dict["bbox"] obj["bbox_mode"] = BoxMode.XYWH_ABS def _maybe_add_segm(obj: Dict[str, Any], ann_dict: Dict[str, Any]) -> None: if "segmentation" not in ann_dict: return segm = ann_dict["segmentation"] if not isinstance(segm, dict): # filter out invalid polygons (< 3 points) segm = [poly for poly in segm if len(poly) % 2 == 0 and len(poly) >= 6] if len(segm) == 0: return obj["segmentation"] = segm def _maybe_add_keypoints(obj: Dict[str, Any], ann_dict: Dict[str, Any]) -> None: if "keypoints" not in ann_dict: return keypts = ann_dict["keypoints"] # list[int] for idx, v in enumerate(keypts): if idx % 3 != 2: # COCO's segmentation coordinates are floating points in [0, H or W], # but keypoint coordinates are integers in [0, H-1 or W-1] # Therefore we assume the coordinates are "pixel indices" and # add 0.5 to convert to floating point coordinates. keypts[idx] = v + 0.5 obj["keypoints"] = keypts def _maybe_add_densepose(obj: Dict[str, Any], ann_dict: Dict[str, Any]) -> None: for key in DENSEPOSE_ALL_POSSIBLE_KEYS: if key in ann_dict: obj[key] = ann_dict[key] def _combine_images_with_annotations( dataset_name: str, image_root: str, img_datas: Iterable[Dict[str, Any]], ann_datas: Iterable[Iterable[Dict[str, Any]]], ): dataset_dicts = [] def get_file_name(img_root, img_dict): # Determine the path including the split folder ("train2017", "val2017", "test2017") from # the coco_url field. Example: # 'coco_url': 'http://images.cocodataset.org/train2017/000000155379.jpg' split_folder, file_name = img_dict["coco_url"].split("/")[-2:] return os.path.join(img_root + split_folder, file_name) for img_dict, ann_dicts in zip(img_datas, ann_datas): record = {} record["file_name"] = get_file_name(image_root, img_dict) record["height"] = img_dict["height"] record["width"] = img_dict["width"] record["not_exhaustive_category_ids"] = img_dict.get("not_exhaustive_category_ids", []) record["neg_category_ids"] = img_dict.get("neg_category_ids", []) record["image_id"] = img_dict["id"] record["dataset"] = dataset_name objs = [] for ann_dict in ann_dicts: assert ann_dict["image_id"] == record["image_id"] obj = {} _maybe_add_bbox(obj, ann_dict) obj["iscrowd"] = ann_dict.get("iscrowd", 0) obj["category_id"] = ann_dict["category_id"] _maybe_add_segm(obj, ann_dict) _maybe_add_keypoints(obj, ann_dict) _maybe_add_densepose(obj, ann_dict) objs.append(obj) record["annotations"] = objs dataset_dicts.append(record) return dataset_dicts def load_lvis_json(annotations_json_file: str, image_root: str, dataset_name: str): """ Loads a JSON file with annotations in LVIS instances format. Replaces `detectron2.data.datasets.coco.load_lvis_json` to handle metadata in a more flexible way. Postpones category mapping to a later stage to be able to combine several datasets with different (but coherent) sets of categories. Args: annotations_json_file: str Path to the JSON file with annotations in COCO instances format. image_root: str directory that contains all the images dataset_name: str the name that identifies a dataset, e.g. "densepose_coco_2014_train" extra_annotation_keys: Optional[List[str]] If provided, these keys are used to extract additional data from the annotations. """ lvis_api = _load_lvis_annotations(PathManager.get_local_path(annotations_json_file)) _add_categories_metadata(dataset_name) # sort indices for reproducible results img_ids = sorted(lvis_api.imgs.keys()) # imgs is a list of dicts, each looks something like: # {'license': 4, # 'url': 'http://farm6.staticflickr.com/5454/9413846304_881d5e5c3b_z.jpg', # 'file_name': 'COCO_val2014_000000001268.jpg', # 'height': 427, # 'width': 640, # 'date_captured': '2013-11-17 05:57:24', # 'id': 1268} imgs = lvis_api.load_imgs(img_ids) logger = logging.getLogger(__name__) logger.info("Loaded {} images in LVIS format from {}".format(len(imgs), annotations_json_file)) # anns is a list[list[dict]], where each dict is an annotation # record for an object. The inner list enumerates the objects in an image # and the outer list enumerates over images. anns = [lvis_api.img_ann_map[img_id] for img_id in img_ids] _verify_annotations_have_unique_ids(annotations_json_file, anns) dataset_records = _combine_images_with_annotations(dataset_name, image_root, imgs, anns) return dataset_records def register_dataset(dataset_data: CocoDatasetInfo, datasets_root: Optional[str] = None) -> None: """ Registers provided LVIS DensePose dataset Args: dataset_data: CocoDatasetInfo Dataset data datasets_root: Optional[str] Datasets root folder (default: None) """ annotations_fpath = maybe_prepend_base_path(datasets_root, dataset_data.annotations_fpath) images_root = maybe_prepend_base_path(datasets_root, dataset_data.images_root) def load_annotations(): return load_lvis_json( annotations_json_file=annotations_fpath, image_root=images_root, dataset_name=dataset_data.name, ) DatasetCatalog.register(dataset_data.name, load_annotations) MetadataCatalog.get(dataset_data.name).set( json_file=annotations_fpath, image_root=images_root, evaluator_type="lvis", **get_metadata(DENSEPOSE_METADATA_URL_PREFIX), ) def register_datasets( datasets_data: Iterable[CocoDatasetInfo], datasets_root: Optional[str] = None ) -> None: """ Registers provided LVIS DensePose datasets Args: datasets_data: Iterable[CocoDatasetInfo] An iterable of dataset datas datasets_root: Optional[str] Datasets root folder (default: None) """ for dataset_data in datasets_data: register_dataset(dataset_data, datasets_root) ================================================ FILE: detectron2/projects/DensePose/densepose/data/image_list_dataset.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np from typing import Any, Callable, Dict, List, Optional, Union import torch from torch.utils.data.dataset import Dataset from detectron2.data.detection_utils import read_image ImageTransform = Callable[[torch.Tensor], torch.Tensor] class ImageListDataset(Dataset): """ Dataset that provides images from a list. """ _EMPTY_IMAGE = torch.empty((0, 3, 1, 1)) def __init__( self, image_list: List[str], category_list: Union[str, List[str], None] = None, transform: Optional[ImageTransform] = None, ): """ Args: image_list (List[str]): list of paths to image files category_list (Union[str, List[str], None]): list of animal categories for each image. If it is a string, or None, this applies to all images """ if type(category_list) == list: self.category_list = category_list else: self.category_list = [category_list] * len(image_list) assert len(image_list) == len( self.category_list ), "length of image and category lists must be equal" self.image_list = image_list self.transform = transform def __getitem__(self, idx: int) -> Dict[str, Any]: """ Gets selected images from the list Args: idx (int): video index in the video list file Returns: A dictionary containing two keys: images (torch.Tensor): tensor of size [N, 3, H, W] (N = 1, or 0 for _EMPTY_IMAGE) categories (List[str]): categories of the frames """ categories = [self.category_list[idx]] fpath = self.image_list[idx] transform = self.transform try: image = torch.from_numpy(np.ascontiguousarray(read_image(fpath, format="BGR"))) image = image.permute(2, 0, 1).unsqueeze(0).float() # HWC -> NCHW if transform is not None: image = transform(image) return {"images": image, "categories": categories} except (OSError, RuntimeError) as e: logger = logging.getLogger(__name__) logger.warning(f"Error opening image file container {fpath}: {e}") return {"images": self._EMPTY_IMAGE, "categories": []} def __len__(self): return len(self.image_list) ================================================ FILE: detectron2/projects/DensePose/densepose/data/inference_based_loader.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import random from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple import torch from torch import nn SampledData = Any ModelOutput = Any def _grouper(iterable: Iterable[Any], n: int, fillvalue=None) -> Iterator[Tuple[Any]]: """ Group elements of an iterable by chunks of size `n`, e.g. grouper(range(9), 4) -> (0, 1, 2, 3), (4, 5, 6, 7), (8, None, None, None) """ it = iter(iterable) while True: values = [] for _ in range(n): try: value = next(it) except StopIteration: if values: values.extend([fillvalue] * (n - len(values))) yield tuple(values) return values.append(value) yield tuple(values) class ScoreBasedFilter: """ Filters entries in model output based on their scores Discards all entries with score less than the specified minimum """ def __init__(self, min_score: float = 0.8): self.min_score = min_score def __call__(self, model_output: ModelOutput) -> ModelOutput: for model_output_i in model_output: instances = model_output_i["instances"] if not instances.has("scores"): continue instances_filtered = instances[instances.scores >= self.min_score] model_output_i["instances"] = instances_filtered return model_output class InferenceBasedLoader: """ Data loader based on results inferred by a model. Consists of: - a data loader that provides batches of images - a model that is used to infer the results - a data sampler that converts inferred results to annotations """ def __init__( self, model: nn.Module, data_loader: Iterable[List[Dict[str, Any]]], data_sampler: Optional[Callable[[ModelOutput], List[SampledData]]] = None, data_filter: Optional[Callable[[ModelOutput], ModelOutput]] = None, shuffle: bool = True, batch_size: int = 4, inference_batch_size: int = 4, drop_last: bool = False, category_to_class_mapping: Optional[dict] = None, ): """ Constructor Args: model (torch.nn.Module): model used to produce data data_loader (Iterable[List[Dict[str, Any]]]): iterable that provides dictionaries with "images" and "categories" fields to perform inference on data_sampler (Callable: ModelOutput -> SampledData): functor that produces annotation data from inference results; (optional, default: None) data_filter (Callable: ModelOutput -> ModelOutput): filter that selects model outputs for further processing (optional, default: None) shuffle (bool): if True, the input images get shuffled batch_size (int): batch size for the produced annotation data inference_batch_size (int): batch size for input images drop_last (bool): if True, drop the last batch if it is undersized category_to_class_mapping (dict): category to class mapping """ self.model = model self.model.eval() self.data_loader = data_loader self.data_sampler = data_sampler self.data_filter = data_filter self.shuffle = shuffle self.batch_size = batch_size self.inference_batch_size = inference_batch_size self.drop_last = drop_last if category_to_class_mapping is not None: self.category_to_class_mapping = category_to_class_mapping else: self.category_to_class_mapping = {} def __iter__(self) -> Iterator[List[SampledData]]: for batch in self.data_loader: # batch : List[Dict[str: Tensor[N, C, H, W], str: Optional[str]]] # images_batch : Tensor[N, C, H, W] # image : Tensor[C, H, W] images_and_categories = [ {"image": image, "category": category} for element in batch for image, category in zip(element["images"], element["categories"]) ] if not images_and_categories: continue if self.shuffle: random.shuffle(images_and_categories) yield from self._produce_data(images_and_categories) # pyre-ignore[6] def _produce_data( self, images_and_categories: List[Tuple[torch.Tensor, Optional[str]]] ) -> Iterator[List[SampledData]]: """ Produce batches of data from images Args: images_and_categories (List[Tuple[torch.Tensor, Optional[str]]]): list of images and corresponding categories to process Returns: Iterator over batches of data sampled from model outputs """ data_batches: List[SampledData] = [] category_to_class_mapping = self.category_to_class_mapping batched_images_and_categories = _grouper(images_and_categories, self.inference_batch_size) for batch in batched_images_and_categories: batch = [ { "image": image_and_category["image"].to(self.model.device), "category": image_and_category["category"], } for image_and_category in batch if image_and_category is not None ] if not batch: continue with torch.no_grad(): model_output = self.model(batch) for model_output_i, batch_i in zip(model_output, batch): assert len(batch_i["image"].shape) == 3 model_output_i["image"] = batch_i["image"] instance_class = category_to_class_mapping.get(batch_i["category"], 0) model_output_i["instances"].dataset_classes = torch.tensor( [instance_class] * len(model_output_i["instances"]) ) model_output_filtered = ( model_output if self.data_filter is None else self.data_filter(model_output) ) data = ( model_output_filtered if self.data_sampler is None else self.data_sampler(model_output_filtered) ) for data_i in data: if len(data_i["instances"]): data_batches.append(data_i) if len(data_batches) >= self.batch_size: yield data_batches[: self.batch_size] data_batches = data_batches[self.batch_size :] if not self.drop_last and data_batches: yield data_batches ================================================ FILE: detectron2/projects/DensePose/densepose/data/meshes/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from . import builtin __all__ = [k for k in globals().keys() if "builtin" not in k and not k.startswith("_")] ================================================ FILE: detectron2/projects/DensePose/densepose/data/meshes/builtin.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from .catalog import MeshInfo, register_meshes DENSEPOSE_MESHES_DIR = "https://dl.fbaipublicfiles.com/densepose/meshes/" MESHES = [ MeshInfo( name="smpl_27554", data="smpl_27554.pkl", geodists="geodists/geodists_smpl_27554.pkl", symmetry="symmetry/symmetry_smpl_27554.pkl", texcoords="texcoords/texcoords_smpl_27554.pkl", ), MeshInfo( name="chimp_5029", data="chimp_5029.pkl", geodists="geodists/geodists_chimp_5029.pkl", symmetry="symmetry/symmetry_chimp_5029.pkl", texcoords="texcoords/texcoords_chimp_5029.pkl", ), MeshInfo( name="cat_5001", data="cat_5001.pkl", geodists="geodists/geodists_cat_5001.pkl", symmetry="symmetry/symmetry_cat_5001.pkl", texcoords="texcoords/texcoords_cat_5001.pkl", ), MeshInfo( name="cat_7466", data="cat_7466.pkl", geodists="geodists/geodists_cat_7466.pkl", symmetry="symmetry/symmetry_cat_7466.pkl", texcoords="texcoords/texcoords_cat_7466.pkl", ), MeshInfo( name="sheep_5004", data="sheep_5004.pkl", geodists="geodists/geodists_sheep_5004.pkl", symmetry="symmetry/symmetry_sheep_5004.pkl", texcoords="texcoords/texcoords_sheep_5004.pkl", ), MeshInfo( name="zebra_5002", data="zebra_5002.pkl", geodists="geodists/geodists_zebra_5002.pkl", symmetry="symmetry/symmetry_zebra_5002.pkl", texcoords="texcoords/texcoords_zebra_5002.pkl", ), MeshInfo( name="horse_5004", data="horse_5004.pkl", geodists="geodists/geodists_horse_5004.pkl", symmetry="symmetry/symmetry_horse_5004.pkl", texcoords="texcoords/texcoords_zebra_5002.pkl", ), MeshInfo( name="giraffe_5002", data="giraffe_5002.pkl", geodists="geodists/geodists_giraffe_5002.pkl", symmetry="symmetry/symmetry_giraffe_5002.pkl", texcoords="texcoords/texcoords_giraffe_5002.pkl", ), MeshInfo( name="elephant_5002", data="elephant_5002.pkl", geodists="geodists/geodists_elephant_5002.pkl", symmetry="symmetry/symmetry_elephant_5002.pkl", texcoords="texcoords/texcoords_elephant_5002.pkl", ), MeshInfo( name="dog_5002", data="dog_5002.pkl", geodists="geodists/geodists_dog_5002.pkl", symmetry="symmetry/symmetry_dog_5002.pkl", texcoords="texcoords/texcoords_dog_5002.pkl", ), MeshInfo( name="dog_7466", data="dog_7466.pkl", geodists="geodists/geodists_dog_7466.pkl", symmetry="symmetry/symmetry_dog_7466.pkl", texcoords="texcoords/texcoords_dog_7466.pkl", ), MeshInfo( name="cow_5002", data="cow_5002.pkl", geodists="geodists/geodists_cow_5002.pkl", symmetry="symmetry/symmetry_cow_5002.pkl", texcoords="texcoords/texcoords_cow_5002.pkl", ), MeshInfo( name="bear_4936", data="bear_4936.pkl", geodists="geodists/geodists_bear_4936.pkl", symmetry="symmetry/symmetry_bear_4936.pkl", texcoords="texcoords/texcoords_bear_4936.pkl", ), ] register_meshes(MESHES, DENSEPOSE_MESHES_DIR) ================================================ FILE: detectron2/projects/DensePose/densepose/data/meshes/catalog.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import logging from collections import UserDict from dataclasses import dataclass from typing import Iterable, Optional from ..utils import maybe_prepend_base_path @dataclass class MeshInfo: name: str data: str geodists: Optional[str] = None symmetry: Optional[str] = None texcoords: Optional[str] = None class _MeshCatalog(UserDict): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.mesh_ids = {} self.mesh_names = {} self.max_mesh_id = -1 def __setitem__(self, key, value): if key in self: logger = logging.getLogger(__name__) logger.warning( f"Overwriting mesh catalog entry '{key}': old value {self[key]}" f", new value {value}" ) mesh_id = self.mesh_ids[key] else: self.max_mesh_id += 1 mesh_id = self.max_mesh_id super().__setitem__(key, value) self.mesh_ids[key] = mesh_id self.mesh_names[mesh_id] = key def get_mesh_id(self, shape_name: str) -> int: return self.mesh_ids[shape_name] def get_mesh_name(self, mesh_id: int) -> str: return self.mesh_names[mesh_id] MeshCatalog = _MeshCatalog() def register_mesh(mesh_info: MeshInfo, base_path: Optional[str]) -> None: geodists, symmetry, texcoords = mesh_info.geodists, mesh_info.symmetry, mesh_info.texcoords if geodists: geodists = maybe_prepend_base_path(base_path, geodists) if symmetry: symmetry = maybe_prepend_base_path(base_path, symmetry) if texcoords: texcoords = maybe_prepend_base_path(base_path, texcoords) MeshCatalog[mesh_info.name] = MeshInfo( name=mesh_info.name, data=maybe_prepend_base_path(base_path, mesh_info.data), geodists=geodists, symmetry=symmetry, texcoords=texcoords, ) def register_meshes(mesh_infos: Iterable[MeshInfo], base_path: Optional[str]) -> None: for mesh_info in mesh_infos: register_mesh(mesh_info, base_path) ================================================ FILE: detectron2/projects/DensePose/densepose/data/samplers/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .densepose_uniform import DensePoseUniformSampler from .densepose_confidence_based import DensePoseConfidenceBasedSampler from .densepose_cse_uniform import DensePoseCSEUniformSampler from .densepose_cse_confidence_based import DensePoseCSEConfidenceBasedSampler from .mask_from_densepose import MaskFromDensePoseSampler from .prediction_to_gt import PredictionToGroundTruthSampler ================================================ FILE: detectron2/projects/DensePose/densepose/data/samplers/densepose_base.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any, Dict, List, Tuple import torch from torch.nn import functional as F from detectron2.structures import BoxMode, Instances from densepose.converters import ToChartResultConverter from densepose.converters.base import IntTupleBox, make_int_box from densepose.structures import DensePoseDataRelative, DensePoseList class DensePoseBaseSampler: """ Base DensePose sampler to produce DensePose data from DensePose predictions. Samples for each class are drawn according to some distribution over all pixels estimated to belong to that class. """ def __init__(self, count_per_class: int = 8): """ Constructor Args: count_per_class (int): the sampler produces at most `count_per_class` samples for each category """ self.count_per_class = count_per_class def __call__(self, instances: Instances) -> DensePoseList: """ Convert DensePose predictions (an instance of `DensePoseChartPredictorOutput`) into DensePose annotations data (an instance of `DensePoseList`) """ boxes_xyxy_abs = instances.pred_boxes.tensor.clone().cpu() boxes_xywh_abs = BoxMode.convert(boxes_xyxy_abs, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS) dp_datas = [] for i in range(len(boxes_xywh_abs)): annotation_i = self._sample(instances[i], make_int_box(boxes_xywh_abs[i])) annotation_i[DensePoseDataRelative.S_KEY] = self._resample_mask( # pyre-ignore[6] instances[i].pred_densepose ) dp_datas.append(DensePoseDataRelative(annotation_i)) # create densepose annotations on CPU dp_list = DensePoseList(dp_datas, boxes_xyxy_abs, instances.image_size) return dp_list def _sample(self, instance: Instances, bbox_xywh: IntTupleBox) -> Dict[str, List[Any]]: """ Sample DensPoseDataRelative from estimation results """ labels, dp_result = self._produce_labels_and_results(instance) annotation = { DensePoseDataRelative.X_KEY: [], DensePoseDataRelative.Y_KEY: [], DensePoseDataRelative.U_KEY: [], DensePoseDataRelative.V_KEY: [], DensePoseDataRelative.I_KEY: [], } n, h, w = dp_result.shape for part_id in range(1, DensePoseDataRelative.N_PART_LABELS + 1): # indices - tuple of 3 1D tensors of size k # 0: index along the first dimension N # 1: index along H dimension # 2: index along W dimension indices = torch.nonzero(labels.expand(n, h, w) == part_id, as_tuple=True) # values - an array of size [n, k] # n: number of channels (U, V, confidences) # k: number of points labeled with part_id values = dp_result[indices].view(n, -1) k = values.shape[1] count = min(self.count_per_class, k) if count <= 0: continue index_sample = self._produce_index_sample(values, count) sampled_values = values[:, index_sample] sampled_y = indices[1][index_sample] + 0.5 sampled_x = indices[2][index_sample] + 0.5 # prepare / normalize data x = (sampled_x / w * 256.0).cpu().tolist() y = (sampled_y / h * 256.0).cpu().tolist() u = sampled_values[0].clamp(0, 1).cpu().tolist() v = sampled_values[1].clamp(0, 1).cpu().tolist() fine_segm_labels = [part_id] * count # extend annotations annotation[DensePoseDataRelative.X_KEY].extend(x) annotation[DensePoseDataRelative.Y_KEY].extend(y) annotation[DensePoseDataRelative.U_KEY].extend(u) annotation[DensePoseDataRelative.V_KEY].extend(v) annotation[DensePoseDataRelative.I_KEY].extend(fine_segm_labels) return annotation def _produce_index_sample(self, values: torch.Tensor, count: int): """ Abstract method to produce a sample of indices to select data To be implemented in descendants Args: values (torch.Tensor): an array of size [n, k] that contains estimated values (U, V, confidences); n: number of channels (U, V, confidences) k: number of points labeled with part_id count (int): number of samples to produce, should be positive and <= k Return: list(int): indices of values (along axis 1) selected as a sample """ raise NotImplementedError def _produce_labels_and_results(self, instance: Instances) -> Tuple[torch.Tensor, torch.Tensor]: """ Method to get labels and DensePose results from an instance Args: instance (Instances): an instance of `DensePoseChartPredictorOutput` Return: labels (torch.Tensor): shape [H, W], DensePose segmentation labels dp_result (torch.Tensor): shape [2, H, W], stacked DensePose results u and v """ converter = ToChartResultConverter chart_result = converter.convert(instance.pred_densepose, instance.pred_boxes) labels, dp_result = chart_result.labels.cpu(), chart_result.uv.cpu() return labels, dp_result def _resample_mask(self, output: Any) -> torch.Tensor: """ Convert DensePose predictor output to segmentation annotation - tensors of size (256, 256) and type `int64`. Args: output: DensePose predictor output with the following attributes: - coarse_segm: tensor of size [N, D, H, W] with unnormalized coarse segmentation scores - fine_segm: tensor of size [N, C, H, W] with unnormalized fine segmentation scores Return: Tensor of size (S, S) and type `int64` with coarse segmentation annotations, where S = DensePoseDataRelative.MASK_SIZE """ sz = DensePoseDataRelative.MASK_SIZE S = ( F.interpolate(output.coarse_segm, (sz, sz), mode="bilinear", align_corners=False) .argmax(dim=1) .long() ) I = ( ( F.interpolate( output.fine_segm, (sz, sz), mode="bilinear", align_corners=False, ).argmax(dim=1) * (S > 0).long() ) .squeeze() .cpu() ) # Map fine segmentation results to coarse segmentation ground truth # TODO: extract this into separate classes # coarse segmentation: 1 = Torso, 2 = Right Hand, 3 = Left Hand, # 4 = Left Foot, 5 = Right Foot, 6 = Upper Leg Right, 7 = Upper Leg Left, # 8 = Lower Leg Right, 9 = Lower Leg Left, 10 = Upper Arm Left, # 11 = Upper Arm Right, 12 = Lower Arm Left, 13 = Lower Arm Right, # 14 = Head # fine segmentation: 1, 2 = Torso, 3 = Right Hand, 4 = Left Hand, # 5 = Left Foot, 6 = Right Foot, 7, 9 = Upper Leg Right, # 8, 10 = Upper Leg Left, 11, 13 = Lower Leg Right, # 12, 14 = Lower Leg Left, 15, 17 = Upper Arm Left, # 16, 18 = Upper Arm Right, 19, 21 = Lower Arm Left, # 20, 22 = Lower Arm Right, 23, 24 = Head FINE_TO_COARSE_SEGMENTATION = { 1: 1, 2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 7, 9: 6, 10: 7, 11: 8, 12: 9, 13: 8, 14: 9, 15: 10, 16: 11, 17: 10, 18: 11, 19: 12, 20: 13, 21: 12, 22: 13, 23: 14, 24: 14, } mask = torch.zeros((sz, sz), dtype=torch.int64, device=torch.device("cpu")) for i in range(DensePoseDataRelative.N_PART_LABELS): mask[I == i + 1] = FINE_TO_COARSE_SEGMENTATION[i + 1] return mask ================================================ FILE: detectron2/projects/DensePose/densepose/data/samplers/densepose_confidence_based.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import random from typing import Optional, Tuple import torch from densepose.converters import ToChartResultConverterWithConfidences from .densepose_base import DensePoseBaseSampler class DensePoseConfidenceBasedSampler(DensePoseBaseSampler): """ Samples DensePose data from DensePose predictions. Samples for each class are drawn using confidence value estimates. """ def __init__( self, confidence_channel: str, count_per_class: int = 8, search_count_multiplier: Optional[float] = None, search_proportion: Optional[float] = None, ): """ Constructor Args: confidence_channel (str): confidence channel to use for sampling; possible values: "sigma_2": confidences for UV values "fine_segm_confidence": confidences for fine segmentation "coarse_segm_confidence": confidences for coarse segmentation (default: "sigma_2") count_per_class (int): the sampler produces at most `count_per_class` samples for each category (default: 8) search_count_multiplier (float or None): if not None, the total number of the most confident estimates of a given class to consider is defined as `min(search_count_multiplier * count_per_class, N)`, where `N` is the total number of estimates of the class; cannot be specified together with `search_proportion` (default: None) search_proportion (float or None): if not None, the total number of the of the most confident estimates of a given class to consider is defined as `min(max(search_proportion * N, count_per_class), N)`, where `N` is the total number of estimates of the class; cannot be specified together with `search_count_multiplier` (default: None) """ super().__init__(count_per_class) self.confidence_channel = confidence_channel self.search_count_multiplier = search_count_multiplier self.search_proportion = search_proportion assert (search_count_multiplier is None) or (search_proportion is None), ( f"Cannot specify both search_count_multiplier (={search_count_multiplier})" f"and search_proportion (={search_proportion})" ) def _produce_index_sample(self, values: torch.Tensor, count: int): """ Produce a sample of indices to select data based on confidences Args: values (torch.Tensor): an array of size [n, k] that contains estimated values (U, V, confidences); n: number of channels (U, V, confidences) k: number of points labeled with part_id count (int): number of samples to produce, should be positive and <= k Return: list(int): indices of values (along axis 1) selected as a sample """ k = values.shape[1] if k == count: index_sample = list(range(k)) else: # take the best count * search_count_multiplier pixels, # sample from them uniformly # (here best = smallest variance) _, sorted_confidence_indices = torch.sort(values[2]) if self.search_count_multiplier is not None: search_count = min(int(count * self.search_count_multiplier), k) elif self.search_proportion is not None: search_count = min(max(int(k * self.search_proportion), count), k) else: search_count = min(count, k) sample_from_top = random.sample(range(search_count), count) index_sample = sorted_confidence_indices[:search_count][sample_from_top] return index_sample def _produce_labels_and_results(self, instance) -> Tuple[torch.Tensor, torch.Tensor]: """ Method to get labels and DensePose results from an instance, with confidences Args: instance (Instances): an instance of `DensePoseChartPredictorOutputWithConfidences` Return: labels (torch.Tensor): shape [H, W], DensePose segmentation labels dp_result (torch.Tensor): shape [3, H, W], DensePose results u and v stacked with the confidence channel """ converter = ToChartResultConverterWithConfidences chart_result = converter.convert(instance.pred_densepose, instance.pred_boxes) labels, dp_result = chart_result.labels.cpu(), chart_result.uv.cpu() dp_result = torch.cat( (dp_result, getattr(chart_result, self.confidence_channel)[None].cpu()) ) return labels, dp_result ================================================ FILE: detectron2/projects/DensePose/densepose/data/samplers/densepose_cse_base.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any, Dict, List, Tuple import torch from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.structures import Instances from densepose.converters.base import IntTupleBox from densepose.data.utils import get_class_to_mesh_name_mapping from densepose.modeling.cse.utils import squared_euclidean_distance_matrix from densepose.structures import DensePoseDataRelative from .densepose_base import DensePoseBaseSampler class DensePoseCSEBaseSampler(DensePoseBaseSampler): """ Base DensePose sampler to produce DensePose data from DensePose predictions. Samples for each class are drawn according to some distribution over all pixels estimated to belong to that class. """ def __init__( self, cfg: CfgNode, use_gt_categories: bool, embedder: torch.nn.Module, count_per_class: int = 8, ): """ Constructor Args: cfg (CfgNode): the config of the model embedder (torch.nn.Module): necessary to compute mesh vertex embeddings count_per_class (int): the sampler produces at most `count_per_class` samples for each category """ super().__init__(count_per_class) self.embedder = embedder self.class_to_mesh_name = get_class_to_mesh_name_mapping(cfg) self.use_gt_categories = use_gt_categories def _sample(self, instance: Instances, bbox_xywh: IntTupleBox) -> Dict[str, List[Any]]: """ Sample DensPoseDataRelative from estimation results """ if self.use_gt_categories: instance_class = instance.dataset_classes.tolist()[0] else: instance_class = instance.pred_classes.tolist()[0] mesh_name = self.class_to_mesh_name[instance_class] annotation = { DensePoseDataRelative.X_KEY: [], DensePoseDataRelative.Y_KEY: [], DensePoseDataRelative.VERTEX_IDS_KEY: [], DensePoseDataRelative.MESH_NAME_KEY: mesh_name, } mask, embeddings, other_values = self._produce_mask_and_results(instance, bbox_xywh) indices = torch.nonzero(mask, as_tuple=True) selected_embeddings = embeddings.permute(1, 2, 0)[indices].cpu() values = other_values[:, indices[0], indices[1]] k = values.shape[1] count = min(self.count_per_class, k) if count <= 0: return annotation index_sample = self._produce_index_sample(values, count) closest_vertices = squared_euclidean_distance_matrix( selected_embeddings[index_sample], self.embedder(mesh_name) ) closest_vertices = torch.argmin(closest_vertices, dim=1) sampled_y = indices[0][index_sample] + 0.5 sampled_x = indices[1][index_sample] + 0.5 # prepare / normalize data _, _, w, h = bbox_xywh x = (sampled_x / w * 256.0).cpu().tolist() y = (sampled_y / h * 256.0).cpu().tolist() # extend annotations annotation[DensePoseDataRelative.X_KEY].extend(x) annotation[DensePoseDataRelative.Y_KEY].extend(y) annotation[DensePoseDataRelative.VERTEX_IDS_KEY].extend(closest_vertices.cpu().tolist()) return annotation def _produce_mask_and_results( self, instance: Instances, bbox_xywh: IntTupleBox ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: """ Method to get labels and DensePose results from an instance Args: instance (Instances): an instance of `DensePoseEmbeddingPredictorOutput` bbox_xywh (IntTupleBox): the corresponding bounding box Return: mask (torch.Tensor): shape [H, W], DensePose segmentation mask embeddings (Tuple[torch.Tensor]): a tensor of shape [D, H, W], DensePose CSE Embeddings other_values (Tuple[torch.Tensor]): a tensor of shape [0, H, W], for potential other values """ densepose_output = instance.pred_densepose S = densepose_output.coarse_segm E = densepose_output.embedding _, _, w, h = bbox_xywh embeddings = F.interpolate(E, size=(h, w), mode="bilinear")[0] coarse_segm_resized = F.interpolate(S, size=(h, w), mode="bilinear")[0] mask = coarse_segm_resized.argmax(0) > 0 other_values = torch.empty((0, h, w), device=E.device) return mask, embeddings, other_values def _resample_mask(self, output: Any) -> torch.Tensor: """ Convert DensePose predictor output to segmentation annotation - tensors of size (256, 256) and type `int64`. Args: output: DensePose predictor output with the following attributes: - coarse_segm: tensor of size [N, D, H, W] with unnormalized coarse segmentation scores Return: Tensor of size (S, S) and type `int64` with coarse segmentation annotations, where S = DensePoseDataRelative.MASK_SIZE """ sz = DensePoseDataRelative.MASK_SIZE mask = ( F.interpolate(output.coarse_segm, (sz, sz), mode="bilinear", align_corners=False) .argmax(dim=1) .long() .squeeze() .cpu() ) return mask ================================================ FILE: detectron2/projects/DensePose/densepose/data/samplers/densepose_cse_confidence_based.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import random from typing import Optional, Tuple import torch from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.structures import Instances from densepose.converters.base import IntTupleBox from .densepose_cse_base import DensePoseCSEBaseSampler class DensePoseCSEConfidenceBasedSampler(DensePoseCSEBaseSampler): """ Samples DensePose data from DensePose predictions. Samples for each class are drawn using confidence value estimates. """ def __init__( self, cfg: CfgNode, use_gt_categories: bool, embedder: torch.nn.Module, confidence_channel: str, count_per_class: int = 8, search_count_multiplier: Optional[float] = None, search_proportion: Optional[float] = None, ): """ Constructor Args: cfg (CfgNode): the config of the model embedder (torch.nn.Module): necessary to compute mesh vertex embeddings confidence_channel (str): confidence channel to use for sampling; possible values: "coarse_segm_confidence": confidences for coarse segmentation (default: "coarse_segm_confidence") count_per_class (int): the sampler produces at most `count_per_class` samples for each category (default: 8) search_count_multiplier (float or None): if not None, the total number of the most confident estimates of a given class to consider is defined as `min(search_count_multiplier * count_per_class, N)`, where `N` is the total number of estimates of the class; cannot be specified together with `search_proportion` (default: None) search_proportion (float or None): if not None, the total number of the of the most confident estimates of a given class to consider is defined as `min(max(search_proportion * N, count_per_class), N)`, where `N` is the total number of estimates of the class; cannot be specified together with `search_count_multiplier` (default: None) """ super().__init__(cfg, use_gt_categories, embedder, count_per_class) self.confidence_channel = confidence_channel self.search_count_multiplier = search_count_multiplier self.search_proportion = search_proportion assert (search_count_multiplier is None) or (search_proportion is None), ( f"Cannot specify both search_count_multiplier (={search_count_multiplier})" f"and search_proportion (={search_proportion})" ) def _produce_index_sample(self, values: torch.Tensor, count: int): """ Produce a sample of indices to select data based on confidences Args: values (torch.Tensor): a tensor of length k that contains confidences k: number of points labeled with part_id count (int): number of samples to produce, should be positive and <= k Return: list(int): indices of values (along axis 1) selected as a sample """ k = values.shape[1] if k == count: index_sample = list(range(k)) else: # take the best count * search_count_multiplier pixels, # sample from them uniformly # (here best = smallest variance) _, sorted_confidence_indices = torch.sort(values[0]) if self.search_count_multiplier is not None: search_count = min(int(count * self.search_count_multiplier), k) elif self.search_proportion is not None: search_count = min(max(int(k * self.search_proportion), count), k) else: search_count = min(count, k) sample_from_top = random.sample(range(search_count), count) index_sample = sorted_confidence_indices[-search_count:][sample_from_top] return index_sample def _produce_mask_and_results( self, instance: Instances, bbox_xywh: IntTupleBox ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: """ Method to get labels and DensePose results from an instance Args: instance (Instances): an instance of `DensePoseEmbeddingPredictorOutputWithConfidences` bbox_xywh (IntTupleBox): the corresponding bounding box Return: mask (torch.Tensor): shape [H, W], DensePose segmentation mask embeddings (Tuple[torch.Tensor]): a tensor of shape [D, H, W] DensePose CSE Embeddings other_values: a tensor of shape [1, H, W], DensePose CSE confidence """ _, _, w, h = bbox_xywh densepose_output = instance.pred_densepose mask, embeddings, _ = super()._produce_mask_and_results(instance, bbox_xywh) other_values = F.interpolate( getattr(densepose_output, self.confidence_channel), size=(h, w), mode="bilinear", )[0].cpu() return mask, embeddings, other_values ================================================ FILE: detectron2/projects/DensePose/densepose/data/samplers/densepose_cse_uniform.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .densepose_cse_base import DensePoseCSEBaseSampler from .densepose_uniform import DensePoseUniformSampler class DensePoseCSEUniformSampler(DensePoseCSEBaseSampler, DensePoseUniformSampler): """ Uniform Sampler for CSE """ pass ================================================ FILE: detectron2/projects/DensePose/densepose/data/samplers/densepose_uniform.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import random import torch from .densepose_base import DensePoseBaseSampler class DensePoseUniformSampler(DensePoseBaseSampler): """ Samples DensePose data from DensePose predictions. Samples for each class are drawn uniformly over all pixels estimated to belong to that class. """ def __init__(self, count_per_class: int = 8): """ Constructor Args: count_per_class (int): the sampler produces at most `count_per_class` samples for each category """ super().__init__(count_per_class) def _produce_index_sample(self, values: torch.Tensor, count: int): """ Produce a uniform sample of indices to select data Args: values (torch.Tensor): an array of size [n, k] that contains estimated values (U, V, confidences); n: number of channels (U, V, confidences) k: number of points labeled with part_id count (int): number of samples to produce, should be positive and <= k Return: list(int): indices of values (along axis 1) selected as a sample """ k = values.shape[1] return random.sample(range(k), count) ================================================ FILE: detectron2/projects/DensePose/densepose/data/samplers/mask_from_densepose.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.structures import BitMasks, Instances from densepose.converters import ToMaskConverter class MaskFromDensePoseSampler: """ Produce mask GT from DensePose predictions This sampler simply converts DensePose predictions to BitMasks that a contain a bool tensor of the size of the input image """ def __call__(self, instances: Instances) -> BitMasks: """ Converts predicted data from `instances` into the GT mask data Args: instances (Instances): predicted results, expected to have `pred_densepose` field Returns: Boolean Tensor of the size of the input image that has non-zero values at pixels that are estimated to belong to the detected object """ return ToMaskConverter.convert( instances.pred_densepose, instances.pred_boxes, instances.image_size ) ================================================ FILE: detectron2/projects/DensePose/densepose/data/samplers/prediction_to_gt.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from dataclasses import dataclass from typing import Any, Callable, Dict, List, Optional from detectron2.structures import Instances ModelOutput = Dict[str, Any] SampledData = Dict[str, Any] @dataclass class _Sampler: """ Sampler registry entry that contains: - src (str): source field to sample from (deleted after sampling) - dst (Optional[str]): destination field to sample to, if not None - func (Optional[Callable: Any -> Any]): function that performs sampling, if None, reference copy is performed """ src: str dst: Optional[str] func: Optional[Callable[[Any], Any]] class PredictionToGroundTruthSampler: """ Sampler implementation that converts predictions to GT using registered samplers for different fields of `Instances`. """ def __init__(self, dataset_name: str = ""): self.dataset_name = dataset_name self._samplers = {} self.register_sampler("pred_boxes", "gt_boxes", None) self.register_sampler("pred_classes", "gt_classes", None) # delete scores self.register_sampler("scores") def __call__(self, model_output: List[ModelOutput]) -> List[SampledData]: """ Transform model output into ground truth data through sampling Args: model_output (Dict[str, Any]): model output Returns: Dict[str, Any]: sampled data """ for model_output_i in model_output: instances: Instances = model_output_i["instances"] # transform data in each field for _, sampler in self._samplers.items(): if not instances.has(sampler.src) or sampler.dst is None: continue if sampler.func is None: instances.set(sampler.dst, instances.get(sampler.src)) else: instances.set(sampler.dst, sampler.func(instances)) # delete model output data that was transformed for _, sampler in self._samplers.items(): if sampler.src != sampler.dst and instances.has(sampler.src): instances.remove(sampler.src) model_output_i["dataset"] = self.dataset_name return model_output def register_sampler( self, prediction_attr: str, gt_attr: Optional[str] = None, func: Optional[Callable[[Any], Any]] = None, ): """ Register sampler for a field Args: prediction_attr (str): field to replace with a sampled value gt_attr (Optional[str]): field to store the sampled value to, if not None func (Optional[Callable: Any -> Any]): sampler function """ self._samplers[(prediction_attr, gt_attr)] = _Sampler( src=prediction_attr, dst=gt_attr, func=func ) def remove_sampler( self, prediction_attr: str, gt_attr: Optional[str] = None, ): """ Remove sampler for a field Args: prediction_attr (str): field to replace with a sampled value gt_attr (Optional[str]): field to store the sampled value to, if not None """ assert (prediction_attr, gt_attr) in self._samplers del self._samplers[(prediction_attr, gt_attr)] ================================================ FILE: detectron2/projects/DensePose/densepose/data/transform/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .image import ImageResizeTransform ================================================ FILE: detectron2/projects/DensePose/densepose/data/transform/image.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch class ImageResizeTransform: """ Transform that resizes images loaded from a dataset (BGR data in NCHW channel order, typically uint8) to a format ready to be consumed by DensePose training (BGR float32 data in NCHW channel order) """ def __init__(self, min_size: int = 800, max_size: int = 1333): self.min_size = min_size self.max_size = max_size def __call__(self, images: torch.Tensor) -> torch.Tensor: """ Args: images (torch.Tensor): tensor of size [N, 3, H, W] that contains BGR data (typically in uint8) Returns: images (torch.Tensor): tensor of size [N, 3, H1, W1] where H1 and W1 are chosen to respect the specified min and max sizes and preserve the original aspect ratio, the data channels follow BGR order and the data type is `torch.float32` """ # resize with min size images = images.float() min_size = min(images.shape[-2:]) max_size = max(images.shape[-2:]) scale = min(self.min_size / min_size, self.max_size / max_size) images = torch.nn.functional.interpolate( images, scale_factor=scale, mode="bilinear", align_corners=False, ) return images ================================================ FILE: detectron2/projects/DensePose/densepose/data/utils.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import os from typing import Dict, Optional from detectron2.config import CfgNode def is_relative_local_path(path: str) -> bool: path_str = os.fsdecode(path) return ("://" not in path_str) and not os.path.isabs(path) def maybe_prepend_base_path(base_path: Optional[str], path: str): """ Prepends the provided path with a base path prefix if: 1) base path is not None; 2) path is a local path """ if base_path is None: return path if is_relative_local_path(path): return os.path.join(base_path, path) return path def get_class_to_mesh_name_mapping(cfg: CfgNode) -> Dict[int, str]: return { int(class_id): mesh_name for class_id, mesh_name in cfg.DATASETS.CLASS_TO_MESH_NAME_MAPPING.items() } def get_category_to_class_mapping(dataset_cfg: CfgNode) -> Dict[str, int]: return { category: int(class_id) for category, class_id in dataset_cfg.CATEGORY_TO_CLASS_MAPPING.items() } ================================================ FILE: detectron2/projects/DensePose/densepose/data/video/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .frame_selector import ( FrameSelectionStrategy, RandomKFramesSelector, FirstKFramesSelector, LastKFramesSelector, FrameTsList, FrameSelector, ) from .video_keyframe_dataset import ( VideoKeyframeDataset, video_list_from_file, list_keyframes, read_keyframes, ) ================================================ FILE: detectron2/projects/DensePose/densepose/data/video/frame_selector.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import random from collections.abc import Callable from enum import Enum from typing import Callable as TCallable from typing import List FrameTsList = List[int] FrameSelector = TCallable[[FrameTsList], FrameTsList] class FrameSelectionStrategy(Enum): """ Frame selection strategy used with videos: - "random_k": select k random frames - "first_k": select k first frames - "last_k": select k last frames - "all": select all frames """ # fmt: off RANDOM_K = "random_k" FIRST_K = "first_k" LAST_K = "last_k" ALL = "all" # fmt: on class RandomKFramesSelector(Callable): # pyre-ignore[39] """ Selector that retains at most `k` random frames """ def __init__(self, k: int): self.k = k def __call__(self, frame_tss: FrameTsList) -> FrameTsList: """ Select `k` random frames Args: frames_tss (List[int]): timestamps of input frames Returns: List[int]: timestamps of selected frames """ return random.sample(frame_tss, min(self.k, len(frame_tss))) class FirstKFramesSelector(Callable): # pyre-ignore[39] """ Selector that retains at most `k` first frames """ def __init__(self, k: int): self.k = k def __call__(self, frame_tss: FrameTsList) -> FrameTsList: """ Select `k` first frames Args: frames_tss (List[int]): timestamps of input frames Returns: List[int]: timestamps of selected frames """ return frame_tss[: self.k] class LastKFramesSelector(Callable): # pyre-ignore[39] """ Selector that retains at most `k` last frames from video data """ def __init__(self, k: int): self.k = k def __call__(self, frame_tss: FrameTsList) -> FrameTsList: """ Select `k` last frames Args: frames_tss (List[int]): timestamps of input frames Returns: List[int]: timestamps of selected frames """ return frame_tss[-self.k :] ================================================ FILE: detectron2/projects/DensePose/densepose/data/video/video_keyframe_dataset.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import csv import logging import numpy as np from typing import Any, Callable, Dict, List, Optional, Union import av import torch from torch.utils.data.dataset import Dataset from detectron2.utils.file_io import PathManager from ..utils import maybe_prepend_base_path from .frame_selector import FrameSelector, FrameTsList FrameList = List[av.frame.Frame] # pyre-ignore[16] FrameTransform = Callable[[torch.Tensor], torch.Tensor] def list_keyframes(video_fpath: str, video_stream_idx: int = 0) -> FrameTsList: """ Traverses all keyframes of a video file. Returns a list of keyframe timestamps. Timestamps are counts in timebase units. Args: video_fpath (str): Video file path video_stream_idx (int): Video stream index (default: 0) Returns: List[int]: list of keyframe timestaps (timestamp is a count in timebase units) """ try: with PathManager.open(video_fpath, "rb") as io: container = av.open(io, mode="r") stream = container.streams.video[video_stream_idx] keyframes = [] pts = -1 # Note: even though we request forward seeks for keyframes, sometimes # a keyframe in backwards direction is returned. We introduce tolerance # as a max count of ignored backward seeks tolerance_backward_seeks = 2 while True: try: container.seek(pts + 1, backward=False, any_frame=False, stream=stream) except av.AVError as e: # the exception occurs when the video length is exceeded, # we then return whatever data we've already collected logger = logging.getLogger(__name__) logger.debug( f"List keyframes: Error seeking video file {video_fpath}, " f"video stream {video_stream_idx}, pts {pts + 1}, AV error: {e}" ) return keyframes except OSError as e: logger = logging.getLogger(__name__) logger.warning( f"List keyframes: Error seeking video file {video_fpath}, " f"video stream {video_stream_idx}, pts {pts + 1}, OS error: {e}" ) return [] packet = next(container.demux(video=video_stream_idx)) if packet.pts is not None and packet.pts <= pts: logger = logging.getLogger(__name__) logger.warning( f"Video file {video_fpath}, stream {video_stream_idx}: " f"bad seek for packet {pts + 1} (got packet {packet.pts}), " f"tolerance {tolerance_backward_seeks}." ) tolerance_backward_seeks -= 1 if tolerance_backward_seeks == 0: return [] pts += 1 continue tolerance_backward_seeks = 2 pts = packet.pts if pts is None: return keyframes if packet.is_keyframe: keyframes.append(pts) return keyframes except OSError as e: logger = logging.getLogger(__name__) logger.warning( f"List keyframes: Error opening video file container {video_fpath}, " f"OS error: {e}" ) except RuntimeError as e: logger = logging.getLogger(__name__) logger.warning( f"List keyframes: Error opening video file container {video_fpath}, " f"Runtime error: {e}" ) return [] def read_keyframes( video_fpath: str, keyframes: FrameTsList, video_stream_idx: int = 0 ) -> FrameList: # pyre-ignore[11] """ Reads keyframe data from a video file. Args: video_fpath (str): Video file path keyframes (List[int]): List of keyframe timestamps (as counts in timebase units to be used in container seek operations) video_stream_idx (int): Video stream index (default: 0) Returns: List[Frame]: list of frames that correspond to the specified timestamps """ try: with PathManager.open(video_fpath, "rb") as io: container = av.open(io) stream = container.streams.video[video_stream_idx] frames = [] for pts in keyframes: try: container.seek(pts, any_frame=False, stream=stream) frame = next(container.decode(video=0)) frames.append(frame) except av.AVError as e: logger = logging.getLogger(__name__) logger.warning( f"Read keyframes: Error seeking video file {video_fpath}, " f"video stream {video_stream_idx}, pts {pts}, AV error: {e}" ) container.close() return frames except OSError as e: logger = logging.getLogger(__name__) logger.warning( f"Read keyframes: Error seeking video file {video_fpath}, " f"video stream {video_stream_idx}, pts {pts}, OS error: {e}" ) container.close() return frames except StopIteration: logger = logging.getLogger(__name__) logger.warning( f"Read keyframes: Error decoding frame from {video_fpath}, " f"video stream {video_stream_idx}, pts {pts}" ) container.close() return frames container.close() return frames except OSError as e: logger = logging.getLogger(__name__) logger.warning( f"Read keyframes: Error opening video file container {video_fpath}, OS error: {e}" ) except RuntimeError as e: logger = logging.getLogger(__name__) logger.warning( f"Read keyframes: Error opening video file container {video_fpath}, Runtime error: {e}" ) return [] def video_list_from_file(video_list_fpath: str, base_path: Optional[str] = None): """ Create a list of paths to video files from a text file. Args: video_list_fpath (str): path to a plain text file with the list of videos base_path (str): base path for entries from the video list (default: None) """ video_list = [] with PathManager.open(video_list_fpath, "r") as io: for line in io: video_list.append(maybe_prepend_base_path(base_path, str(line.strip()))) return video_list def read_keyframe_helper_data(fpath: str): """ Read keyframe data from a file in CSV format: the header should contain "video_id" and "keyframes" fields. Value specifications are: video_id: int keyframes: list(int) Example of contents: video_id,keyframes 2,"[1,11,21,31,41,51,61,71,81]" Args: fpath (str): File containing keyframe data Return: video_id_to_keyframes (dict: int -> list(int)): for a given video ID it contains a list of keyframes for that video """ video_id_to_keyframes = {} try: with PathManager.open(fpath, "r") as io: csv_reader = csv.reader(io) # pyre-ignore[6] header = next(csv_reader) video_id_idx = header.index("video_id") keyframes_idx = header.index("keyframes") for row in csv_reader: video_id = int(row[video_id_idx]) assert ( video_id not in video_id_to_keyframes ), f"Duplicate keyframes entry for video {fpath}" video_id_to_keyframes[video_id] = ( [int(v) for v in row[keyframes_idx][1:-1].split(",")] if len(row[keyframes_idx]) > 2 else [] ) except Exception as e: logger = logging.getLogger(__name__) logger.warning(f"Error reading keyframe helper data from {fpath}: {e}") return video_id_to_keyframes class VideoKeyframeDataset(Dataset): """ Dataset that provides keyframes for a set of videos. """ _EMPTY_FRAMES = torch.empty((0, 3, 1, 1)) def __init__( self, video_list: List[str], category_list: Union[str, List[str], None] = None, frame_selector: Optional[FrameSelector] = None, transform: Optional[FrameTransform] = None, keyframe_helper_fpath: Optional[str] = None, ): """ Dataset constructor Args: video_list (List[str]): list of paths to video files category_list (Union[str, List[str], None]): list of animal categories for each video file. If it is a string, or None, this applies to all videos frame_selector (Callable: KeyFrameList -> KeyFrameList): selects keyframes to process, keyframes are given by packet timestamps in timebase counts. If None, all keyframes are selected (default: None) transform (Callable: torch.Tensor -> torch.Tensor): transforms a batch of RGB images (tensors of size [B, 3, H, W]), returns a tensor of the same size. If None, no transform is applied (default: None) """ if type(category_list) == list: self.category_list = category_list else: self.category_list = [category_list] * len(video_list) assert len(video_list) == len( self.category_list ), "length of video and category lists must be equal" self.video_list = video_list self.frame_selector = frame_selector self.transform = transform self.keyframe_helper_data = ( read_keyframe_helper_data(keyframe_helper_fpath) if keyframe_helper_fpath is not None else None ) def __getitem__(self, idx: int) -> Dict[str, Any]: """ Gets selected keyframes from a given video Args: idx (int): video index in the video list file Returns: A dictionary containing two keys: images (torch.Tensor): tensor of size [N, H, W, 3] or of size defined by the transform that contains keyframes data categories (List[str]): categories of the frames """ categories = [self.category_list[idx]] fpath = self.video_list[idx] keyframes = ( list_keyframes(fpath) if self.keyframe_helper_data is None or idx not in self.keyframe_helper_data else self.keyframe_helper_data[idx] ) transform = self.transform frame_selector = self.frame_selector if not keyframes: return {"images": self._EMPTY_FRAMES, "categories": []} if frame_selector is not None: keyframes = frame_selector(keyframes) frames = read_keyframes(fpath, keyframes) if not frames: return {"images": self._EMPTY_FRAMES, "categories": []} frames = np.stack([frame.to_rgb().to_ndarray() for frame in frames]) frames = torch.as_tensor(frames, device=torch.device("cpu")) frames = frames[..., [2, 1, 0]] # RGB -> BGR frames = frames.permute(0, 3, 1, 2).float() # NHWC -> NCHW if transform is not None: frames = transform(frames) return {"images": frames, "categories": categories} def __len__(self): return len(self.video_list) ================================================ FILE: detectron2/projects/DensePose/densepose/engine/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .trainer import Trainer ================================================ FILE: detectron2/projects/DensePose/densepose/engine/trainer.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import logging import os from collections import OrderedDict from typing import List, Optional, Union import torch from torch import nn from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import CfgNode from detectron2.engine import DefaultTrainer from detectron2.evaluation import ( DatasetEvaluator, DatasetEvaluators, inference_on_dataset, print_csv_format, ) from detectron2.solver.build import get_default_optimizer_params, maybe_add_gradient_clipping from detectron2.utils import comm from detectron2.utils.events import EventWriter, get_event_storage from densepose import DensePoseDatasetMapperTTA, DensePoseGeneralizedRCNNWithTTA, load_from_cfg from densepose.data import ( DatasetMapper, build_combined_loader, build_detection_test_loader, build_detection_train_loader, build_inference_based_loaders, has_inference_based_loaders, ) from densepose.evaluation.d2_evaluator_adapter import Detectron2COCOEvaluatorAdapter from densepose.evaluation.evaluator import DensePoseCOCOEvaluator, build_densepose_evaluator_storage from densepose.modeling.cse import Embedder class SampleCountingLoader: def __init__(self, loader): self.loader = loader def __iter__(self): it = iter(self.loader) storage = get_event_storage() while True: try: batch = next(it) num_inst_per_dataset = {} for data in batch: dataset_name = data["dataset"] if dataset_name not in num_inst_per_dataset: num_inst_per_dataset[dataset_name] = 0 num_inst = len(data["instances"]) num_inst_per_dataset[dataset_name] += num_inst for dataset_name in num_inst_per_dataset: storage.put_scalar(f"batch/{dataset_name}", num_inst_per_dataset[dataset_name]) yield batch except StopIteration: break class SampleCountMetricPrinter(EventWriter): def __init__(self): self.logger = logging.getLogger(__name__) def write(self): storage = get_event_storage() batch_stats_strs = [] for key, buf in storage.histories().items(): if key.startswith("batch/"): batch_stats_strs.append(f"{key} {buf.avg(20)}") self.logger.info(", ".join(batch_stats_strs)) class Trainer(DefaultTrainer): @classmethod def extract_embedder_from_model(cls, model: nn.Module) -> Optional[Embedder]: if isinstance(model, nn.parallel.DistributedDataParallel): model = model.module if hasattr(model, "roi_heads") and hasattr(model.roi_heads, "embedder"): # pyre-fixme[16]: Item `Tensor` of `Union[Tensor, Module]` has no # attribute `embedder`. return model.roi_heads.embedder return None # TODO: the only reason to copy the base class code here is to pass the embedder from # the model to the evaluator; that should be refactored to avoid unnecessary copy-pasting @classmethod def test( cls, cfg: CfgNode, model: nn.Module, evaluators: Optional[Union[DatasetEvaluator, List[DatasetEvaluator]]] = None, ): """ Args: cfg (CfgNode): model (nn.Module): evaluators (DatasetEvaluator, list[DatasetEvaluator] or None): if None, will call :meth:`build_evaluator`. Otherwise, must have the same length as ``cfg.DATASETS.TEST``. Returns: dict: a dict of result metrics """ logger = logging.getLogger(__name__) if isinstance(evaluators, DatasetEvaluator): evaluators = [evaluators] if evaluators is not None: assert len(cfg.DATASETS.TEST) == len(evaluators), "{} != {}".format( len(cfg.DATASETS.TEST), len(evaluators) ) results = OrderedDict() for idx, dataset_name in enumerate(cfg.DATASETS.TEST): data_loader = cls.build_test_loader(cfg, dataset_name) # When evaluators are passed in as arguments, # implicitly assume that evaluators can be created before data_loader. if evaluators is not None: evaluator = evaluators[idx] else: try: embedder = cls.extract_embedder_from_model(model) evaluator = cls.build_evaluator(cfg, dataset_name, embedder=embedder) except NotImplementedError: logger.warn( "No evaluator found. Use `DefaultTrainer.test(evaluators=)`, " "or implement its `build_evaluator` method." ) results[dataset_name] = {} continue if cfg.DENSEPOSE_EVALUATION.DISTRIBUTED_INFERENCE or comm.is_main_process(): results_i = inference_on_dataset(model, data_loader, evaluator) else: results_i = {} results[dataset_name] = results_i if comm.is_main_process(): assert isinstance( results_i, dict ), "Evaluator must return a dict on the main process. Got {} instead.".format( results_i ) logger.info("Evaluation results for {} in csv format:".format(dataset_name)) print_csv_format(results_i) if len(results) == 1: results = list(results.values())[0] return results @classmethod def build_evaluator( cls, cfg: CfgNode, dataset_name: str, output_folder: Optional[str] = None, embedder: Optional[Embedder] = None, ) -> DatasetEvaluators: if output_folder is None: output_folder = os.path.join(cfg.OUTPUT_DIR, "inference") evaluators = [] distributed = cfg.DENSEPOSE_EVALUATION.DISTRIBUTED_INFERENCE # Note: we currently use COCO evaluator for both COCO and LVIS datasets # to have compatible metrics. LVIS bbox evaluator could also be used # with an adapter to properly handle filtered / mapped categories # evaluator_type = MetadataCatalog.get(dataset_name).evaluator_type # if evaluator_type == "coco": # evaluators.append(COCOEvaluator(dataset_name, output_dir=output_folder)) # elif evaluator_type == "lvis": # evaluators.append(LVISEvaluator(dataset_name, output_dir=output_folder)) evaluators.append( Detectron2COCOEvaluatorAdapter( dataset_name, output_dir=output_folder, distributed=distributed ) ) if cfg.MODEL.DENSEPOSE_ON: storage = build_densepose_evaluator_storage(cfg, output_folder) evaluators.append( DensePoseCOCOEvaluator( dataset_name, distributed, output_folder, evaluator_type=cfg.DENSEPOSE_EVALUATION.TYPE, min_iou_threshold=cfg.DENSEPOSE_EVALUATION.MIN_IOU_THRESHOLD, storage=storage, embedder=embedder, should_evaluate_mesh_alignment=cfg.DENSEPOSE_EVALUATION.EVALUATE_MESH_ALIGNMENT, mesh_alignment_mesh_names=cfg.DENSEPOSE_EVALUATION.MESH_ALIGNMENT_MESH_NAMES, ) ) return DatasetEvaluators(evaluators) @classmethod def build_optimizer(cls, cfg: CfgNode, model: nn.Module): params = get_default_optimizer_params( model, base_lr=cfg.SOLVER.BASE_LR, weight_decay_norm=cfg.SOLVER.WEIGHT_DECAY_NORM, bias_lr_factor=cfg.SOLVER.BIAS_LR_FACTOR, weight_decay_bias=cfg.SOLVER.WEIGHT_DECAY_BIAS, overrides={ "features": { "lr": cfg.SOLVER.BASE_LR * cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.FEATURES_LR_FACTOR, }, "embeddings": { "lr": cfg.SOLVER.BASE_LR * cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDING_LR_FACTOR, }, }, ) optimizer = torch.optim.SGD( params, cfg.SOLVER.BASE_LR, momentum=cfg.SOLVER.MOMENTUM, nesterov=cfg.SOLVER.NESTEROV, weight_decay=cfg.SOLVER.WEIGHT_DECAY, ) # pyre-fixme[6]: For 2nd param expected `Type[Optimizer]` but got `SGD`. return maybe_add_gradient_clipping(cfg, optimizer) @classmethod def build_test_loader(cls, cfg: CfgNode, dataset_name): return build_detection_test_loader(cfg, dataset_name, mapper=DatasetMapper(cfg, False)) @classmethod def build_train_loader(cls, cfg: CfgNode): data_loader = build_detection_train_loader(cfg, mapper=DatasetMapper(cfg, True)) if not has_inference_based_loaders(cfg): return data_loader model = cls.build_model(cfg) model.to(cfg.BOOTSTRAP_MODEL.DEVICE) DetectionCheckpointer(model).resume_or_load(cfg.BOOTSTRAP_MODEL.WEIGHTS, resume=False) inference_based_loaders, ratios = build_inference_based_loaders(cfg, model) loaders = [data_loader] + inference_based_loaders ratios = [1.0] + ratios combined_data_loader = build_combined_loader(cfg, loaders, ratios) sample_counting_loader = SampleCountingLoader(combined_data_loader) return sample_counting_loader def build_writers(self): writers = super().build_writers() writers.append(SampleCountMetricPrinter()) return writers @classmethod def test_with_TTA(cls, cfg: CfgNode, model): logger = logging.getLogger("detectron2.trainer") # In the end of training, run an evaluation with TTA # Only support some R-CNN models. logger.info("Running inference with test-time augmentation ...") transform_data = load_from_cfg(cfg) model = DensePoseGeneralizedRCNNWithTTA( cfg, model, transform_data, DensePoseDatasetMapperTTA(cfg) ) evaluators = [ cls.build_evaluator( cfg, name, output_folder=os.path.join(cfg.OUTPUT_DIR, "inference_TTA") ) for name in cfg.DATASETS.TEST ] res = cls.test(cfg, model, evaluators) # pyre-ignore[6] res = OrderedDict({k + "_TTA": v for k, v in res.items()}) return res ================================================ FILE: detectron2/projects/DensePose/densepose/evaluation/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .evaluator import DensePoseCOCOEvaluator ================================================ FILE: detectron2/projects/DensePose/densepose/evaluation/d2_evaluator_adapter.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.data.catalog import Metadata from detectron2.evaluation import COCOEvaluator from densepose.data.datasets.coco import ( get_contiguous_id_to_category_id_map, maybe_filter_categories_cocoapi, ) def _maybe_add_iscrowd_annotations(cocoapi) -> None: for ann in cocoapi.dataset["annotations"]: if "iscrowd" not in ann: ann["iscrowd"] = 0 class Detectron2COCOEvaluatorAdapter(COCOEvaluator): def __init__( self, dataset_name, output_dir=None, distributed=True, ): super().__init__(dataset_name, output_dir=output_dir, distributed=distributed) maybe_filter_categories_cocoapi(dataset_name, self._coco_api) _maybe_add_iscrowd_annotations(self._coco_api) # substitute category metadata to account for categories # that are mapped to the same contiguous id if hasattr(self._metadata, "thing_dataset_id_to_contiguous_id"): self._maybe_substitute_metadata() def _maybe_substitute_metadata(self): cont_id_2_cat_id = get_contiguous_id_to_category_id_map(self._metadata) cat_id_2_cont_id = self._metadata.thing_dataset_id_to_contiguous_id if len(cont_id_2_cat_id) == len(cat_id_2_cont_id): return cat_id_2_cont_id_injective = {} for cat_id, cont_id in cat_id_2_cont_id.items(): if (cont_id in cont_id_2_cat_id) and (cont_id_2_cat_id[cont_id] == cat_id): cat_id_2_cont_id_injective[cat_id] = cont_id metadata_new = Metadata(name=self._metadata.name) for key, value in self._metadata.__dict__.items(): if key == "thing_dataset_id_to_contiguous_id": setattr(metadata_new, key, cat_id_2_cont_id_injective) else: setattr(metadata_new, key, value) self._metadata = metadata_new ================================================ FILE: detectron2/projects/DensePose/densepose/evaluation/densepose_coco_evaluation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # All rights reserved. # # This source code is licensed under the license found in the # LICENSE file in the root directory of this source tree. # This is a modified version of cocoeval.py where we also have the densepose evaluation. __author__ = "tsungyi" import copy import datetime import logging import numpy as np import pickle import time from collections import defaultdict from enum import Enum from typing import Any, Dict, Tuple import scipy.spatial.distance as ssd import torch import torch.nn.functional as F from pycocotools import mask as maskUtils from scipy.io import loadmat from scipy.ndimage import zoom as spzoom from detectron2.utils.file_io import PathManager from densepose.converters.chart_output_to_chart_result import resample_uv_tensors_to_bbox from densepose.converters.segm_to_mask import ( resample_coarse_segm_tensor_to_bbox, resample_fine_and_coarse_segm_tensors_to_bbox, ) from densepose.modeling.cse.utils import squared_euclidean_distance_matrix from densepose.structures import DensePoseDataRelative from densepose.structures.mesh import create_mesh logger = logging.getLogger(__name__) class DensePoseEvalMode(str, Enum): # use both masks and geodesic distances (GPS * IOU) to compute scores GPSM = "gpsm" # use only geodesic distances (GPS) to compute scores GPS = "gps" # use only masks (IOU) to compute scores IOU = "iou" class DensePoseDataMode(str, Enum): # use estimated IUV data (default mode) IUV_DT = "iuvdt" # use ground truth IUV data IUV_GT = "iuvgt" # use ground truth labels I and set UV to 0 I_GT_UV_0 = "igtuv0" # use ground truth labels I and estimated UV coordinates I_GT_UV_DT = "igtuvdt" # use estimated labels I and set UV to 0 I_DT_UV_0 = "idtuv0" class DensePoseCocoEval(object): # Interface for evaluating detection on the Microsoft COCO dataset. # # The usage for CocoEval is as follows: # cocoGt=..., cocoDt=... # load dataset and results # E = CocoEval(cocoGt,cocoDt); # initialize CocoEval object # E.params.recThrs = ...; # set parameters as desired # E.evaluate(); # run per image evaluation # E.accumulate(); # accumulate per image results # E.summarize(); # display summary metrics of results # For example usage see evalDemo.m and http://mscoco.org/. # # The evaluation parameters are as follows (defaults in brackets): # imgIds - [all] N img ids to use for evaluation # catIds - [all] K cat ids to use for evaluation # iouThrs - [.5:.05:.95] T=10 IoU thresholds for evaluation # recThrs - [0:.01:1] R=101 recall thresholds for evaluation # areaRng - [...] A=4 object area ranges for evaluation # maxDets - [1 10 100] M=3 thresholds on max detections per image # iouType - ['segm'] set iouType to 'segm', 'bbox', 'keypoints' or 'densepose' # iouType replaced the now DEPRECATED useSegm parameter. # useCats - [1] if true use category labels for evaluation # Note: if useCats=0 category labels are ignored as in proposal scoring. # Note: multiple areaRngs [Ax2] and maxDets [Mx1] can be specified. # # evaluate(): evaluates detections on every image and every category and # concats the results into the "evalImgs" with fields: # dtIds - [1xD] id for each of the D detections (dt) # gtIds - [1xG] id for each of the G ground truths (gt) # dtMatches - [TxD] matching gt id at each IoU or 0 # gtMatches - [TxG] matching dt id at each IoU or 0 # dtScores - [1xD] confidence of each dt # gtIgnore - [1xG] ignore flag for each gt # dtIgnore - [TxD] ignore flag for each dt at each IoU # # accumulate(): accumulates the per-image, per-category evaluation # results in "evalImgs" into the dictionary "eval" with fields: # params - parameters used for evaluation # date - date evaluation was performed # counts - [T,R,K,A,M] parameter dimensions (see above) # precision - [TxRxKxAxM] precision for every evaluation setting # recall - [TxKxAxM] max recall for every evaluation setting # Note: precision and recall==-1 for settings with no gt objects. # # See also coco, mask, pycocoDemo, pycocoEvalDemo # # Microsoft COCO Toolbox. version 2.0 # Data, paper, and tutorials available at: http://mscoco.org/ # Code written by Piotr Dollar and Tsung-Yi Lin, 2015. # Licensed under the Simplified BSD License [see coco/license.txt] def __init__( self, cocoGt=None, cocoDt=None, iouType: str = "densepose", multi_storage=None, embedder=None, dpEvalMode: DensePoseEvalMode = DensePoseEvalMode.GPS, dpDataMode: DensePoseDataMode = DensePoseDataMode.IUV_DT, ): """ Initialize CocoEval using coco APIs for gt and dt :param cocoGt: coco object with ground truth annotations :param cocoDt: coco object with detection results :return: None """ self.cocoGt = cocoGt # ground truth COCO API self.cocoDt = cocoDt # detections COCO API self.multi_storage = multi_storage self.embedder = embedder self._dpEvalMode = dpEvalMode self._dpDataMode = dpDataMode self.evalImgs = defaultdict(list) # per-image per-category eval results [KxAxI] self.eval = {} # accumulated evaluation results self._gts = defaultdict(list) # gt for evaluation self._dts = defaultdict(list) # dt for evaluation self.params = Params(iouType=iouType) # parameters self._paramsEval = {} # parameters for evaluation self.stats = [] # result summarization self.ious = {} # ious between all gts and dts if cocoGt is not None: self.params.imgIds = sorted(cocoGt.getImgIds()) self.params.catIds = sorted(cocoGt.getCatIds()) self.ignoreThrBB = 0.7 self.ignoreThrUV = 0.9 def _loadGEval(self): smpl_subdiv_fpath = PathManager.get_local_path( "https://dl.fbaipublicfiles.com/densepose/data/SMPL_subdiv.mat" ) pdist_transform_fpath = PathManager.get_local_path( "https://dl.fbaipublicfiles.com/densepose/data/SMPL_SUBDIV_TRANSFORM.mat" ) pdist_matrix_fpath = PathManager.get_local_path( "https://dl.fbaipublicfiles.com/densepose/data/Pdist_matrix.pkl", timeout_sec=120 ) SMPL_subdiv = loadmat(smpl_subdiv_fpath) self.PDIST_transform = loadmat(pdist_transform_fpath) self.PDIST_transform = self.PDIST_transform["index"].squeeze() UV = np.array([SMPL_subdiv["U_subdiv"], SMPL_subdiv["V_subdiv"]]).squeeze() ClosestVertInds = np.arange(UV.shape[1]) + 1 self.Part_UVs = [] self.Part_ClosestVertInds = [] for i in np.arange(24): self.Part_UVs.append(UV[:, SMPL_subdiv["Part_ID_subdiv"].squeeze() == (i + 1)]) self.Part_ClosestVertInds.append( ClosestVertInds[SMPL_subdiv["Part_ID_subdiv"].squeeze() == (i + 1)] ) with open(pdist_matrix_fpath, "rb") as hFile: arrays = pickle.load(hFile, encoding="latin1") self.Pdist_matrix = arrays["Pdist_matrix"] self.Part_ids = np.array(SMPL_subdiv["Part_ID_subdiv"].squeeze()) # Mean geodesic distances for parts. self.Mean_Distances = np.array([0, 0.351, 0.107, 0.126, 0.237, 0.173, 0.142, 0.128, 0.150]) # Coarse Part labels. self.CoarseParts = np.array( [0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8] ) def _prepare(self): """ Prepare ._gts and ._dts for evaluation based on params :return: None """ def _toMask(anns, coco): # modify ann['segmentation'] by reference for ann in anns: # safeguard for invalid segmentation annotation; # annotations containing empty lists exist in the posetrack # dataset. This is not a correct segmentation annotation # in terms of COCO format; we need to deal with it somehow segm = ann["segmentation"] if type(segm) == list and len(segm) == 0: ann["segmentation"] = None continue rle = coco.annToRLE(ann) ann["segmentation"] = rle def _getIgnoreRegion(iid, coco): img = coco.imgs[iid] if "ignore_regions_x" not in img.keys(): return None if len(img["ignore_regions_x"]) == 0: return None rgns_merged = [ [v for xy in zip(region_x, region_y) for v in xy] for region_x, region_y in zip(img["ignore_regions_x"], img["ignore_regions_y"]) ] rles = maskUtils.frPyObjects(rgns_merged, img["height"], img["width"]) rle = maskUtils.merge(rles) return maskUtils.decode(rle) def _checkIgnore(dt, iregion): if iregion is None: return True bb = np.array(dt["bbox"]).astype(np.int) x1, y1, x2, y2 = bb[0], bb[1], bb[0] + bb[2], bb[1] + bb[3] x2 = min([x2, iregion.shape[1]]) y2 = min([y2, iregion.shape[0]]) if bb[2] * bb[3] == 0: return False crop_iregion = iregion[y1:y2, x1:x2] if crop_iregion.sum() == 0: return True if "densepose" not in dt.keys(): # filtering boxes return crop_iregion.sum() / bb[2] / bb[3] < self.ignoreThrBB # filtering UVs ignoremask = np.require(crop_iregion, requirements=["F"]) mask = self._extract_mask(dt) uvmask = np.require(np.asarray(mask > 0), dtype=np.uint8, requirements=["F"]) uvmask_ = maskUtils.encode(uvmask) ignoremask_ = maskUtils.encode(ignoremask) uviou = maskUtils.iou([uvmask_], [ignoremask_], [1])[0] return uviou < self.ignoreThrUV p = self.params if p.useCats: gts = self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) dts = self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds, catIds=p.catIds)) else: gts = self.cocoGt.loadAnns(self.cocoGt.getAnnIds(imgIds=p.imgIds)) dts = self.cocoDt.loadAnns(self.cocoDt.getAnnIds(imgIds=p.imgIds)) imns = self.cocoGt.loadImgs(p.imgIds) self.size_mapping = {} for im in imns: self.size_mapping[im["id"]] = [im["height"], im["width"]] # if iouType == 'uv', add point gt annotations if p.iouType == "densepose": self._loadGEval() # convert ground truth to mask if iouType == 'segm' if p.iouType == "segm": _toMask(gts, self.cocoGt) _toMask(dts, self.cocoDt) # set ignore flag for gt in gts: gt["ignore"] = gt["ignore"] if "ignore" in gt else 0 gt["ignore"] = "iscrowd" in gt and gt["iscrowd"] if p.iouType == "keypoints": gt["ignore"] = (gt["num_keypoints"] == 0) or gt["ignore"] if p.iouType == "densepose": gt["ignore"] = ("dp_x" in gt) == 0 if p.iouType == "segm": gt["ignore"] = gt["segmentation"] is None self._gts = defaultdict(list) # gt for evaluation self._dts = defaultdict(list) # dt for evaluation self._igrgns = defaultdict(list) for gt in gts: iid = gt["image_id"] if iid not in self._igrgns.keys(): self._igrgns[iid] = _getIgnoreRegion(iid, self.cocoGt) if _checkIgnore(gt, self._igrgns[iid]): self._gts[iid, gt["category_id"]].append(gt) for dt in dts: iid = dt["image_id"] if (iid not in self._igrgns) or _checkIgnore(dt, self._igrgns[iid]): self._dts[iid, dt["category_id"]].append(dt) self.evalImgs = defaultdict(list) # per-image per-category evaluation results self.eval = {} # accumulated evaluation results def evaluate(self): """ Run per image evaluation on given images and store results (a list of dict) in self.evalImgs :return: None """ tic = time.time() logger.info("Running per image DensePose evaluation... {}".format(self.params.iouType)) p = self.params # add backward compatibility if useSegm is specified in params if p.useSegm is not None: p.iouType = "segm" if p.useSegm == 1 else "bbox" logger.info("useSegm (deprecated) is not None. Running DensePose evaluation") p.imgIds = list(np.unique(p.imgIds)) if p.useCats: p.catIds = list(np.unique(p.catIds)) p.maxDets = sorted(p.maxDets) self.params = p self._prepare() # loop through images, area range, max detection number catIds = p.catIds if p.useCats else [-1] if p.iouType in ["segm", "bbox"]: computeIoU = self.computeIoU elif p.iouType == "keypoints": computeIoU = self.computeOks elif p.iouType == "densepose": computeIoU = self.computeOgps if self._dpEvalMode in {DensePoseEvalMode.GPSM, DensePoseEvalMode.IOU}: self.real_ious = { (imgId, catId): self.computeDPIoU(imgId, catId) for imgId in p.imgIds for catId in catIds } self.ious = { (imgId, catId): computeIoU(imgId, catId) for imgId in p.imgIds for catId in catIds } evaluateImg = self.evaluateImg maxDet = p.maxDets[-1] self.evalImgs = [ evaluateImg(imgId, catId, areaRng, maxDet) for catId in catIds for areaRng in p.areaRng for imgId in p.imgIds ] self._paramsEval = copy.deepcopy(self.params) toc = time.time() logger.info("DensePose evaluation DONE (t={:0.2f}s).".format(toc - tic)) def getDensePoseMask(self, polys): maskGen = np.zeros([256, 256]) stop = min(len(polys) + 1, 15) for i in range(1, stop): if polys[i - 1]: currentMask = maskUtils.decode(polys[i - 1]) maskGen[currentMask > 0] = i return maskGen def _generate_rlemask_on_image(self, mask, imgId, data): bbox_xywh = np.array(data["bbox"]) x, y, w, h = bbox_xywh im_h, im_w = self.size_mapping[imgId] im_mask = np.zeros((im_h, im_w), dtype=np.uint8) if mask is not None: x0 = max(int(x), 0) x1 = min(int(x + w), im_w, int(x) + mask.shape[1]) y0 = max(int(y), 0) y1 = min(int(y + h), im_h, int(y) + mask.shape[0]) y = int(y) x = int(x) im_mask[y0:y1, x0:x1] = mask[y0 - y : y1 - y, x0 - x : x1 - x] im_mask = np.require(np.asarray(im_mask > 0), dtype=np.uint8, requirements=["F"]) rle_mask = maskUtils.encode(np.array(im_mask[:, :, np.newaxis], order="F"))[0] return rle_mask def computeDPIoU(self, imgId, catId): p = self.params if p.useCats: gt = self._gts[imgId, catId] dt = self._dts[imgId, catId] else: gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]] dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]] if len(gt) == 0 and len(dt) == 0: return [] inds = np.argsort([-d["score"] for d in dt], kind="mergesort") dt = [dt[i] for i in inds] if len(dt) > p.maxDets[-1]: dt = dt[0 : p.maxDets[-1]] gtmasks = [] for g in gt: if DensePoseDataRelative.S_KEY in g: # convert DensePose mask to a binary mask mask = np.minimum(self.getDensePoseMask(g[DensePoseDataRelative.S_KEY]), 1.0) _, _, w, h = g["bbox"] scale_x = float(max(w, 1)) / mask.shape[1] scale_y = float(max(h, 1)) / mask.shape[0] mask = spzoom(mask, (scale_y, scale_x), order=1, prefilter=False) mask = np.array(mask > 0.5, dtype=np.uint8) rle_mask = self._generate_rlemask_on_image(mask, imgId, g) elif "segmentation" in g: segmentation = g["segmentation"] if isinstance(segmentation, list) and segmentation: # polygons im_h, im_w = self.size_mapping[imgId] rles = maskUtils.frPyObjects(segmentation, im_h, im_w) rle_mask = maskUtils.merge(rles) elif isinstance(segmentation, dict): if isinstance(segmentation["counts"], list): # uncompressed RLE im_h, im_w = self.size_mapping[imgId] rle_mask = maskUtils.frPyObjects(segmentation, im_h, im_w) else: # compressed RLE rle_mask = segmentation else: rle_mask = self._generate_rlemask_on_image(None, imgId, g) else: rle_mask = self._generate_rlemask_on_image(None, imgId, g) gtmasks.append(rle_mask) dtmasks = [] for d in dt: mask = self._extract_mask(d) mask = np.require(np.asarray(mask > 0), dtype=np.uint8, requirements=["F"]) rle_mask = self._generate_rlemask_on_image(mask, imgId, d) dtmasks.append(rle_mask) # compute iou between each dt and gt region iscrowd = [int(o.get("iscrowd", 0)) for o in gt] iousDP = maskUtils.iou(dtmasks, gtmasks, iscrowd) return iousDP def computeIoU(self, imgId, catId): p = self.params if p.useCats: gt = self._gts[imgId, catId] dt = self._dts[imgId, catId] else: gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]] dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]] if len(gt) == 0 and len(dt) == 0: return [] inds = np.argsort([-d["score"] for d in dt], kind="mergesort") dt = [dt[i] for i in inds] if len(dt) > p.maxDets[-1]: dt = dt[0 : p.maxDets[-1]] if p.iouType == "segm": g = [g["segmentation"] for g in gt if g["segmentation"] is not None] d = [d["segmentation"] for d in dt if d["segmentation"] is not None] elif p.iouType == "bbox": g = [g["bbox"] for g in gt] d = [d["bbox"] for d in dt] else: raise Exception("unknown iouType for iou computation") # compute iou between each dt and gt region iscrowd = [int(o.get("iscrowd", 0)) for o in gt] ious = maskUtils.iou(d, g, iscrowd) return ious def computeOks(self, imgId, catId): p = self.params # dimension here should be Nxm gts = self._gts[imgId, catId] dts = self._dts[imgId, catId] inds = np.argsort([-d["score"] for d in dts], kind="mergesort") dts = [dts[i] for i in inds] if len(dts) > p.maxDets[-1]: dts = dts[0 : p.maxDets[-1]] # if len(gts) == 0 and len(dts) == 0: if len(gts) == 0 or len(dts) == 0: return [] ious = np.zeros((len(dts), len(gts))) sigmas = ( np.array( [ 0.26, 0.25, 0.25, 0.35, 0.35, 0.79, 0.79, 0.72, 0.72, 0.62, 0.62, 1.07, 1.07, 0.87, 0.87, 0.89, 0.89, ] ) / 10.0 ) vars = (sigmas * 2) ** 2 k = len(sigmas) # compute oks between each detection and ground truth object for j, gt in enumerate(gts): # create bounds for ignore regions(double the gt bbox) g = np.array(gt["keypoints"]) xg = g[0::3] yg = g[1::3] vg = g[2::3] k1 = np.count_nonzero(vg > 0) bb = gt["bbox"] x0 = bb[0] - bb[2] x1 = bb[0] + bb[2] * 2 y0 = bb[1] - bb[3] y1 = bb[1] + bb[3] * 2 for i, dt in enumerate(dts): d = np.array(dt["keypoints"]) xd = d[0::3] yd = d[1::3] if k1 > 0: # measure the per-keypoint distance if keypoints visible dx = xd - xg dy = yd - yg else: # measure minimum distance to keypoints in (x0,y0) & (x1,y1) z = np.zeros(k) dx = np.max((z, x0 - xd), axis=0) + np.max((z, xd - x1), axis=0) dy = np.max((z, y0 - yd), axis=0) + np.max((z, yd - y1), axis=0) e = (dx**2 + dy**2) / vars / (gt["area"] + np.spacing(1)) / 2 if k1 > 0: e = e[vg > 0] ious[i, j] = np.sum(np.exp(-e)) / e.shape[0] return ious def _extract_mask(self, dt: Dict[str, Any]) -> np.ndarray: if "densepose" in dt: densepose_results_quantized = dt["densepose"] return densepose_results_quantized.labels_uv_uint8[0].numpy() elif "cse_mask" in dt: return dt["cse_mask"] elif "coarse_segm" in dt: dy = max(int(dt["bbox"][3]), 1) dx = max(int(dt["bbox"][2]), 1) return ( F.interpolate( dt["coarse_segm"].unsqueeze(0), (dy, dx), mode="bilinear", align_corners=False, ) .squeeze(0) .argmax(0) .numpy() .astype(np.uint8) ) elif "record_id" in dt: assert ( self.multi_storage is not None ), f"Storage record id encountered in a detection {dt}, but no storage provided!" record = self.multi_storage.get(dt["rank"], dt["record_id"]) coarse_segm = record["coarse_segm"] dy = max(int(dt["bbox"][3]), 1) dx = max(int(dt["bbox"][2]), 1) return ( F.interpolate( coarse_segm.unsqueeze(0), (dy, dx), mode="bilinear", align_corners=False, ) .squeeze(0) .argmax(0) .numpy() .astype(np.uint8) ) else: raise Exception(f"No mask data in the detection: {dt}") raise ValueError('The prediction dict needs to contain either "densepose" or "cse_mask"') def _extract_iuv( self, densepose_data: np.ndarray, py: np.ndarray, px: np.ndarray, gt: Dict[str, Any] ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ Extract arrays of I, U and V values at given points as numpy arrays given the data mode stored in self._dpDataMode """ if self._dpDataMode == DensePoseDataMode.IUV_DT: # estimated labels and UV (default) ipoints = densepose_data[0, py, px] upoints = densepose_data[1, py, px] / 255.0 # convert from uint8 by /255. vpoints = densepose_data[2, py, px] / 255.0 elif self._dpDataMode == DensePoseDataMode.IUV_GT: # ground truth ipoints = np.array(gt["dp_I"]) upoints = np.array(gt["dp_U"]) vpoints = np.array(gt["dp_V"]) elif self._dpDataMode == DensePoseDataMode.I_GT_UV_0: # ground truth labels, UV = 0 ipoints = np.array(gt["dp_I"]) upoints = upoints * 0.0 vpoints = vpoints * 0.0 elif self._dpDataMode == DensePoseDataMode.I_GT_UV_DT: # ground truth labels, estimated UV ipoints = np.array(gt["dp_I"]) upoints = densepose_data[1, py, px] / 255.0 # convert from uint8 by /255. vpoints = densepose_data[2, py, px] / 255.0 elif self._dpDataMode == DensePoseDataMode.I_DT_UV_0: # estimated labels, UV = 0 ipoints = densepose_data[0, py, px] upoints = upoints * 0.0 vpoints = vpoints * 0.0 else: raise ValueError(f"Unknown data mode: {self._dpDataMode}") return ipoints, upoints, vpoints def computeOgps_single_pair(self, dt, gt, py, px, pt_mask): if "densepose" in dt: ipoints, upoints, vpoints = self.extract_iuv_from_quantized(dt, gt, py, px, pt_mask) return self.computeOgps_single_pair_iuv(dt, gt, ipoints, upoints, vpoints) elif "u" in dt: ipoints, upoints, vpoints = self.extract_iuv_from_raw(dt, gt, py, px, pt_mask) return self.computeOgps_single_pair_iuv(dt, gt, ipoints, upoints, vpoints) elif "record_id" in dt: assert ( self.multi_storage is not None ), f"Storage record id encountered in detection {dt}, but no storage provided!" record = self.multi_storage.get(dt["rank"], dt["record_id"]) record["bbox"] = dt["bbox"] if "u" in record: ipoints, upoints, vpoints = self.extract_iuv_from_raw(record, gt, py, px, pt_mask) return self.computeOgps_single_pair_iuv(dt, gt, ipoints, upoints, vpoints) elif "embedding" in record: return self.computeOgps_single_pair_cse( dt, gt, py, px, pt_mask, record["coarse_segm"], record["embedding"], record["bbox"], ) else: raise Exception(f"Unknown record format: {record}") elif "embedding" in dt: return self.computeOgps_single_pair_cse( dt, gt, py, px, pt_mask, dt["coarse_segm"], dt["embedding"], dt["bbox"] ) raise Exception(f"Unknown detection format: {dt}") def extract_iuv_from_quantized(self, dt, gt, py, px, pt_mask): densepose_results_quantized = dt["densepose"] ipoints, upoints, vpoints = self._extract_iuv( densepose_results_quantized.labels_uv_uint8.numpy(), py, px, gt ) ipoints[pt_mask == -1] = 0 return ipoints, upoints, vpoints def extract_iuv_from_raw(self, dt, gt, py, px, pt_mask): labels_dt = resample_fine_and_coarse_segm_tensors_to_bbox( dt["fine_segm"].unsqueeze(0), dt["coarse_segm"].unsqueeze(0), dt["bbox"], ) uv = resample_uv_tensors_to_bbox( dt["u"].unsqueeze(0), dt["v"].unsqueeze(0), labels_dt.squeeze(0), dt["bbox"] ) labels_uv_uint8 = torch.cat((labels_dt.byte(), (uv * 255).clamp(0, 255).byte())) ipoints, upoints, vpoints = self._extract_iuv(labels_uv_uint8.numpy(), py, px, gt) ipoints[pt_mask == -1] = 0 return ipoints, upoints, vpoints def computeOgps_single_pair_iuv(self, dt, gt, ipoints, upoints, vpoints): cVertsGT, ClosestVertsGTTransformed = self.findAllClosestVertsGT(gt) cVerts = self.findAllClosestVertsUV(upoints, vpoints, ipoints) # Get pairwise geodesic distances between gt and estimated mesh points. dist = self.getDistancesUV(ClosestVertsGTTransformed, cVerts) # Compute the Ogps measure. # Find the mean geodesic normalization distance for # each GT point, based on which part it is on. Current_Mean_Distances = self.Mean_Distances[ self.CoarseParts[self.Part_ids[cVertsGT[cVertsGT > 0].astype(int) - 1]] ] return dist, Current_Mean_Distances def computeOgps_single_pair_cse( self, dt, gt, py, px, pt_mask, coarse_segm, embedding, bbox_xywh_abs ): # 0-based mesh vertex indices cVertsGT = torch.as_tensor(gt["dp_vertex"], dtype=torch.int64) # label for each pixel of the bbox, [H, W] tensor of long labels_dt = resample_coarse_segm_tensor_to_bbox( coarse_segm.unsqueeze(0), bbox_xywh_abs ).squeeze(0) x, y, w, h = bbox_xywh_abs # embedding for each pixel of the bbox, [D, H, W] tensor of float32 embedding = F.interpolate( embedding.unsqueeze(0), (int(h), int(w)), mode="bilinear", align_corners=False ).squeeze(0) # valid locations py, px py_pt = torch.from_numpy(py[pt_mask > -1]) px_pt = torch.from_numpy(px[pt_mask > -1]) cVerts = torch.ones_like(cVertsGT) * -1 cVerts[pt_mask > -1] = self.findClosestVertsCse( embedding, py_pt, px_pt, labels_dt, gt["ref_model"] ) # Get pairwise geodesic distances between gt and estimated mesh points. dist = self.getDistancesCse(cVertsGT, cVerts, gt["ref_model"]) # normalize distances if (gt["ref_model"] == "smpl_27554") and ("dp_I" in gt): Current_Mean_Distances = self.Mean_Distances[ self.CoarseParts[np.array(gt["dp_I"], dtype=int)] ] else: Current_Mean_Distances = 0.255 return dist, Current_Mean_Distances def computeOgps(self, imgId, catId): p = self.params # dimension here should be Nxm g = self._gts[imgId, catId] d = self._dts[imgId, catId] inds = np.argsort([-d_["score"] for d_ in d], kind="mergesort") d = [d[i] for i in inds] if len(d) > p.maxDets[-1]: d = d[0 : p.maxDets[-1]] # if len(gts) == 0 and len(dts) == 0: if len(g) == 0 or len(d) == 0: return [] ious = np.zeros((len(d), len(g))) # compute opgs between each detection and ground truth object # sigma = self.sigma #0.255 # dist = 0.3m corresponds to ogps = 0.5 # 1 # dist = 0.3m corresponds to ogps = 0.96 # 1.45 # dist = 1.7m (person height) corresponds to ogps = 0.5) for j, gt in enumerate(g): if not gt["ignore"]: g_ = gt["bbox"] for i, dt in enumerate(d): # dy = int(dt["bbox"][3]) dx = int(dt["bbox"][2]) dp_x = np.array(gt["dp_x"]) * g_[2] / 255.0 dp_y = np.array(gt["dp_y"]) * g_[3] / 255.0 py = (dp_y + g_[1] - dt["bbox"][1]).astype(np.int) px = (dp_x + g_[0] - dt["bbox"][0]).astype(np.int) # pts = np.zeros(len(px)) pts[px >= dx] = -1 pts[py >= dy] = -1 pts[px < 0] = -1 pts[py < 0] = -1 if len(pts) < 1: ogps = 0.0 elif np.max(pts) == -1: ogps = 0.0 else: px[pts == -1] = 0 py[pts == -1] = 0 dists_between_matches, dist_norm_coeffs = self.computeOgps_single_pair( dt, gt, py, px, pts ) # Compute gps ogps_values = np.exp( -(dists_between_matches**2) / (2 * (dist_norm_coeffs**2)) ) # ogps = np.mean(ogps_values) if len(ogps_values) > 0 else 0.0 ious[i, j] = ogps gbb = [gt["bbox"] for gt in g] dbb = [dt["bbox"] for dt in d] # compute iou between each dt and gt region iscrowd = [int(o.get("iscrowd", 0)) for o in g] ious_bb = maskUtils.iou(dbb, gbb, iscrowd) return ious, ious_bb def evaluateImg(self, imgId, catId, aRng, maxDet): """ perform evaluation for single category and image :return: dict (single image results) """ p = self.params if p.useCats: gt = self._gts[imgId, catId] dt = self._dts[imgId, catId] else: gt = [_ for cId in p.catIds for _ in self._gts[imgId, cId]] dt = [_ for cId in p.catIds for _ in self._dts[imgId, cId]] if len(gt) == 0 and len(dt) == 0: return None for g in gt: # g['_ignore'] = g['ignore'] if g["ignore"] or (g["area"] < aRng[0] or g["area"] > aRng[1]): g["_ignore"] = True else: g["_ignore"] = False # sort dt highest score first, sort gt ignore last gtind = np.argsort([g["_ignore"] for g in gt], kind="mergesort") gt = [gt[i] for i in gtind] dtind = np.argsort([-d["score"] for d in dt], kind="mergesort") dt = [dt[i] for i in dtind[0:maxDet]] iscrowd = [int(o.get("iscrowd", 0)) for o in gt] # load computed ious if p.iouType == "densepose": # print('Checking the length', len(self.ious[imgId, catId])) # if len(self.ious[imgId, catId]) == 0: # print(self.ious[imgId, catId]) ious = ( self.ious[imgId, catId][0][:, gtind] if len(self.ious[imgId, catId]) > 0 else self.ious[imgId, catId] ) ioubs = ( self.ious[imgId, catId][1][:, gtind] if len(self.ious[imgId, catId]) > 0 else self.ious[imgId, catId] ) if self._dpEvalMode in {DensePoseEvalMode.GPSM, DensePoseEvalMode.IOU}: iousM = ( self.real_ious[imgId, catId][:, gtind] if len(self.real_ious[imgId, catId]) > 0 else self.real_ious[imgId, catId] ) else: ious = ( self.ious[imgId, catId][:, gtind] if len(self.ious[imgId, catId]) > 0 else self.ious[imgId, catId] ) T = len(p.iouThrs) G = len(gt) D = len(dt) gtm = np.zeros((T, G)) dtm = np.zeros((T, D)) gtIg = np.array([g["_ignore"] for g in gt]) dtIg = np.zeros((T, D)) if np.all(gtIg) and p.iouType == "densepose": dtIg = np.logical_or(dtIg, True) if len(ious) > 0: # and not p.iouType == 'densepose': for tind, t in enumerate(p.iouThrs): for dind, d in enumerate(dt): # information about best match so far (m=-1 -> unmatched) iou = min([t, 1 - 1e-10]) m = -1 for gind, _g in enumerate(gt): # if this gt already matched, and not a crowd, continue if gtm[tind, gind] > 0 and not iscrowd[gind]: continue # if dt matched to reg gt, and on ignore gt, stop if m > -1 and gtIg[m] == 0 and gtIg[gind] == 1: break if p.iouType == "densepose": if self._dpEvalMode == DensePoseEvalMode.GPSM: new_iou = np.sqrt(iousM[dind, gind] * ious[dind, gind]) elif self._dpEvalMode == DensePoseEvalMode.IOU: new_iou = iousM[dind, gind] elif self._dpEvalMode == DensePoseEvalMode.GPS: new_iou = ious[dind, gind] else: new_iou = ious[dind, gind] if new_iou < iou: continue if new_iou == 0.0: continue # if match successful and best so far, store appropriately iou = new_iou m = gind # if match made store id of match for both dt and gt if m == -1: continue dtIg[tind, dind] = gtIg[m] dtm[tind, dind] = gt[m]["id"] gtm[tind, m] = d["id"] if p.iouType == "densepose": if not len(ioubs) == 0: for dind, d in enumerate(dt): # information about best match so far (m=-1 -> unmatched) if dtm[tind, dind] == 0: ioub = 0.8 m = -1 for gind, _g in enumerate(gt): # if this gt already matched, and not a crowd, continue if gtm[tind, gind] > 0 and not iscrowd[gind]: continue # continue to next gt unless better match made if ioubs[dind, gind] < ioub: continue # if match successful and best so far, store appropriately ioub = ioubs[dind, gind] m = gind # if match made store id of match for both dt and gt if m > -1: dtIg[:, dind] = gtIg[m] if gtIg[m]: dtm[tind, dind] = gt[m]["id"] gtm[tind, m] = d["id"] # set unmatched detections outside of area range to ignore a = np.array([d["area"] < aRng[0] or d["area"] > aRng[1] for d in dt]).reshape((1, len(dt))) dtIg = np.logical_or(dtIg, np.logical_and(dtm == 0, np.repeat(a, T, 0))) # store results for given image and category # print('Done with the function', len(self.ious[imgId, catId])) return { "image_id": imgId, "category_id": catId, "aRng": aRng, "maxDet": maxDet, "dtIds": [d["id"] for d in dt], "gtIds": [g["id"] for g in gt], "dtMatches": dtm, "gtMatches": gtm, "dtScores": [d["score"] for d in dt], "gtIgnore": gtIg, "dtIgnore": dtIg, } def accumulate(self, p=None): """ Accumulate per image evaluation results and store the result in self.eval :param p: input params for evaluation :return: None """ logger.info("Accumulating evaluation results...") tic = time.time() if not self.evalImgs: logger.info("Please run evaluate() first") # allows input customized parameters if p is None: p = self.params p.catIds = p.catIds if p.useCats == 1 else [-1] T = len(p.iouThrs) R = len(p.recThrs) K = len(p.catIds) if p.useCats else 1 A = len(p.areaRng) M = len(p.maxDets) precision = -(np.ones((T, R, K, A, M))) # -1 for the precision of absent categories recall = -(np.ones((T, K, A, M))) # create dictionary for future indexing logger.info("Categories: {}".format(p.catIds)) _pe = self._paramsEval catIds = _pe.catIds if _pe.useCats else [-1] setK = set(catIds) setA = set(map(tuple, _pe.areaRng)) setM = set(_pe.maxDets) setI = set(_pe.imgIds) # get inds to evaluate k_list = [n for n, k in enumerate(p.catIds) if k in setK] m_list = [m for n, m in enumerate(p.maxDets) if m in setM] a_list = [n for n, a in enumerate(map(lambda x: tuple(x), p.areaRng)) if a in setA] i_list = [n for n, i in enumerate(p.imgIds) if i in setI] I0 = len(_pe.imgIds) A0 = len(_pe.areaRng) # retrieve E at each category, area range, and max number of detections for k, k0 in enumerate(k_list): Nk = k0 * A0 * I0 for a, a0 in enumerate(a_list): Na = a0 * I0 for m, maxDet in enumerate(m_list): E = [self.evalImgs[Nk + Na + i] for i in i_list] E = [e for e in E if e is not None] if len(E) == 0: continue dtScores = np.concatenate([e["dtScores"][0:maxDet] for e in E]) # different sorting method generates slightly different results. # mergesort is used to be consistent as Matlab implementation. inds = np.argsort(-dtScores, kind="mergesort") dtm = np.concatenate([e["dtMatches"][:, 0:maxDet] for e in E], axis=1)[:, inds] dtIg = np.concatenate([e["dtIgnore"][:, 0:maxDet] for e in E], axis=1)[:, inds] gtIg = np.concatenate([e["gtIgnore"] for e in E]) npig = np.count_nonzero(gtIg == 0) if npig == 0: continue tps = np.logical_and(dtm, np.logical_not(dtIg)) fps = np.logical_and(np.logical_not(dtm), np.logical_not(dtIg)) tp_sum = np.cumsum(tps, axis=1).astype(dtype=np.float) fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float) for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)): tp = np.array(tp) fp = np.array(fp) nd = len(tp) rc = tp / npig pr = tp / (fp + tp + np.spacing(1)) q = np.zeros((R,)) if nd: recall[t, k, a, m] = rc[-1] else: recall[t, k, a, m] = 0 # numpy is slow without cython optimization for accessing elements # use python array gets significant speed improvement pr = pr.tolist() q = q.tolist() for i in range(nd - 1, 0, -1): if pr[i] > pr[i - 1]: pr[i - 1] = pr[i] inds = np.searchsorted(rc, p.recThrs, side="left") try: for ri, pi in enumerate(inds): q[ri] = pr[pi] except Exception: pass precision[t, :, k, a, m] = np.array(q) logger.info( "Final: max precision {}, min precision {}".format(np.max(precision), np.min(precision)) ) self.eval = { "params": p, "counts": [T, R, K, A, M], "date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "precision": precision, "recall": recall, } toc = time.time() logger.info("DONE (t={:0.2f}s).".format(toc - tic)) def summarize(self): """ Compute and display summary metrics for evaluation results. Note this function can *only* be applied on the default parameter setting """ def _summarize(ap=1, iouThr=None, areaRng="all", maxDets=100): p = self.params iStr = " {:<18} {} @[ {}={:<9} | area={:>6s} | maxDets={:>3d} ] = {:0.3f}" titleStr = "Average Precision" if ap == 1 else "Average Recall" typeStr = "(AP)" if ap == 1 else "(AR)" measure = "IoU" if self.params.iouType == "keypoints": measure = "OKS" elif self.params.iouType == "densepose": measure = "OGPS" iouStr = ( "{:0.2f}:{:0.2f}".format(p.iouThrs[0], p.iouThrs[-1]) if iouThr is None else "{:0.2f}".format(iouThr) ) aind = [i for i, aRng in enumerate(p.areaRngLbl) if aRng == areaRng] mind = [i for i, mDet in enumerate(p.maxDets) if mDet == maxDets] if ap == 1: # dimension of precision: [TxRxKxAxM] s = self.eval["precision"] # IoU if iouThr is not None: t = np.where(np.abs(iouThr - p.iouThrs) < 0.001)[0] s = s[t] s = s[:, :, :, aind, mind] else: # dimension of recall: [TxKxAxM] s = self.eval["recall"] if iouThr is not None: t = np.where(np.abs(iouThr - p.iouThrs) < 0.001)[0] s = s[t] s = s[:, :, aind, mind] if len(s[s > -1]) == 0: mean_s = -1 else: mean_s = np.mean(s[s > -1]) logger.info(iStr.format(titleStr, typeStr, measure, iouStr, areaRng, maxDets, mean_s)) return mean_s def _summarizeDets(): stats = np.zeros((12,)) stats[0] = _summarize(1) stats[1] = _summarize(1, iouThr=0.5, maxDets=self.params.maxDets[2]) stats[2] = _summarize(1, iouThr=0.75, maxDets=self.params.maxDets[2]) stats[3] = _summarize(1, areaRng="small", maxDets=self.params.maxDets[2]) stats[4] = _summarize(1, areaRng="medium", maxDets=self.params.maxDets[2]) stats[5] = _summarize(1, areaRng="large", maxDets=self.params.maxDets[2]) stats[6] = _summarize(0, maxDets=self.params.maxDets[0]) stats[7] = _summarize(0, maxDets=self.params.maxDets[1]) stats[8] = _summarize(0, maxDets=self.params.maxDets[2]) stats[9] = _summarize(0, areaRng="small", maxDets=self.params.maxDets[2]) stats[10] = _summarize(0, areaRng="medium", maxDets=self.params.maxDets[2]) stats[11] = _summarize(0, areaRng="large", maxDets=self.params.maxDets[2]) return stats def _summarizeKps(): stats = np.zeros((10,)) stats[0] = _summarize(1, maxDets=20) stats[1] = _summarize(1, maxDets=20, iouThr=0.5) stats[2] = _summarize(1, maxDets=20, iouThr=0.75) stats[3] = _summarize(1, maxDets=20, areaRng="medium") stats[4] = _summarize(1, maxDets=20, areaRng="large") stats[5] = _summarize(0, maxDets=20) stats[6] = _summarize(0, maxDets=20, iouThr=0.5) stats[7] = _summarize(0, maxDets=20, iouThr=0.75) stats[8] = _summarize(0, maxDets=20, areaRng="medium") stats[9] = _summarize(0, maxDets=20, areaRng="large") return stats def _summarizeUvs(): stats = [_summarize(1, maxDets=self.params.maxDets[0])] min_threshold = self.params.iouThrs.min() if min_threshold <= 0.201: stats += [_summarize(1, maxDets=self.params.maxDets[0], iouThr=0.2)] if min_threshold <= 0.301: stats += [_summarize(1, maxDets=self.params.maxDets[0], iouThr=0.3)] if min_threshold <= 0.401: stats += [_summarize(1, maxDets=self.params.maxDets[0], iouThr=0.4)] stats += [ _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.5), _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.75), _summarize(1, maxDets=self.params.maxDets[0], areaRng="medium"), _summarize(1, maxDets=self.params.maxDets[0], areaRng="large"), _summarize(0, maxDets=self.params.maxDets[0]), _summarize(0, maxDets=self.params.maxDets[0], iouThr=0.5), _summarize(0, maxDets=self.params.maxDets[0], iouThr=0.75), _summarize(0, maxDets=self.params.maxDets[0], areaRng="medium"), _summarize(0, maxDets=self.params.maxDets[0], areaRng="large"), ] return np.array(stats) def _summarizeUvsOld(): stats = np.zeros((18,)) stats[0] = _summarize(1, maxDets=self.params.maxDets[0]) stats[1] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.5) stats[2] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.55) stats[3] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.60) stats[4] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.65) stats[5] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.70) stats[6] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.75) stats[7] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.80) stats[8] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.85) stats[9] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.90) stats[10] = _summarize(1, maxDets=self.params.maxDets[0], iouThr=0.95) stats[11] = _summarize(1, maxDets=self.params.maxDets[0], areaRng="medium") stats[12] = _summarize(1, maxDets=self.params.maxDets[0], areaRng="large") stats[13] = _summarize(0, maxDets=self.params.maxDets[0]) stats[14] = _summarize(0, maxDets=self.params.maxDets[0], iouThr=0.5) stats[15] = _summarize(0, maxDets=self.params.maxDets[0], iouThr=0.75) stats[16] = _summarize(0, maxDets=self.params.maxDets[0], areaRng="medium") stats[17] = _summarize(0, maxDets=self.params.maxDets[0], areaRng="large") return stats if not self.eval: raise Exception("Please run accumulate() first") iouType = self.params.iouType if iouType in ["segm", "bbox"]: summarize = _summarizeDets elif iouType in ["keypoints"]: summarize = _summarizeKps elif iouType in ["densepose"]: summarize = _summarizeUvs self.stats = summarize() def __str__(self): self.summarize() # ================ functions for dense pose ============================== def findAllClosestVertsUV(self, U_points, V_points, Index_points): ClosestVerts = np.ones(Index_points.shape) * -1 for i in np.arange(24): # if (i + 1) in Index_points: UVs = np.array( [U_points[Index_points == (i + 1)], V_points[Index_points == (i + 1)]] ) Current_Part_UVs = self.Part_UVs[i] Current_Part_ClosestVertInds = self.Part_ClosestVertInds[i] D = ssd.cdist(Current_Part_UVs.transpose(), UVs.transpose()).squeeze() ClosestVerts[Index_points == (i + 1)] = Current_Part_ClosestVertInds[ np.argmin(D, axis=0) ] ClosestVertsTransformed = self.PDIST_transform[ClosestVerts.astype(int) - 1] ClosestVertsTransformed[ClosestVerts < 0] = 0 return ClosestVertsTransformed def findClosestVertsCse(self, embedding, py, px, mask, mesh_name): mesh_vertex_embeddings = self.embedder(mesh_name) pixel_embeddings = embedding[:, py, px].t().to(device="cuda") mask_vals = mask[py, px] edm = squared_euclidean_distance_matrix(pixel_embeddings, mesh_vertex_embeddings) vertex_indices = edm.argmin(dim=1).cpu() vertex_indices[mask_vals <= 0] = -1 return vertex_indices def findAllClosestVertsGT(self, gt): # I_gt = np.array(gt["dp_I"]) U_gt = np.array(gt["dp_U"]) V_gt = np.array(gt["dp_V"]) # # print(I_gt) # ClosestVertsGT = np.ones(I_gt.shape) * -1 for i in np.arange(24): if (i + 1) in I_gt: UVs = np.array([U_gt[I_gt == (i + 1)], V_gt[I_gt == (i + 1)]]) Current_Part_UVs = self.Part_UVs[i] Current_Part_ClosestVertInds = self.Part_ClosestVertInds[i] D = ssd.cdist(Current_Part_UVs.transpose(), UVs.transpose()).squeeze() ClosestVertsGT[I_gt == (i + 1)] = Current_Part_ClosestVertInds[np.argmin(D, axis=0)] # ClosestVertsGTTransformed = self.PDIST_transform[ClosestVertsGT.astype(int) - 1] ClosestVertsGTTransformed[ClosestVertsGT < 0] = 0 return ClosestVertsGT, ClosestVertsGTTransformed def getDistancesCse(self, cVertsGT, cVerts, mesh_name): geodists_vertices = torch.ones_like(cVertsGT) * float("inf") selected = (cVertsGT >= 0) * (cVerts >= 0) mesh = create_mesh(mesh_name, "cpu") geodists_vertices[selected] = mesh.geodists[cVertsGT[selected], cVerts[selected]] return geodists_vertices.numpy() def getDistancesUV(self, cVertsGT, cVerts): # n = 27554 dists = [] for d in range(len(cVertsGT)): if cVertsGT[d] > 0: if cVerts[d] > 0: i = cVertsGT[d] - 1 j = cVerts[d] - 1 if j == i: dists.append(0) elif j > i: ccc = i i = j j = ccc i = n - i - 1 j = n - j - 1 k = (n * (n - 1) / 2) - (n - i) * ((n - i) - 1) / 2 + j - i - 1 k = (n * n - n) / 2 - k - 1 dists.append(self.Pdist_matrix[int(k)][0]) else: i = n - i - 1 j = n - j - 1 k = (n * (n - 1) / 2) - (n - i) * ((n - i) - 1) / 2 + j - i - 1 k = (n * n - n) / 2 - k - 1 dists.append(self.Pdist_matrix[int(k)][0]) else: dists.append(np.inf) return np.atleast_1d(np.array(dists).squeeze()) class Params: """ Params for coco evaluation api """ def setDetParams(self): self.imgIds = [] self.catIds = [] # np.arange causes trouble. the data point on arange is slightly larger than the true value self.iouThrs = np.linspace(0.5, 0.95, int(np.round((0.95 - 0.5) / 0.05)) + 1, endpoint=True) self.recThrs = np.linspace(0.0, 1.00, int(np.round((1.00 - 0.0) / 0.01)) + 1, endpoint=True) self.maxDets = [1, 10, 100] self.areaRng = [ [0**2, 1e5**2], [0**2, 32**2], [32**2, 96**2], [96**2, 1e5**2], ] self.areaRngLbl = ["all", "small", "medium", "large"] self.useCats = 1 def setKpParams(self): self.imgIds = [] self.catIds = [] # np.arange causes trouble. the data point on arange is slightly larger than the true value self.iouThrs = np.linspace(0.5, 0.95, np.round((0.95 - 0.5) / 0.05) + 1, endpoint=True) self.recThrs = np.linspace(0.0, 1.00, np.round((1.00 - 0.0) / 0.01) + 1, endpoint=True) self.maxDets = [20] self.areaRng = [[0**2, 1e5**2], [32**2, 96**2], [96**2, 1e5**2]] self.areaRngLbl = ["all", "medium", "large"] self.useCats = 1 def setUvParams(self): self.imgIds = [] self.catIds = [] self.iouThrs = np.linspace(0.5, 0.95, int(np.round((0.95 - 0.5) / 0.05)) + 1, endpoint=True) self.recThrs = np.linspace(0.0, 1.00, int(np.round((1.00 - 0.0) / 0.01)) + 1, endpoint=True) self.maxDets = [20] self.areaRng = [[0**2, 1e5**2], [32**2, 96**2], [96**2, 1e5**2]] self.areaRngLbl = ["all", "medium", "large"] self.useCats = 1 def __init__(self, iouType="segm"): if iouType == "segm" or iouType == "bbox": self.setDetParams() elif iouType == "keypoints": self.setKpParams() elif iouType == "densepose": self.setUvParams() else: raise Exception("iouType not supported") self.iouType = iouType # useSegm is deprecated self.useSegm = None ================================================ FILE: detectron2/projects/DensePose/densepose/evaluation/evaluator.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import contextlib import copy import io import itertools import logging import numpy as np import os from collections import OrderedDict from typing import Dict, Iterable, List, Optional import pycocotools.mask as mask_utils import torch from pycocotools.coco import COCO from tabulate import tabulate from detectron2.config import CfgNode from detectron2.data import MetadataCatalog from detectron2.evaluation import DatasetEvaluator from detectron2.structures import BoxMode from detectron2.utils.comm import gather, get_rank, is_main_process, synchronize from detectron2.utils.file_io import PathManager from detectron2.utils.logger import create_small_table from densepose.converters import ToChartResultConverter, ToMaskConverter from densepose.data.datasets.coco import maybe_filter_and_map_categories_cocoapi from densepose.structures import ( DensePoseChartPredictorOutput, DensePoseEmbeddingPredictorOutput, quantize_densepose_chart_result, ) from .densepose_coco_evaluation import DensePoseCocoEval, DensePoseEvalMode from .mesh_alignment_evaluator import MeshAlignmentEvaluator from .tensor_storage import ( SingleProcessFileTensorStorage, SingleProcessRamTensorStorage, SingleProcessTensorStorage, SizeData, storage_gather, ) class DensePoseCOCOEvaluator(DatasetEvaluator): def __init__( self, dataset_name, distributed, output_dir=None, evaluator_type: str = "iuv", min_iou_threshold: float = 0.5, storage: Optional[SingleProcessTensorStorage] = None, embedder=None, should_evaluate_mesh_alignment: bool = False, mesh_alignment_mesh_names: Optional[List[str]] = None, ): self._embedder = embedder self._distributed = distributed self._output_dir = output_dir self._evaluator_type = evaluator_type self._storage = storage self._should_evaluate_mesh_alignment = should_evaluate_mesh_alignment assert not ( should_evaluate_mesh_alignment and embedder is None ), "Mesh alignment evaluation is activated, but no vertex embedder provided!" if should_evaluate_mesh_alignment: self._mesh_alignment_evaluator = MeshAlignmentEvaluator( embedder, mesh_alignment_mesh_names, ) self._cpu_device = torch.device("cpu") self._logger = logging.getLogger(__name__) self._metadata = MetadataCatalog.get(dataset_name) self._min_threshold = min_iou_threshold json_file = PathManager.get_local_path(self._metadata.json_file) with contextlib.redirect_stdout(io.StringIO()): self._coco_api = COCO(json_file) maybe_filter_and_map_categories_cocoapi(dataset_name, self._coco_api) def reset(self): self._predictions = [] def process(self, inputs, outputs): """ Args: inputs: the inputs to a COCO model (e.g., GeneralizedRCNN). It is a list of dict. Each dict corresponds to an image and contains keys like "height", "width", "file_name", "image_id". outputs: the outputs of a COCO model. It is a list of dicts with key "instances" that contains :class:`Instances`. The :class:`Instances` object needs to have `densepose` field. """ for input, output in zip(inputs, outputs): instances = output["instances"].to(self._cpu_device) if not instances.has("pred_densepose"): continue prediction_list = prediction_to_dict( instances, input["image_id"], self._embedder, self._metadata.class_to_mesh_name, self._storage is not None, ) if self._storage is not None: for prediction_dict in prediction_list: dict_to_store = {} for field_name in self._storage.data_schema: dict_to_store[field_name] = prediction_dict[field_name] record_id = self._storage.put(dict_to_store) prediction_dict["record_id"] = record_id prediction_dict["rank"] = get_rank() for field_name in self._storage.data_schema: del prediction_dict[field_name] self._predictions.extend(prediction_list) def evaluate(self, img_ids=None): if self._distributed: synchronize() predictions = gather(self._predictions) predictions = list(itertools.chain(*predictions)) else: predictions = self._predictions multi_storage = storage_gather(self._storage) if self._storage is not None else None if not is_main_process(): return return copy.deepcopy(self._eval_predictions(predictions, multi_storage, img_ids)) def _eval_predictions(self, predictions, multi_storage=None, img_ids=None): """ Evaluate predictions on densepose. Return results with the metrics of the tasks. """ self._logger.info("Preparing results for COCO format ...") if self._output_dir: PathManager.mkdirs(self._output_dir) file_path = os.path.join(self._output_dir, "coco_densepose_predictions.pth") with PathManager.open(file_path, "wb") as f: torch.save(predictions, f) self._logger.info("Evaluating predictions ...") res = OrderedDict() results_gps, results_gpsm, results_segm = _evaluate_predictions_on_coco( self._coco_api, predictions, multi_storage, self._embedder, class_names=self._metadata.get("thing_classes"), min_threshold=self._min_threshold, img_ids=img_ids, ) res["densepose_gps"] = results_gps res["densepose_gpsm"] = results_gpsm res["densepose_segm"] = results_segm if self._should_evaluate_mesh_alignment: res["densepose_mesh_alignment"] = self._evaluate_mesh_alignment() return res def _evaluate_mesh_alignment(self): self._logger.info("Mesh alignment evaluation ...") mean_ge, mean_gps, per_mesh_metrics = self._mesh_alignment_evaluator.evaluate() results = { "GE": mean_ge * 100, "GPS": mean_gps * 100, } mesh_names = set() for metric_name in per_mesh_metrics: for mesh_name, value in per_mesh_metrics[metric_name].items(): results[f"{metric_name}-{mesh_name}"] = value * 100 mesh_names.add(mesh_name) self._print_mesh_alignment_results(results, mesh_names) return results def _print_mesh_alignment_results(self, results: Dict[str, float], mesh_names: Iterable[str]): self._logger.info("Evaluation results for densepose, mesh alignment:") self._logger.info(f'| {"Mesh":13s} | {"GErr":7s} | {"GPS":7s} |') self._logger.info("| :-----------: | :-----: | :-----: |") for mesh_name in mesh_names: ge_key = f"GE-{mesh_name}" ge_str = f"{results[ge_key]:.4f}" if ge_key in results else " " gps_key = f"GPS-{mesh_name}" gps_str = f"{results[gps_key]:.4f}" if gps_key in results else " " self._logger.info(f"| {mesh_name:13s} | {ge_str:7s} | {gps_str:7s} |") self._logger.info("| :-------------------------------: |") ge_key = "GE" ge_str = f"{results[ge_key]:.4f}" if ge_key in results else " " gps_key = "GPS" gps_str = f"{results[gps_key]:.4f}" if gps_key in results else " " self._logger.info(f'| {"MEAN":13s} | {ge_str:7s} | {gps_str:7s} |') def prediction_to_dict(instances, img_id, embedder, class_to_mesh_name, use_storage): """ Args: instances (Instances): the output of the model img_id (str): the image id in COCO Returns: list[dict]: the results in densepose evaluation format """ scores = instances.scores.tolist() classes = instances.pred_classes.tolist() raw_boxes_xywh = BoxMode.convert( instances.pred_boxes.tensor.clone(), BoxMode.XYXY_ABS, BoxMode.XYWH_ABS ) if isinstance(instances.pred_densepose, DensePoseEmbeddingPredictorOutput): results_densepose = densepose_cse_predictions_to_dict( instances, embedder, class_to_mesh_name, use_storage ) elif isinstance(instances.pred_densepose, DensePoseChartPredictorOutput): if not use_storage: results_densepose = densepose_chart_predictions_to_dict(instances) else: results_densepose = densepose_chart_predictions_to_storage_dict(instances) results = [] for k in range(len(instances)): result = { "image_id": img_id, "category_id": classes[k], "bbox": raw_boxes_xywh[k].tolist(), "score": scores[k], } results.append({**result, **results_densepose[k]}) return results def densepose_chart_predictions_to_dict(instances): segmentations = ToMaskConverter.convert( instances.pred_densepose, instances.pred_boxes, instances.image_size ) results = [] for k in range(len(instances)): densepose_results_quantized = quantize_densepose_chart_result( ToChartResultConverter.convert(instances.pred_densepose[k], instances.pred_boxes[k]) ) densepose_results_quantized.labels_uv_uint8 = ( densepose_results_quantized.labels_uv_uint8.cpu() ) segmentation = segmentations.tensor[k] segmentation_encoded = mask_utils.encode( np.require(segmentation.numpy(), dtype=np.uint8, requirements=["F"]) ) segmentation_encoded["counts"] = segmentation_encoded["counts"].decode("utf-8") result = { "densepose": densepose_results_quantized, "segmentation": segmentation_encoded, } results.append(result) return results def densepose_chart_predictions_to_storage_dict(instances): results = [] for k in range(len(instances)): densepose_predictor_output = instances.pred_densepose[k] result = { "coarse_segm": densepose_predictor_output.coarse_segm.squeeze(0).cpu(), "fine_segm": densepose_predictor_output.fine_segm.squeeze(0).cpu(), "u": densepose_predictor_output.u.squeeze(0).cpu(), "v": densepose_predictor_output.v.squeeze(0).cpu(), } results.append(result) return results def densepose_cse_predictions_to_dict(instances, embedder, class_to_mesh_name, use_storage): results = [] for k in range(len(instances)): cse = instances.pred_densepose[k] results.append( { "coarse_segm": cse.coarse_segm[0].cpu(), "embedding": cse.embedding[0].cpu(), } ) return results def _evaluate_predictions_on_coco( coco_gt, coco_results, multi_storage=None, embedder=None, class_names=None, min_threshold: float = 0.5, img_ids=None, ): logger = logging.getLogger(__name__) densepose_metrics = _get_densepose_metrics(min_threshold) if len(coco_results) == 0: # cocoapi does not handle empty results very well logger.warn("No predictions from the model! Set scores to -1") results_gps = {metric: -1 for metric in densepose_metrics} results_gpsm = {metric: -1 for metric in densepose_metrics} results_segm = {metric: -1 for metric in densepose_metrics} return results_gps, results_gpsm, results_segm coco_dt = coco_gt.loadRes(coco_results) results = [] for eval_mode_name in ["GPS", "GPSM", "IOU"]: eval_mode = getattr(DensePoseEvalMode, eval_mode_name) coco_eval = DensePoseCocoEval( coco_gt, coco_dt, "densepose", multi_storage, embedder, dpEvalMode=eval_mode ) result = _derive_results_from_coco_eval( coco_eval, eval_mode_name, densepose_metrics, class_names, min_threshold, img_ids ) results.append(result) return results def _get_densepose_metrics(min_threshold: float = 0.5): metrics = ["AP"] if min_threshold <= 0.201: metrics += ["AP20"] if min_threshold <= 0.301: metrics += ["AP30"] if min_threshold <= 0.401: metrics += ["AP40"] metrics.extend(["AP50", "AP75", "APm", "APl", "AR", "AR50", "AR75", "ARm", "ARl"]) return metrics def _derive_results_from_coco_eval( coco_eval, eval_mode_name, metrics, class_names, min_threshold: float, img_ids ): if img_ids is not None: coco_eval.params.imgIds = img_ids coco_eval.params.iouThrs = np.linspace( min_threshold, 0.95, int(np.round((0.95 - min_threshold) / 0.05)) + 1, endpoint=True ) coco_eval.evaluate() coco_eval.accumulate() coco_eval.summarize() results = {metric: float(coco_eval.stats[idx] * 100) for idx, metric in enumerate(metrics)} logger = logging.getLogger(__name__) logger.info( f"Evaluation results for densepose, {eval_mode_name} metric: \n" + create_small_table(results) ) if class_names is None or len(class_names) <= 1: return results # Compute per-category AP, the same way as it is done in D2 # (see detectron2/evaluation/coco_evaluation.py): precisions = coco_eval.eval["precision"] # precision has dims (iou, recall, cls, area range, max dets) assert len(class_names) == precisions.shape[2] results_per_category = [] for idx, name in enumerate(class_names): # area range index 0: all area ranges # max dets index -1: typically 100 per image precision = precisions[:, :, idx, 0, -1] precision = precision[precision > -1] ap = np.mean(precision) if precision.size else float("nan") results_per_category.append((f"{name}", float(ap * 100))) # tabulate it n_cols = min(6, len(results_per_category) * 2) results_flatten = list(itertools.chain(*results_per_category)) results_2d = itertools.zip_longest(*[results_flatten[i::n_cols] for i in range(n_cols)]) table = tabulate( results_2d, tablefmt="pipe", floatfmt=".3f", headers=["category", "AP"] * (n_cols // 2), numalign="left", ) logger.info(f"Per-category {eval_mode_name} AP: \n" + table) results.update({"AP-" + name: ap for name, ap in results_per_category}) return results def build_densepose_evaluator_storage(cfg: CfgNode, output_folder: str): storage_spec = cfg.DENSEPOSE_EVALUATION.STORAGE if storage_spec == "none": return None evaluator_type = cfg.DENSEPOSE_EVALUATION.TYPE # common output tensor sizes hout = cfg.MODEL.ROI_DENSEPOSE_HEAD.HEATMAP_SIZE wout = cfg.MODEL.ROI_DENSEPOSE_HEAD.HEATMAP_SIZE n_csc = cfg.MODEL.ROI_DENSEPOSE_HEAD.NUM_COARSE_SEGM_CHANNELS # specific output tensors if evaluator_type == "iuv": n_fsc = cfg.MODEL.ROI_DENSEPOSE_HEAD.NUM_PATCHES + 1 schema = { "coarse_segm": SizeData(dtype="float32", shape=(n_csc, hout, wout)), "fine_segm": SizeData(dtype="float32", shape=(n_fsc, hout, wout)), "u": SizeData(dtype="float32", shape=(n_fsc, hout, wout)), "v": SizeData(dtype="float32", shape=(n_fsc, hout, wout)), } elif evaluator_type == "cse": embed_size = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_SIZE schema = { "coarse_segm": SizeData(dtype="float32", shape=(n_csc, hout, wout)), "embedding": SizeData(dtype="float32", shape=(embed_size, hout, wout)), } else: raise ValueError(f"Unknown evaluator type: {evaluator_type}") # storage types if storage_spec == "ram": storage = SingleProcessRamTensorStorage(schema, io.BytesIO()) elif storage_spec == "file": fpath = os.path.join(output_folder, f"DensePoseEvaluatorStorage.{get_rank()}.bin") PathManager.mkdirs(output_folder) storage = SingleProcessFileTensorStorage(schema, fpath, "wb") else: raise ValueError(f"Unknown storage specification: {storage_spec}") return storage ================================================ FILE: detectron2/projects/DensePose/densepose/evaluation/mesh_alignment_evaluator.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import json import logging from typing import List, Optional import torch from torch import nn from detectron2.utils.file_io import PathManager from densepose.structures.mesh import create_mesh class MeshAlignmentEvaluator: """ Class for evaluation of 3D mesh alignment based on the learned vertex embeddings """ def __init__(self, embedder: nn.Module, mesh_names: Optional[List[str]]): self.embedder = embedder # use the provided mesh names if not None and not an empty list self.mesh_names = mesh_names if mesh_names else embedder.mesh_names self.logger = logging.getLogger(__name__) with PathManager.open( "https://dl.fbaipublicfiles.com/densepose/data/cse/mesh_keyvertices_v0.json", "r" ) as f: self.mesh_keyvertices = json.load(f) def evaluate(self): ge_per_mesh = {} gps_per_mesh = {} for mesh_name_1 in self.mesh_names: avg_errors = [] avg_gps = [] embeddings_1 = self.embedder(mesh_name_1) keyvertices_1 = self.mesh_keyvertices[mesh_name_1] keyvertex_names_1 = list(keyvertices_1.keys()) keyvertex_indices_1 = [keyvertices_1[name] for name in keyvertex_names_1] for mesh_name_2 in self.mesh_names: if mesh_name_1 == mesh_name_2: continue embeddings_2 = self.embedder(mesh_name_2) keyvertices_2 = self.mesh_keyvertices[mesh_name_2] sim_matrix_12 = embeddings_1[keyvertex_indices_1].mm(embeddings_2.T) vertices_2_matching_keyvertices_1 = sim_matrix_12.argmax(axis=1) mesh_2 = create_mesh(mesh_name_2, embeddings_2.device) geodists = mesh_2.geodists[ vertices_2_matching_keyvertices_1, [keyvertices_2[name] for name in keyvertex_names_1], ] Current_Mean_Distances = 0.255 gps = (-(geodists**2) / (2 * (Current_Mean_Distances**2))).exp() avg_errors.append(geodists.mean().item()) avg_gps.append(gps.mean().item()) ge_mean = torch.as_tensor(avg_errors).mean().item() gps_mean = torch.as_tensor(avg_gps).mean().item() ge_per_mesh[mesh_name_1] = ge_mean gps_per_mesh[mesh_name_1] = gps_mean ge_mean_global = torch.as_tensor(list(ge_per_mesh.values())).mean().item() gps_mean_global = torch.as_tensor(list(gps_per_mesh.values())).mean().item() per_mesh_metrics = { "GE": ge_per_mesh, "GPS": gps_per_mesh, } return ge_mean_global, gps_mean_global, per_mesh_metrics ================================================ FILE: detectron2/projects/DensePose/densepose/evaluation/tensor_storage.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import io import numpy as np import os from dataclasses import dataclass from functools import reduce from operator import mul from typing import BinaryIO, Dict, Optional, Tuple import torch from detectron2.utils.comm import gather, get_rank from detectron2.utils.file_io import PathManager @dataclass class SizeData: dtype: str shape: Tuple[int] def _calculate_record_field_size_b(data_schema: Dict[str, SizeData], field_name: str) -> int: schema = data_schema[field_name] element_size_b = np.dtype(schema.dtype).itemsize record_field_size_b = reduce(mul, schema.shape) * element_size_b return record_field_size_b def _calculate_record_size_b(data_schema: Dict[str, SizeData]) -> int: record_size_b = 0 for field_name in data_schema: record_field_size_b = _calculate_record_field_size_b(data_schema, field_name) record_size_b += record_field_size_b return record_size_b def _calculate_record_field_sizes_b(data_schema: Dict[str, SizeData]) -> Dict[str, int]: field_sizes_b = {} for field_name in data_schema: field_sizes_b[field_name] = _calculate_record_field_size_b(data_schema, field_name) return field_sizes_b class SingleProcessTensorStorage: """ Compact tensor storage to keep tensor data of predefined size and type. """ def __init__(self, data_schema: Dict[str, SizeData], storage_impl: BinaryIO): """ Construct tensor storage based on information on data shape and size. Internally uses numpy to interpret the type specification. The storage must support operations `seek(offset, whence=os.SEEK_SET)` and `read(size)` to be able to perform the `get` operation. The storage must support operation `write(bytes)` to be able to perform the `put` operation. Args: data_schema (dict: str -> SizeData): dictionary which maps tensor name to its size data (shape and data type), e.g. ``` { "coarse_segm": SizeData(dtype="float32", shape=(112, 112)), "embedding": SizeData(dtype="float32", shape=(16, 112, 112)), } ``` storage_impl (BinaryIO): io instance that handles file-like seek, read and write operations, e.g. a file handle or a memory buffer like io.BytesIO """ self.data_schema = data_schema self.record_size_b = _calculate_record_size_b(data_schema) self.record_field_sizes_b = _calculate_record_field_sizes_b(data_schema) self.storage_impl = storage_impl self.next_record_id = 0 def get(self, record_id: int) -> Dict[str, torch.Tensor]: """ Load tensors from the storage by record ID Args: record_id (int): Record ID, for which to load the data Return: dict: str -> tensor: tensor name mapped to tensor data, recorded under the provided ID """ self.storage_impl.seek(record_id * self.record_size_b, os.SEEK_SET) data_bytes = self.storage_impl.read(self.record_size_b) assert len(data_bytes) == self.record_size_b, ( f"Expected data size {self.record_size_b} B could not be read: " f"got {len(data_bytes)} B" ) record = {} cur_idx = 0 # it's important to read and write in the same order for field_name in sorted(self.data_schema): schema = self.data_schema[field_name] field_size_b = self.record_field_sizes_b[field_name] chunk = data_bytes[cur_idx : cur_idx + field_size_b] data_np = np.frombuffer( chunk, dtype=schema.dtype, count=reduce(mul, schema.shape) ).reshape(schema.shape) record[field_name] = torch.from_numpy(data_np) cur_idx += field_size_b return record def put(self, data: Dict[str, torch.Tensor]) -> int: """ Store tensors in the storage Args: data (dict: str -> tensor): data to store, a dictionary which maps tensor names into tensors; tensor shapes must match those specified in data schema. Return: int: record ID, under which the data is stored """ # it's important to read and write in the same order for field_name in sorted(self.data_schema): assert ( field_name in data ), f"Field '{field_name}' not present in data: data keys are {data.keys()}" value = data[field_name] assert value.shape == self.data_schema[field_name].shape, ( f"Mismatched tensor shapes for field '{field_name}': " f"expected {self.data_schema[field_name].shape}, got {value.shape}" ) data_bytes = value.cpu().numpy().tobytes() assert len(data_bytes) == self.record_field_sizes_b[field_name], ( f"Expected field {field_name} to be of size " f"{self.record_field_sizes_b[field_name]} B, got {len(data_bytes)} B" ) self.storage_impl.write(data_bytes) record_id = self.next_record_id self.next_record_id += 1 return record_id class SingleProcessFileTensorStorage(SingleProcessTensorStorage): """ Implementation of a single process tensor storage which stores data in a file """ def __init__(self, data_schema: Dict[str, SizeData], fpath: str, mode: str): self.fpath = fpath assert "b" in mode, f"Tensor storage should be opened in binary mode, got '{mode}'" if "w" in mode: file_h = PathManager.open(fpath, mode) elif "r" in mode: local_fpath = PathManager.get_local_path(fpath) file_h = open(local_fpath, mode) else: raise ValueError(f"Unsupported file mode {mode}, supported modes: rb, wb") super().__init__(data_schema, file_h) # pyre-ignore[6] class SingleProcessRamTensorStorage(SingleProcessTensorStorage): """ Implementation of a single process tensor storage which stores data in RAM """ def __init__(self, data_schema: Dict[str, SizeData], buf: io.BytesIO): super().__init__(data_schema, buf) class MultiProcessTensorStorage: """ Representation of a set of tensor storages created by individual processes, allows to access those storages from a single owner process. The storages should either be shared or broadcasted to the owner process. The processes are identified by their rank, data is uniquely defined by the rank of the process and the record ID. """ def __init__(self, rank_to_storage: Dict[int, SingleProcessTensorStorage]): self.rank_to_storage = rank_to_storage def get(self, rank: int, record_id: int) -> Dict[str, torch.Tensor]: storage = self.rank_to_storage[rank] return storage.get(record_id) def put(self, rank: int, data: Dict[str, torch.Tensor]) -> int: storage = self.rank_to_storage[rank] return storage.put(data) class MultiProcessFileTensorStorage(MultiProcessTensorStorage): def __init__(self, data_schema: Dict[str, SizeData], rank_to_fpath: Dict[int, str], mode: str): rank_to_storage = { rank: SingleProcessFileTensorStorage(data_schema, fpath, mode) for rank, fpath in rank_to_fpath.items() } super().__init__(rank_to_storage) # pyre-ignore[6] class MultiProcessRamTensorStorage(MultiProcessTensorStorage): def __init__(self, data_schema: Dict[str, SizeData], rank_to_buffer: Dict[int, io.BytesIO]): rank_to_storage = { rank: SingleProcessRamTensorStorage(data_schema, buf) for rank, buf in rank_to_buffer.items() } super().__init__(rank_to_storage) # pyre-ignore[6] def _ram_storage_gather( storage: SingleProcessRamTensorStorage, dst_rank: int = 0 ) -> Optional[MultiProcessRamTensorStorage]: storage.storage_impl.seek(0, os.SEEK_SET) # TODO: overhead, pickling a bytes object, can just pass bytes in a tensor directly # see detectron2/utils.comm.py data_list = gather(storage.storage_impl.read(), dst=dst_rank) if get_rank() != dst_rank: return None rank_to_buffer = {i: io.BytesIO(data_list[i]) for i in range(len(data_list))} multiprocess_storage = MultiProcessRamTensorStorage(storage.data_schema, rank_to_buffer) return multiprocess_storage def _file_storage_gather( storage: SingleProcessFileTensorStorage, dst_rank: int = 0, mode: str = "rb", ) -> Optional[MultiProcessFileTensorStorage]: storage.storage_impl.close() fpath_list = gather(storage.fpath, dst=dst_rank) if get_rank() != dst_rank: return None rank_to_fpath = {i: fpath_list[i] for i in range(len(fpath_list))} return MultiProcessFileTensorStorage(storage.data_schema, rank_to_fpath, mode) def storage_gather( storage: SingleProcessTensorStorage, dst_rank: int = 0 ) -> Optional[MultiProcessTensorStorage]: if isinstance(storage, SingleProcessRamTensorStorage): return _ram_storage_gather(storage, dst_rank) elif isinstance(storage, SingleProcessFileTensorStorage): return _file_storage_gather(storage, dst_rank) raise Exception(f"Unsupported storage for gather operation: {storage}") ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .confidence import DensePoseConfidenceModelConfig, DensePoseUVConfidenceType from .filter import DensePoseDataFilter from .inference import densepose_inference from .utils import initialize_module_params from .build import ( build_densepose_data_filter, build_densepose_embedder, build_densepose_head, build_densepose_losses, build_densepose_predictor, ) ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/build.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Optional from torch import nn from detectron2.config import CfgNode from .cse.embedder import Embedder from .filter import DensePoseDataFilter def build_densepose_predictor(cfg: CfgNode, input_channels: int): """ Create an instance of DensePose predictor based on configuration options. Args: cfg (CfgNode): configuration options input_channels (int): input tensor size along the channel dimension Return: An instance of DensePose predictor """ from .predictors import DENSEPOSE_PREDICTOR_REGISTRY predictor_name = cfg.MODEL.ROI_DENSEPOSE_HEAD.PREDICTOR_NAME return DENSEPOSE_PREDICTOR_REGISTRY.get(predictor_name)(cfg, input_channels) def build_densepose_data_filter(cfg: CfgNode): """ Build DensePose data filter which selects data for training Args: cfg (CfgNode): configuration options Return: Callable: list(Tensor), list(Instances) -> list(Tensor), list(Instances) An instance of DensePose filter, which takes feature tensors and proposals as an input and returns filtered features and proposals """ dp_filter = DensePoseDataFilter(cfg) return dp_filter def build_densepose_head(cfg: CfgNode, input_channels: int): """ Build DensePose head based on configurations options Args: cfg (CfgNode): configuration options input_channels (int): input tensor size along the channel dimension Return: An instance of DensePose head """ from .roi_heads.registry import ROI_DENSEPOSE_HEAD_REGISTRY head_name = cfg.MODEL.ROI_DENSEPOSE_HEAD.NAME return ROI_DENSEPOSE_HEAD_REGISTRY.get(head_name)(cfg, input_channels) def build_densepose_losses(cfg: CfgNode): """ Build DensePose loss based on configurations options Args: cfg (CfgNode): configuration options Return: An instance of DensePose loss """ from .losses import DENSEPOSE_LOSS_REGISTRY loss_name = cfg.MODEL.ROI_DENSEPOSE_HEAD.LOSS_NAME return DENSEPOSE_LOSS_REGISTRY.get(loss_name)(cfg) def build_densepose_embedder(cfg: CfgNode) -> Optional[nn.Module]: """ Build embedder used to embed mesh vertices into an embedding space. Embedder contains sub-embedders, one for each mesh ID. Args: cfg (cfgNode): configuration options Return: Embedding module """ if cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDERS: return Embedder(cfg) return None ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/confidence.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from dataclasses import dataclass from enum import Enum from detectron2.config import CfgNode class DensePoseUVConfidenceType(Enum): """ Statistical model type for confidence learning, possible values: - "iid_iso": statistically independent identically distributed residuals with anisotropic covariance - "indep_aniso": statistically independent residuals with anisotropic covariances For details, see: N. Neverova, D. Novotny, A. Vedaldi "Correlated Uncertainty for Learning Dense Correspondences from Noisy Labels", p. 918--926, in Proc. NIPS 2019 """ # fmt: off IID_ISO = "iid_iso" INDEP_ANISO = "indep_aniso" # fmt: on @dataclass class DensePoseUVConfidenceConfig: """ Configuration options for confidence on UV data """ enabled: bool = False # lower bound on UV confidences epsilon: float = 0.01 type: DensePoseUVConfidenceType = DensePoseUVConfidenceType.IID_ISO @dataclass class DensePoseSegmConfidenceConfig: """ Configuration options for confidence on segmentation """ enabled: bool = False # lower bound on confidence values epsilon: float = 0.01 @dataclass class DensePoseConfidenceModelConfig: """ Configuration options for confidence models """ # confidence for U and V values uv_confidence: DensePoseUVConfidenceConfig # segmentation confidence segm_confidence: DensePoseSegmConfidenceConfig @staticmethod def from_cfg(cfg: CfgNode) -> "DensePoseConfidenceModelConfig": return DensePoseConfidenceModelConfig( uv_confidence=DensePoseUVConfidenceConfig( enabled=cfg.MODEL.ROI_DENSEPOSE_HEAD.UV_CONFIDENCE.ENABLED, epsilon=cfg.MODEL.ROI_DENSEPOSE_HEAD.UV_CONFIDENCE.EPSILON, type=DensePoseUVConfidenceType(cfg.MODEL.ROI_DENSEPOSE_HEAD.UV_CONFIDENCE.TYPE), ), segm_confidence=DensePoseSegmConfidenceConfig( enabled=cfg.MODEL.ROI_DENSEPOSE_HEAD.SEGM_CONFIDENCE.ENABLED, epsilon=cfg.MODEL.ROI_DENSEPOSE_HEAD.SEGM_CONFIDENCE.EPSILON, ), ) ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/cse/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from .vertex_direct_embedder import VertexDirectEmbedder from .vertex_feature_embedder import VertexFeatureEmbedder from .embedder import Embedder ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/cse/embedder.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import logging import numpy as np import pickle from enum import Enum from typing import Optional import torch from torch import nn from detectron2.config import CfgNode from detectron2.utils.file_io import PathManager from .vertex_direct_embedder import VertexDirectEmbedder from .vertex_feature_embedder import VertexFeatureEmbedder class EmbedderType(Enum): """ Embedder type which defines how vertices are mapped into the embedding space: - "vertex_direct": direct vertex embedding - "vertex_feature": embedding vertex features """ VERTEX_DIRECT = "vertex_direct" VERTEX_FEATURE = "vertex_feature" def create_embedder(embedder_spec: CfgNode, embedder_dim: int) -> nn.Module: """ Create an embedder based on the provided configuration Args: embedder_spec (CfgNode): embedder configuration embedder_dim (int): embedding space dimensionality Return: An embedder instance for the specified configuration Raises ValueError, in case of unexpected embedder type """ embedder_type = EmbedderType(embedder_spec.TYPE) if embedder_type == EmbedderType.VERTEX_DIRECT: embedder = VertexDirectEmbedder( num_vertices=embedder_spec.NUM_VERTICES, embed_dim=embedder_dim, ) if embedder_spec.INIT_FILE != "": embedder.load(embedder_spec.INIT_FILE) elif embedder_type == EmbedderType.VERTEX_FEATURE: embedder = VertexFeatureEmbedder( num_vertices=embedder_spec.NUM_VERTICES, feature_dim=embedder_spec.FEATURE_DIM, embed_dim=embedder_dim, train_features=embedder_spec.FEATURES_TRAINABLE, ) if embedder_spec.INIT_FILE != "": embedder.load(embedder_spec.INIT_FILE) else: raise ValueError(f"Unexpected embedder type {embedder_type}") if not embedder_spec.IS_TRAINABLE: embedder.requires_grad_(False) return embedder class Embedder(nn.Module): """ Embedder module that serves as a container for embedders to use with different meshes. Extends Module to automatically save / load state dict. """ DEFAULT_MODEL_CHECKPOINT_PREFIX = "roi_heads.embedder." def __init__(self, cfg: CfgNode): """ Initialize mesh embedders. An embedder for mesh `i` is stored in a submodule "embedder_{i}". Args: cfg (CfgNode): configuration options """ super(Embedder, self).__init__() self.mesh_names = set() embedder_dim = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_SIZE logger = logging.getLogger(__name__) for mesh_name, embedder_spec in cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDERS.items(): logger.info(f"Adding embedder embedder_{mesh_name} with spec {embedder_spec}") self.add_module(f"embedder_{mesh_name}", create_embedder(embedder_spec, embedder_dim)) self.mesh_names.add(mesh_name) if cfg.MODEL.WEIGHTS != "": self.load_from_model_checkpoint(cfg.MODEL.WEIGHTS) def load_from_model_checkpoint(self, fpath: str, prefix: Optional[str] = None): if prefix is None: prefix = Embedder.DEFAULT_MODEL_CHECKPOINT_PREFIX state_dict = None if fpath.endswith(".pkl"): with PathManager.open(fpath, "rb") as hFile: state_dict = pickle.load(hFile, encoding="latin1") # pyre-ignore[6] else: with PathManager.open(fpath, "rb") as hFile: state_dict = torch.load(hFile, map_location=torch.device("cpu")) if state_dict is not None and "model" in state_dict: state_dict_local = {} for key in state_dict["model"]: if key.startswith(prefix): v_key = state_dict["model"][key] if isinstance(v_key, np.ndarray): v_key = torch.from_numpy(v_key) state_dict_local[key[len(prefix) :]] = v_key # non-strict loading to finetune on different meshes self.load_state_dict(state_dict_local, strict=False) def forward(self, mesh_name: str) -> torch.Tensor: """ Produce vertex embeddings for the specific mesh; vertex embeddings are a tensor of shape [N, D] where: N = number of vertices D = number of dimensions in the embedding space Args: mesh_name (str): name of a mesh for which to obtain vertex embeddings Return: Vertex embeddings, a tensor of shape [N, D] """ return getattr(self, f"embedder_{mesh_name}")() def has_embeddings(self, mesh_name: str) -> bool: return hasattr(self, f"embedder_{mesh_name}") ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/cse/utils.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import torch from torch.nn import functional as F def squared_euclidean_distance_matrix(pts1: torch.Tensor, pts2: torch.Tensor) -> torch.Tensor: """ Get squared Euclidean Distance Matrix Computes pairwise squared Euclidean distances between points Args: pts1: Tensor [M x D], M is the number of points, D is feature dimensionality pts2: Tensor [N x D], N is the number of points, D is feature dimensionality Return: Tensor [M, N]: matrix of squared Euclidean distances; at index (m, n) it contains || pts1[m] - pts2[n] ||^2 """ edm = torch.mm(-2 * pts1, pts2.t()) edm += (pts1 * pts1).sum(1, keepdim=True) + (pts2 * pts2).sum(1, keepdim=True).t() return edm.contiguous() def normalize_embeddings(embeddings: torch.Tensor, epsilon: float = 1e-6) -> torch.Tensor: """ Normalize N D-dimensional embedding vectors arranged in a tensor [N, D] Args: embeddings (tensor [N, D]): N D-dimensional embedding vectors epsilon (float): minimum value for a vector norm Return: Normalized embeddings (tensor [N, D]), such that L2 vector norms are all equal to 1. """ return embeddings / torch.clamp(embeddings.norm(p=None, dim=1, keepdim=True), min=epsilon) def get_closest_vertices_mask_from_ES( E: torch.Tensor, S: torch.Tensor, h: int, w: int, mesh_vertex_embeddings: torch.Tensor, device: torch.device, ): """ Interpolate Embeddings and Segmentations to the size of a given bounding box, and compute closest vertices and the segmentation mask Args: E (tensor [1, D, H, W]): D-dimensional embedding vectors for every point of the default-sized box S (tensor [1, 2, H, W]): 2-dimensional segmentation mask for every point of the default-sized box h (int): height of the target bounding box w (int): width of the target bounding box mesh_vertex_embeddings (tensor [N, D]): vertex embeddings for a chosen mesh N is the number of vertices in the mesh, D is feature dimensionality device (torch.device): device to move the tensors to Return: Closest Vertices (tensor [h, w]), int, for every point of the resulting box Segmentation mask (tensor [h, w]), boolean, for every point of the resulting box """ embedding_resized = F.interpolate(E, size=(h, w), mode="bilinear")[0].to(device) coarse_segm_resized = F.interpolate(S, size=(h, w), mode="bilinear")[0].to(device) mask = coarse_segm_resized.argmax(0) > 0 closest_vertices = torch.zeros(mask.shape, dtype=torch.long, device=device) all_embeddings = embedding_resized[:, mask].t() size_chunk = 10_000 # Chunking to avoid possible OOM edm = [] if len(all_embeddings) == 0: return closest_vertices, mask for chunk in range((len(all_embeddings) - 1) // size_chunk + 1): chunk_embeddings = all_embeddings[size_chunk * chunk : size_chunk * (chunk + 1)] edm.append( torch.argmin( squared_euclidean_distance_matrix(chunk_embeddings, mesh_vertex_embeddings), dim=1 ) ) closest_vertices[mask] = torch.cat(edm) return closest_vertices, mask ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/cse/vertex_direct_embedder.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import pickle import torch from torch import nn from detectron2.utils.file_io import PathManager from .utils import normalize_embeddings class VertexDirectEmbedder(nn.Module): """ Class responsible for embedding vertices. Vertex embeddings take the form of a tensor of size [N, D], where N = number of vertices D = number of dimensions in the embedding space """ def __init__(self, num_vertices: int, embed_dim: int): """ Initialize embedder, set random embeddings Args: num_vertices (int): number of vertices to embed embed_dim (int): number of dimensions in the embedding space """ super(VertexDirectEmbedder, self).__init__() self.embeddings = nn.Parameter(torch.Tensor(num_vertices, embed_dim)) self.reset_parameters() @torch.no_grad() def reset_parameters(self): """ Reset embeddings to random values """ self.embeddings.zero_() def forward(self) -> torch.Tensor: """ Produce vertex embeddings, a tensor of shape [N, D] where: N = number of vertices D = number of dimensions in the embedding space Return: Full vertex embeddings, a tensor of shape [N, D] """ return normalize_embeddings(self.embeddings) @torch.no_grad() def load(self, fpath: str): """ Load data from a file Args: fpath (str): file path to load data from """ with PathManager.open(fpath, "rb") as hFile: data = pickle.load(hFile) # pyre-ignore[6] for name in ["embeddings"]: if name in data: getattr(self, name).copy_( torch.tensor(data[name]).float().to(device=getattr(self, name).device) ) ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/cse/vertex_feature_embedder.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import pickle import torch from torch import nn from detectron2.utils.file_io import PathManager from .utils import normalize_embeddings class VertexFeatureEmbedder(nn.Module): """ Class responsible for embedding vertex features. Mapping from feature space to the embedding space is a tensor of size [K, D], where K = number of dimensions in the feature space D = number of dimensions in the embedding space Vertex features is a tensor of size [N, K], where N = number of vertices K = number of dimensions in the feature space Vertex embeddings are computed as F * E = tensor of size [N, D] """ def __init__( self, num_vertices: int, feature_dim: int, embed_dim: int, train_features: bool = False ): """ Initialize embedder, set random embeddings Args: num_vertices (int): number of vertices to embed feature_dim (int): number of dimensions in the feature space embed_dim (int): number of dimensions in the embedding space train_features (bool): determines whether vertex features should be trained (default: False) """ super(VertexFeatureEmbedder, self).__init__() if train_features: self.features = nn.Parameter(torch.Tensor(num_vertices, feature_dim)) else: self.register_buffer("features", torch.Tensor(num_vertices, feature_dim)) self.embeddings = nn.Parameter(torch.Tensor(feature_dim, embed_dim)) self.reset_parameters() @torch.no_grad() def reset_parameters(self): self.features.zero_() self.embeddings.zero_() def forward(self) -> torch.Tensor: """ Produce vertex embeddings, a tensor of shape [N, D] where: N = number of vertices D = number of dimensions in the embedding space Return: Full vertex embeddings, a tensor of shape [N, D] """ return normalize_embeddings(torch.mm(self.features, self.embeddings)) @torch.no_grad() def load(self, fpath: str): """ Load data from a file Args: fpath (str): file path to load data from """ with PathManager.open(fpath, "rb") as hFile: data = pickle.load(hFile) # pyre-ignore[6] for name in ["features", "embeddings"]: if name in data: getattr(self, name).copy_( torch.tensor(data[name]).float().to(device=getattr(self, name).device) ) ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/densepose_checkpoint.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from collections import OrderedDict from detectron2.checkpoint import DetectionCheckpointer def _rename_HRNet_weights(weights): # We detect and rename HRNet weights for DensePose. 1956 and 1716 are values that are # common to all HRNet pretrained weights, and should be enough to accurately identify them if ( len(weights["model"].keys()) == 1956 and len([k for k in weights["model"].keys() if k.startswith("stage")]) == 1716 ): hrnet_weights = OrderedDict() for k in weights["model"].keys(): hrnet_weights["backbone.bottom_up." + str(k)] = weights["model"][k] return {"model": hrnet_weights} else: return weights class DensePoseCheckpointer(DetectionCheckpointer): """ Same as :class:`DetectionCheckpointer`, but is able to handle HRNet weights """ def __init__(self, model, save_dir="", *, save_to_disk=None, **checkpointables): super().__init__(model, save_dir, save_to_disk=save_to_disk, **checkpointables) def _load_file(self, filename: str) -> object: """ Adding hrnet support """ weights = super()._load_file(filename) return _rename_HRNet_weights(weights) ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/filter.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import List import torch from detectron2.config import CfgNode from detectron2.structures import Instances from detectron2.structures.boxes import matched_pairwise_iou class DensePoseDataFilter(object): def __init__(self, cfg: CfgNode): self.iou_threshold = cfg.MODEL.ROI_DENSEPOSE_HEAD.FG_IOU_THRESHOLD self.keep_masks = cfg.MODEL.ROI_DENSEPOSE_HEAD.COARSE_SEGM_TRAINED_BY_MASKS @torch.no_grad() def __call__(self, features: List[torch.Tensor], proposals_with_targets: List[Instances]): """ Filters proposals with targets to keep only the ones relevant for DensePose training Args: features (list[Tensor]): input data as a list of features, each feature is a tensor. Axis 0 represents the number of images `N` in the input data; axes 1-3 are channels, height, and width, which may vary between features (e.g., if a feature pyramid is used). proposals_with_targets (list[Instances]): length `N` list of `Instances`. The i-th `Instances` contains instances (proposals, GT) for the i-th input image, Returns: list[Tensor]: filtered features list[Instances]: filtered proposals """ proposals_filtered = [] # TODO: the commented out code was supposed to correctly deal with situations # where no valid DensePose GT is available for certain images. The corresponding # image features were sliced and proposals were filtered. This led to performance # deterioration, both in terms of runtime and in terms of evaluation results. # # feature_mask = torch.ones( # len(proposals_with_targets), # dtype=torch.bool, # device=features[0].device if len(features) > 0 else torch.device("cpu"), # ) for i, proposals_per_image in enumerate(proposals_with_targets): if not proposals_per_image.has("gt_densepose") and ( not proposals_per_image.has("gt_masks") or not self.keep_masks ): # feature_mask[i] = 0 continue gt_boxes = proposals_per_image.gt_boxes est_boxes = proposals_per_image.proposal_boxes # apply match threshold for densepose head iou = matched_pairwise_iou(gt_boxes, est_boxes) iou_select = iou > self.iou_threshold proposals_per_image = proposals_per_image[iou_select] # pyre-ignore[6] N_gt_boxes = len(proposals_per_image.gt_boxes) assert N_gt_boxes == len(proposals_per_image.proposal_boxes), ( f"The number of GT boxes {N_gt_boxes} is different from the " f"number of proposal boxes {len(proposals_per_image.proposal_boxes)}" ) # filter out any target without suitable annotation if self.keep_masks: gt_masks = ( proposals_per_image.gt_masks if hasattr(proposals_per_image, "gt_masks") else [None] * N_gt_boxes ) else: gt_masks = [None] * N_gt_boxes gt_densepose = ( proposals_per_image.gt_densepose if hasattr(proposals_per_image, "gt_densepose") else [None] * N_gt_boxes ) assert len(gt_masks) == N_gt_boxes assert len(gt_densepose) == N_gt_boxes selected_indices = [ i for i, (dp_target, mask_target) in enumerate(zip(gt_densepose, gt_masks)) if (dp_target is not None) or (mask_target is not None) ] # if not len(selected_indices): # feature_mask[i] = 0 # continue if len(selected_indices) != N_gt_boxes: proposals_per_image = proposals_per_image[selected_indices] # pyre-ignore[6] assert len(proposals_per_image.gt_boxes) == len(proposals_per_image.proposal_boxes) proposals_filtered.append(proposals_per_image) # features_filtered = [feature[feature_mask] for feature in features] # return features_filtered, proposals_filtered return features, proposals_filtered ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/hrfpn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. """ MIT License Copyright (c) 2019 Microsoft Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import torch import torch.nn as nn import torch.nn.functional as F from detectron2.layers import ShapeSpec from detectron2.modeling.backbone import BACKBONE_REGISTRY from detectron2.modeling.backbone.backbone import Backbone from .hrnet import build_pose_hrnet_backbone class HRFPN(Backbone): """HRFPN (High Resolution Feature Pyramids) Transforms outputs of HRNet backbone so they are suitable for the ROI_heads arXiv: https://arxiv.org/abs/1904.04514 Adapted from https://github.com/open-mmlab/mmdetection/blob/master/mmdet/models/necks/hrfpn.py Args: bottom_up: (list) output of HRNet in_features (list): names of the input features (output of HRNet) in_channels (list): number of channels for each branch out_channels (int): output channels of feature pyramids n_out_features (int): number of output stages pooling (str): pooling for generating feature pyramids (from {MAX, AVG}) share_conv (bool): Have one conv per output, or share one with all the outputs """ def __init__( self, bottom_up, in_features, n_out_features, in_channels, out_channels, pooling="AVG", share_conv=False, ): super(HRFPN, self).__init__() assert isinstance(in_channels, list) self.bottom_up = bottom_up self.in_features = in_features self.n_out_features = n_out_features self.in_channels = in_channels self.out_channels = out_channels self.num_ins = len(in_channels) self.share_conv = share_conv if self.share_conv: self.fpn_conv = nn.Conv2d( in_channels=out_channels, out_channels=out_channels, kernel_size=3, padding=1 ) else: self.fpn_conv = nn.ModuleList() for _ in range(self.n_out_features): self.fpn_conv.append( nn.Conv2d( in_channels=out_channels, out_channels=out_channels, kernel_size=3, padding=1, ) ) # Custom change: Replaces a simple bilinear interpolation self.interp_conv = nn.ModuleList() for i in range(len(self.in_features)): self.interp_conv.append( nn.Sequential( nn.ConvTranspose2d( in_channels=in_channels[i], out_channels=in_channels[i], kernel_size=4, stride=2**i, padding=0, output_padding=0, bias=False, ), nn.BatchNorm2d(in_channels[i], momentum=0.1), nn.ReLU(inplace=True), ) ) # Custom change: Replaces a couple (reduction conv + pooling) by one conv self.reduction_pooling_conv = nn.ModuleList() for i in range(self.n_out_features): self.reduction_pooling_conv.append( nn.Sequential( nn.Conv2d(sum(in_channels), out_channels, kernel_size=2**i, stride=2**i), nn.BatchNorm2d(out_channels, momentum=0.1), nn.ReLU(inplace=True), ) ) if pooling == "MAX": self.pooling = F.max_pool2d else: self.pooling = F.avg_pool2d self._out_features = [] self._out_feature_channels = {} self._out_feature_strides = {} for i in range(self.n_out_features): self._out_features.append("p%d" % (i + 1)) self._out_feature_channels.update({self._out_features[-1]: self.out_channels}) self._out_feature_strides.update({self._out_features[-1]: 2 ** (i + 2)}) # default init_weights for conv(msra) and norm in ConvModule def init_weights(self): for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, a=1) nn.init.constant_(m.bias, 0) def forward(self, inputs): bottom_up_features = self.bottom_up(inputs) assert len(bottom_up_features) == len(self.in_features) inputs = [bottom_up_features[f] for f in self.in_features] outs = [] for i in range(len(inputs)): outs.append(self.interp_conv[i](inputs[i])) shape_2 = min(o.shape[2] for o in outs) shape_3 = min(o.shape[3] for o in outs) out = torch.cat([o[:, :, :shape_2, :shape_3] for o in outs], dim=1) outs = [] for i in range(self.n_out_features): outs.append(self.reduction_pooling_conv[i](out)) for i in range(len(outs)): # Make shapes consistent outs[-1 - i] = outs[-1 - i][ :, :, : outs[-1].shape[2] * 2**i, : outs[-1].shape[3] * 2**i ] outputs = [] for i in range(len(outs)): if self.share_conv: outputs.append(self.fpn_conv(outs[i])) else: outputs.append(self.fpn_conv[i](outs[i])) assert len(self._out_features) == len(outputs) return dict(zip(self._out_features, outputs)) @BACKBONE_REGISTRY.register() def build_hrfpn_backbone(cfg, input_shape: ShapeSpec) -> HRFPN: in_channels = cfg.MODEL.HRNET.STAGE4.NUM_CHANNELS in_features = ["p%d" % (i + 1) for i in range(cfg.MODEL.HRNET.STAGE4.NUM_BRANCHES)] n_out_features = len(cfg.MODEL.ROI_HEADS.IN_FEATURES) out_channels = cfg.MODEL.HRNET.HRFPN.OUT_CHANNELS hrnet = build_pose_hrnet_backbone(cfg, input_shape) hrfpn = HRFPN( hrnet, in_features, n_out_features, in_channels, out_channels, pooling="AVG", share_conv=False, ) return hrfpn ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/hrnet.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # ------------------------------------------------------------------------------ # Copyright (c) Microsoft # Licensed under the MIT License. # Written by Bin Xiao (leoxiaobin@gmail.com) # Modified by Bowen Cheng (bcheng9@illinois.edu) # Adapted from https://github.com/HRNet/Higher-HRNet-Human-Pose-Estimation/blob/master/lib/models/pose_higher_hrnet.py # noqa # ------------------------------------------------------------------------------ from __future__ import absolute_import, division, print_function import logging import torch.nn as nn from detectron2.layers import ShapeSpec from detectron2.modeling.backbone import BACKBONE_REGISTRY from detectron2.modeling.backbone.backbone import Backbone BN_MOMENTUM = 0.1 logger = logging.getLogger(__name__) __all__ = ["build_pose_hrnet_backbone", "PoseHigherResolutionNet"] def conv3x3(in_planes, out_planes, stride=1): """3x3 convolution with padding""" return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False) class BasicBlock(nn.Module): expansion = 1 def __init__(self, inplanes, planes, stride=1, downsample=None): super(BasicBlock, self).__init__() self.conv1 = conv3x3(inplanes, planes, stride) self.bn1 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM) self.relu = nn.ReLU(inplace=True) self.conv2 = conv3x3(planes, planes) self.bn2 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM) self.downsample = downsample self.stride = stride def forward(self, x): residual = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) if self.downsample is not None: residual = self.downsample(x) out += residual out = self.relu(out) return out class Bottleneck(nn.Module): expansion = 4 def __init__(self, inplanes, planes, stride=1, downsample=None): super(Bottleneck, self).__init__() self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) self.bn1 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM) self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM) self.conv3 = nn.Conv2d(planes, planes * self.expansion, kernel_size=1, bias=False) self.bn3 = nn.BatchNorm2d(planes * self.expansion, momentum=BN_MOMENTUM) self.relu = nn.ReLU(inplace=True) self.downsample = downsample self.stride = stride def forward(self, x): residual = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) out = self.relu(out) out = self.conv3(out) out = self.bn3(out) if self.downsample is not None: residual = self.downsample(x) out += residual out = self.relu(out) return out class HighResolutionModule(nn.Module): """HighResolutionModule Building block of the PoseHigherResolutionNet (see lower) arXiv: https://arxiv.org/abs/1908.10357 Args: num_branches (int): number of branches of the modyle blocks (str): type of block of the module num_blocks (int): number of blocks of the module num_inchannels (int): number of input channels of the module num_channels (list): number of channels of each branch multi_scale_output (bool): only used by the last module of PoseHigherResolutionNet """ def __init__( self, num_branches, blocks, num_blocks, num_inchannels, num_channels, multi_scale_output=True, ): super(HighResolutionModule, self).__init__() self._check_branches(num_branches, blocks, num_blocks, num_inchannels, num_channels) self.num_inchannels = num_inchannels self.num_branches = num_branches self.multi_scale_output = multi_scale_output self.branches = self._make_branches(num_branches, blocks, num_blocks, num_channels) self.fuse_layers = self._make_fuse_layers() self.relu = nn.ReLU(True) def _check_branches(self, num_branches, blocks, num_blocks, num_inchannels, num_channels): if num_branches != len(num_blocks): error_msg = "NUM_BRANCHES({}) <> NUM_BLOCKS({})".format(num_branches, len(num_blocks)) logger.error(error_msg) raise ValueError(error_msg) if num_branches != len(num_channels): error_msg = "NUM_BRANCHES({}) <> NUM_CHANNELS({})".format( num_branches, len(num_channels) ) logger.error(error_msg) raise ValueError(error_msg) if num_branches != len(num_inchannels): error_msg = "NUM_BRANCHES({}) <> NUM_INCHANNELS({})".format( num_branches, len(num_inchannels) ) logger.error(error_msg) raise ValueError(error_msg) def _make_one_branch(self, branch_index, block, num_blocks, num_channels, stride=1): downsample = None if ( stride != 1 or self.num_inchannels[branch_index] != num_channels[branch_index] * block.expansion ): downsample = nn.Sequential( nn.Conv2d( self.num_inchannels[branch_index], num_channels[branch_index] * block.expansion, kernel_size=1, stride=stride, bias=False, ), nn.BatchNorm2d(num_channels[branch_index] * block.expansion, momentum=BN_MOMENTUM), ) layers = [] layers.append( block(self.num_inchannels[branch_index], num_channels[branch_index], stride, downsample) ) self.num_inchannels[branch_index] = num_channels[branch_index] * block.expansion for _ in range(1, num_blocks[branch_index]): layers.append(block(self.num_inchannels[branch_index], num_channels[branch_index])) return nn.Sequential(*layers) def _make_branches(self, num_branches, block, num_blocks, num_channels): branches = [] for i in range(num_branches): branches.append(self._make_one_branch(i, block, num_blocks, num_channels)) return nn.ModuleList(branches) def _make_fuse_layers(self): if self.num_branches == 1: return None num_branches = self.num_branches num_inchannels = self.num_inchannels fuse_layers = [] for i in range(num_branches if self.multi_scale_output else 1): fuse_layer = [] for j in range(num_branches): if j > i: fuse_layer.append( nn.Sequential( nn.Conv2d(num_inchannels[j], num_inchannels[i], 1, 1, 0, bias=False), nn.BatchNorm2d(num_inchannels[i]), nn.Upsample(scale_factor=2 ** (j - i), mode="nearest"), ) ) elif j == i: fuse_layer.append(None) else: conv3x3s = [] for k in range(i - j): if k == i - j - 1: num_outchannels_conv3x3 = num_inchannels[i] conv3x3s.append( nn.Sequential( nn.Conv2d( num_inchannels[j], num_outchannels_conv3x3, 3, 2, 1, bias=False, ), nn.BatchNorm2d(num_outchannels_conv3x3), ) ) else: num_outchannels_conv3x3 = num_inchannels[j] conv3x3s.append( nn.Sequential( nn.Conv2d( num_inchannels[j], num_outchannels_conv3x3, 3, 2, 1, bias=False, ), nn.BatchNorm2d(num_outchannels_conv3x3), nn.ReLU(True), ) ) fuse_layer.append(nn.Sequential(*conv3x3s)) fuse_layers.append(nn.ModuleList(fuse_layer)) return nn.ModuleList(fuse_layers) def get_num_inchannels(self): return self.num_inchannels def forward(self, x): if self.num_branches == 1: return [self.branches[0](x[0])] for i in range(self.num_branches): x[i] = self.branches[i](x[i]) x_fuse = [] for i in range(len(self.fuse_layers)): y = x[0] if i == 0 else self.fuse_layers[i][0](x[0]) for j in range(1, self.num_branches): if i == j: y = y + x[j] else: z = self.fuse_layers[i][j](x[j])[:, :, : y.shape[2], : y.shape[3]] y = y + z x_fuse.append(self.relu(y)) return x_fuse blocks_dict = {"BASIC": BasicBlock, "BOTTLENECK": Bottleneck} class PoseHigherResolutionNet(Backbone): """PoseHigherResolutionNet Composed of several HighResolutionModule tied together with ConvNets Adapted from the GitHub version to fit with HRFPN and the Detectron2 infrastructure arXiv: https://arxiv.org/abs/1908.10357 """ def __init__(self, cfg, **kwargs): self.inplanes = cfg.MODEL.HRNET.STEM_INPLANES super(PoseHigherResolutionNet, self).__init__() # stem net self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=2, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(64, momentum=BN_MOMENTUM) self.conv2 = nn.Conv2d(64, 64, kernel_size=3, stride=2, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(64, momentum=BN_MOMENTUM) self.relu = nn.ReLU(inplace=True) self.layer1 = self._make_layer(Bottleneck, 64, 4) self.stage2_cfg = cfg.MODEL.HRNET.STAGE2 num_channels = self.stage2_cfg.NUM_CHANNELS block = blocks_dict[self.stage2_cfg.BLOCK] num_channels = [num_channels[i] * block.expansion for i in range(len(num_channels))] self.transition1 = self._make_transition_layer([256], num_channels) self.stage2, pre_stage_channels = self._make_stage(self.stage2_cfg, num_channels) self.stage3_cfg = cfg.MODEL.HRNET.STAGE3 num_channels = self.stage3_cfg.NUM_CHANNELS block = blocks_dict[self.stage3_cfg.BLOCK] num_channels = [num_channels[i] * block.expansion for i in range(len(num_channels))] self.transition2 = self._make_transition_layer(pre_stage_channels, num_channels) self.stage3, pre_stage_channels = self._make_stage(self.stage3_cfg, num_channels) self.stage4_cfg = cfg.MODEL.HRNET.STAGE4 num_channels = self.stage4_cfg.NUM_CHANNELS block = blocks_dict[self.stage4_cfg.BLOCK] num_channels = [num_channels[i] * block.expansion for i in range(len(num_channels))] self.transition3 = self._make_transition_layer(pre_stage_channels, num_channels) self.stage4, pre_stage_channels = self._make_stage( self.stage4_cfg, num_channels, multi_scale_output=True ) self._out_features = [] self._out_feature_channels = {} self._out_feature_strides = {} for i in range(cfg.MODEL.HRNET.STAGE4.NUM_BRANCHES): self._out_features.append("p%d" % (i + 1)) self._out_feature_channels.update( {self._out_features[-1]: cfg.MODEL.HRNET.STAGE4.NUM_CHANNELS[i]} ) self._out_feature_strides.update({self._out_features[-1]: 1}) def _get_deconv_cfg(self, deconv_kernel): if deconv_kernel == 4: padding = 1 output_padding = 0 elif deconv_kernel == 3: padding = 1 output_padding = 1 elif deconv_kernel == 2: padding = 0 output_padding = 0 return deconv_kernel, padding, output_padding def _make_transition_layer(self, num_channels_pre_layer, num_channels_cur_layer): num_branches_cur = len(num_channels_cur_layer) num_branches_pre = len(num_channels_pre_layer) transition_layers = [] for i in range(num_branches_cur): if i < num_branches_pre: if num_channels_cur_layer[i] != num_channels_pre_layer[i]: transition_layers.append( nn.Sequential( nn.Conv2d( num_channels_pre_layer[i], num_channels_cur_layer[i], 3, 1, 1, bias=False, ), nn.BatchNorm2d(num_channels_cur_layer[i]), nn.ReLU(inplace=True), ) ) else: transition_layers.append(None) else: conv3x3s = [] for j in range(i + 1 - num_branches_pre): inchannels = num_channels_pre_layer[-1] outchannels = ( num_channels_cur_layer[i] if j == i - num_branches_pre else inchannels ) conv3x3s.append( nn.Sequential( nn.Conv2d(inchannels, outchannels, 3, 2, 1, bias=False), nn.BatchNorm2d(outchannels), nn.ReLU(inplace=True), ) ) transition_layers.append(nn.Sequential(*conv3x3s)) return nn.ModuleList(transition_layers) def _make_layer(self, block, planes, blocks, stride=1): downsample = None if stride != 1 or self.inplanes != planes * block.expansion: downsample = nn.Sequential( nn.Conv2d( self.inplanes, planes * block.expansion, kernel_size=1, stride=stride, bias=False, ), nn.BatchNorm2d(planes * block.expansion, momentum=BN_MOMENTUM), ) layers = [] layers.append(block(self.inplanes, planes, stride, downsample)) self.inplanes = planes * block.expansion for _ in range(1, blocks): layers.append(block(self.inplanes, planes)) return nn.Sequential(*layers) def _make_stage(self, layer_config, num_inchannels, multi_scale_output=True): num_modules = layer_config["NUM_MODULES"] num_branches = layer_config["NUM_BRANCHES"] num_blocks = layer_config["NUM_BLOCKS"] num_channels = layer_config["NUM_CHANNELS"] block = blocks_dict[layer_config["BLOCK"]] modules = [] for i in range(num_modules): # multi_scale_output is only used last module if not multi_scale_output and i == num_modules - 1: reset_multi_scale_output = False else: reset_multi_scale_output = True modules.append( HighResolutionModule( num_branches, block, num_blocks, num_inchannels, num_channels, reset_multi_scale_output, ) ) num_inchannels = modules[-1].get_num_inchannels() return nn.Sequential(*modules), num_inchannels def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = self.relu(x) x = self.conv2(x) x = self.bn2(x) x = self.relu(x) x = self.layer1(x) x_list = [] for i in range(self.stage2_cfg.NUM_BRANCHES): if self.transition1[i] is not None: x_list.append(self.transition1[i](x)) else: x_list.append(x) y_list = self.stage2(x_list) x_list = [] for i in range(self.stage3_cfg.NUM_BRANCHES): if self.transition2[i] is not None: x_list.append(self.transition2[i](y_list[-1])) else: x_list.append(y_list[i]) y_list = self.stage3(x_list) x_list = [] for i in range(self.stage4_cfg.NUM_BRANCHES): if self.transition3[i] is not None: x_list.append(self.transition3[i](y_list[-1])) else: x_list.append(y_list[i]) y_list = self.stage4(x_list) assert len(self._out_features) == len(y_list) return dict(zip(self._out_features, y_list)) # final_outputs @BACKBONE_REGISTRY.register() def build_pose_hrnet_backbone(cfg, input_shape: ShapeSpec): model = PoseHigherResolutionNet(cfg) return model ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/inference.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from dataclasses import fields from typing import Any, List import torch from detectron2.structures import Instances def densepose_inference(densepose_predictor_output: Any, detections: List[Instances]) -> None: """ Splits DensePose predictor outputs into chunks, each chunk corresponds to detections on one image. Predictor output chunks are stored in `pred_densepose` attribute of the corresponding `Instances` object. Args: densepose_predictor_output: a dataclass instance (can be of different types, depending on predictor used for inference). Each field can be `None` (if the corresponding output was not inferred) or a tensor of size [N, ...], where N = N_1 + N_2 + .. + N_k is a total number of detections on all images, N_1 is the number of detections on image 1, N_2 is the number of detections on image 2, etc. detections: a list of objects of type `Instance`, k-th object corresponds to detections on k-th image. """ k = 0 for detection_i in detections: if densepose_predictor_output is None: # don't add `pred_densepose` attribute continue n_i = len(detection_i) PredictorOutput = type(densepose_predictor_output) output_i_dict = {} # we assume here that `densepose_predictor_output` is a dataclass object for field in fields(densepose_predictor_output): field_value = getattr(densepose_predictor_output, field.name) # slice tensors if isinstance(field_value, torch.Tensor): output_i_dict[field.name] = field_value[k : k + n_i] # leave others as is else: output_i_dict[field.name] = field_value detection_i.pred_densepose = PredictorOutput(**output_i_dict) k += n_i ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .chart import DensePoseChartLoss from .chart_with_confidences import DensePoseChartWithConfidenceLoss from .cse import DensePoseCseLoss from .registry import DENSEPOSE_LOSS_REGISTRY __all__ = [ "DensePoseChartLoss", "DensePoseChartWithConfidenceLoss", "DensePoseCseLoss", "DENSEPOSE_LOSS_REGISTRY", ] ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/chart.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any, List import torch from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.structures import Instances from .mask_or_segm import MaskOrSegmentationLoss from .registry import DENSEPOSE_LOSS_REGISTRY from .utils import ( BilinearInterpolationHelper, ChartBasedAnnotationsAccumulator, LossDict, extract_packed_annotations_from_matches, ) @DENSEPOSE_LOSS_REGISTRY.register() class DensePoseChartLoss: """ DensePose loss for chart-based training. A mesh is split into charts, each chart is given a label (I) and parametrized by 2 coordinates referred to as U and V. Ground truth consists of a number of points annotated with I, U and V values and coarse segmentation S defined for all pixels of the object bounding box. In some cases (see `COARSE_SEGM_TRAINED_BY_MASKS`), semantic segmentation annotations can be used as ground truth inputs as well. Estimated values are tensors: * U coordinates, tensor of shape [N, C, S, S] * V coordinates, tensor of shape [N, C, S, S] * fine segmentation estimates, tensor of shape [N, C, S, S] with raw unnormalized scores for each fine segmentation label at each location * coarse segmentation estimates, tensor of shape [N, D, S, S] with raw unnormalized scores for each coarse segmentation label at each location where N is the number of detections, C is the number of fine segmentation labels, S is the estimate size ( = width = height) and D is the number of coarse segmentation channels. The losses are: * regression (smooth L1) loss for U and V coordinates * cross entropy loss for fine (I) and coarse (S) segmentations Each loss has an associated weight """ def __init__(self, cfg: CfgNode): """ Initialize chart-based loss from configuration options Args: cfg (CfgNode): configuration options """ # fmt: off self.heatmap_size = cfg.MODEL.ROI_DENSEPOSE_HEAD.HEATMAP_SIZE self.w_points = cfg.MODEL.ROI_DENSEPOSE_HEAD.POINT_REGRESSION_WEIGHTS self.w_part = cfg.MODEL.ROI_DENSEPOSE_HEAD.PART_WEIGHTS self.w_segm = cfg.MODEL.ROI_DENSEPOSE_HEAD.INDEX_WEIGHTS self.n_segm_chan = cfg.MODEL.ROI_DENSEPOSE_HEAD.NUM_COARSE_SEGM_CHANNELS # fmt: on self.segm_trained_by_masks = cfg.MODEL.ROI_DENSEPOSE_HEAD.COARSE_SEGM_TRAINED_BY_MASKS self.segm_loss = MaskOrSegmentationLoss(cfg) def __call__( self, proposals_with_gt: List[Instances], densepose_predictor_outputs: Any, **kwargs ) -> LossDict: """ Produce chart-based DensePose losses Args: proposals_with_gt (list of Instances): detections with associated ground truth data densepose_predictor_outputs: an object of a dataclass that contains predictor outputs with estimated values; assumed to have the following attributes: * coarse_segm - coarse segmentation estimates, tensor of shape [N, D, S, S] * fine_segm - fine segmentation estimates, tensor of shape [N, C, S, S] * u - U coordinate estimates per fine labels, tensor of shape [N, C, S, S] * v - V coordinate estimates per fine labels, tensor of shape [N, C, S, S] where N is the number of detections, C is the number of fine segmentation labels, S is the estimate size ( = width = height) and D is the number of coarse segmentation channels. Return: dict: str -> tensor: dict of losses with the following entries: * `loss_densepose_U`: smooth L1 loss for U coordinate estimates * `loss_densepose_V`: smooth L1 loss for V coordinate estimates * `loss_densepose_I`: cross entropy for raw unnormalized scores for fine segmentation estimates given ground truth labels; * `loss_densepose_S`: cross entropy for raw unnormalized scores for coarse segmentation estimates given ground truth labels; """ # densepose outputs are computed for all images and all bounding boxes; # i.e. if a batch has 4 images with (3, 1, 2, 1) proposals respectively, # the outputs will have size(0) == 3+1+2+1 == 7 if not len(proposals_with_gt): return self.produce_fake_densepose_losses(densepose_predictor_outputs) accumulator = ChartBasedAnnotationsAccumulator() packed_annotations = extract_packed_annotations_from_matches(proposals_with_gt, accumulator) # NOTE: we need to keep the same computation graph on all the GPUs to # perform reduction properly. Hence even if we have no data on one # of the GPUs, we still need to generate the computation graph. # Add fake (zero) loss in the form Tensor.sum() * 0 if packed_annotations is None: return self.produce_fake_densepose_losses(densepose_predictor_outputs) h, w = densepose_predictor_outputs.u.shape[2:] interpolator = BilinearInterpolationHelper.from_matches( packed_annotations, (h, w), ) j_valid_fg = interpolator.j_valid * ( # pyre-ignore[16] packed_annotations.fine_segm_labels_gt > 0 ) # pyre-fixme[6]: For 1st param expected `Tensor` but got `int`. if not torch.any(j_valid_fg): return self.produce_fake_densepose_losses(densepose_predictor_outputs) losses_uv = self.produce_densepose_losses_uv( proposals_with_gt, densepose_predictor_outputs, packed_annotations, interpolator, j_valid_fg, # pyre-ignore[6] ) losses_segm = self.produce_densepose_losses_segm( proposals_with_gt, densepose_predictor_outputs, packed_annotations, interpolator, j_valid_fg, # pyre-ignore[6] ) return {**losses_uv, **losses_segm} def produce_fake_densepose_losses(self, densepose_predictor_outputs: Any) -> LossDict: """ Fake losses for fine segmentation and U/V coordinates. These are used when no suitable ground truth data was found in a batch. The loss has a value 0 and is primarily used to construct the computation graph, so that `DistributedDataParallel` has similar graphs on all GPUs and can perform reduction properly. Args: densepose_predictor_outputs: DensePose predictor outputs, an object of a dataclass that is assumed to have the following attributes: * fine_segm - fine segmentation estimates, tensor of shape [N, C, S, S] * u - U coordinate estimates per fine labels, tensor of shape [N, C, S, S] * v - V coordinate estimates per fine labels, tensor of shape [N, C, S, S] Return: dict: str -> tensor: dict of losses with the following entries: * `loss_densepose_U`: has value 0 * `loss_densepose_V`: has value 0 * `loss_densepose_I`: has value 0 * `loss_densepose_S`: has value 0 """ losses_uv = self.produce_fake_densepose_losses_uv(densepose_predictor_outputs) losses_segm = self.produce_fake_densepose_losses_segm(densepose_predictor_outputs) return {**losses_uv, **losses_segm} def produce_fake_densepose_losses_uv(self, densepose_predictor_outputs: Any) -> LossDict: """ Fake losses for U/V coordinates. These are used when no suitable ground truth data was found in a batch. The loss has a value 0 and is primarily used to construct the computation graph, so that `DistributedDataParallel` has similar graphs on all GPUs and can perform reduction properly. Args: densepose_predictor_outputs: DensePose predictor outputs, an object of a dataclass that is assumed to have the following attributes: * u - U coordinate estimates per fine labels, tensor of shape [N, C, S, S] * v - V coordinate estimates per fine labels, tensor of shape [N, C, S, S] Return: dict: str -> tensor: dict of losses with the following entries: * `loss_densepose_U`: has value 0 * `loss_densepose_V`: has value 0 """ return { "loss_densepose_U": densepose_predictor_outputs.u.sum() * 0, "loss_densepose_V": densepose_predictor_outputs.v.sum() * 0, } def produce_fake_densepose_losses_segm(self, densepose_predictor_outputs: Any) -> LossDict: """ Fake losses for fine / coarse segmentation. These are used when no suitable ground truth data was found in a batch. The loss has a value 0 and is primarily used to construct the computation graph, so that `DistributedDataParallel` has similar graphs on all GPUs and can perform reduction properly. Args: densepose_predictor_outputs: DensePose predictor outputs, an object of a dataclass that is assumed to have the following attributes: * fine_segm - fine segmentation estimates, tensor of shape [N, C, S, S] * coarse_segm - coarse segmentation estimates, tensor of shape [N, D, S, S] Return: dict: str -> tensor: dict of losses with the following entries: * `loss_densepose_I`: has value 0 * `loss_densepose_S`: has value 0, added only if `segm_trained_by_masks` is False """ losses = { "loss_densepose_I": densepose_predictor_outputs.fine_segm.sum() * 0, "loss_densepose_S": self.segm_loss.fake_value(densepose_predictor_outputs), } return losses def produce_densepose_losses_uv( self, proposals_with_gt: List[Instances], densepose_predictor_outputs: Any, packed_annotations: Any, interpolator: BilinearInterpolationHelper, j_valid_fg: torch.Tensor, ) -> LossDict: """ Compute losses for U/V coordinates: smooth L1 loss between estimated coordinates and the ground truth. Args: proposals_with_gt (list of Instances): detections with associated ground truth data densepose_predictor_outputs: DensePose predictor outputs, an object of a dataclass that is assumed to have the following attributes: * u - U coordinate estimates per fine labels, tensor of shape [N, C, S, S] * v - V coordinate estimates per fine labels, tensor of shape [N, C, S, S] Return: dict: str -> tensor: dict of losses with the following entries: * `loss_densepose_U`: smooth L1 loss for U coordinate estimates * `loss_densepose_V`: smooth L1 loss for V coordinate estimates """ u_gt = packed_annotations.u_gt[j_valid_fg] u_est = interpolator.extract_at_points(densepose_predictor_outputs.u)[j_valid_fg] v_gt = packed_annotations.v_gt[j_valid_fg] v_est = interpolator.extract_at_points(densepose_predictor_outputs.v)[j_valid_fg] return { "loss_densepose_U": F.smooth_l1_loss(u_est, u_gt, reduction="sum") * self.w_points, "loss_densepose_V": F.smooth_l1_loss(v_est, v_gt, reduction="sum") * self.w_points, } def produce_densepose_losses_segm( self, proposals_with_gt: List[Instances], densepose_predictor_outputs: Any, packed_annotations: Any, interpolator: BilinearInterpolationHelper, j_valid_fg: torch.Tensor, ) -> LossDict: """ Losses for fine / coarse segmentation: cross-entropy for segmentation unnormalized scores given ground truth labels at annotated points for fine segmentation and dense mask annotations for coarse segmentation. Args: proposals_with_gt (list of Instances): detections with associated ground truth data densepose_predictor_outputs: DensePose predictor outputs, an object of a dataclass that is assumed to have the following attributes: * fine_segm - fine segmentation estimates, tensor of shape [N, C, S, S] * coarse_segm - coarse segmentation estimates, tensor of shape [N, D, S, S] Return: dict: str -> tensor: dict of losses with the following entries: * `loss_densepose_I`: cross entropy for raw unnormalized scores for fine segmentation estimates given ground truth labels * `loss_densepose_S`: cross entropy for raw unnormalized scores for coarse segmentation estimates given ground truth labels; may be included if coarse segmentation is only trained using DensePose ground truth; if additional supervision through instance segmentation data is performed (`segm_trained_by_masks` is True), this loss is handled by `produce_mask_losses` instead """ fine_segm_gt = packed_annotations.fine_segm_labels_gt[ interpolator.j_valid # pyre-ignore[16] ] fine_segm_est = interpolator.extract_at_points( densepose_predictor_outputs.fine_segm, slice_fine_segm=slice(None), w_ylo_xlo=interpolator.w_ylo_xlo[:, None], # pyre-ignore[16] w_ylo_xhi=interpolator.w_ylo_xhi[:, None], # pyre-ignore[16] w_yhi_xlo=interpolator.w_yhi_xlo[:, None], # pyre-ignore[16] w_yhi_xhi=interpolator.w_yhi_xhi[:, None], # pyre-ignore[16] )[interpolator.j_valid, :] return { "loss_densepose_I": F.cross_entropy(fine_segm_est, fine_segm_gt.long()) * self.w_part, "loss_densepose_S": self.segm_loss( proposals_with_gt, densepose_predictor_outputs, packed_annotations ) * self.w_segm, } ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/chart_with_confidences.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import math from typing import Any, List import torch from torch import nn from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.structures import Instances from .. import DensePoseConfidenceModelConfig, DensePoseUVConfidenceType from .chart import DensePoseChartLoss from .registry import DENSEPOSE_LOSS_REGISTRY from .utils import BilinearInterpolationHelper, LossDict @DENSEPOSE_LOSS_REGISTRY.register() class DensePoseChartWithConfidenceLoss(DensePoseChartLoss): """ """ def __init__(self, cfg: CfgNode): super().__init__(cfg) self.confidence_model_cfg = DensePoseConfidenceModelConfig.from_cfg(cfg) if self.confidence_model_cfg.uv_confidence.type == DensePoseUVConfidenceType.IID_ISO: self.uv_loss_with_confidences = IIDIsotropicGaussianUVLoss( self.confidence_model_cfg.uv_confidence.epsilon ) elif self.confidence_model_cfg.uv_confidence.type == DensePoseUVConfidenceType.INDEP_ANISO: self.uv_loss_with_confidences = IndepAnisotropicGaussianUVLoss( self.confidence_model_cfg.uv_confidence.epsilon ) def produce_fake_densepose_losses_uv(self, densepose_predictor_outputs: Any) -> LossDict: """ Overrides fake losses for fine segmentation and U/V coordinates to include computation graphs for additional confidence parameters. These are used when no suitable ground truth data was found in a batch. The loss has a value 0 and is primarily used to construct the computation graph, so that `DistributedDataParallel` has similar graphs on all GPUs and can perform reduction properly. Args: densepose_predictor_outputs: DensePose predictor outputs, an object of a dataclass that is assumed to have the following attributes: * fine_segm - fine segmentation estimates, tensor of shape [N, C, S, S] * u - U coordinate estimates per fine labels, tensor of shape [N, C, S, S] * v - V coordinate estimates per fine labels, tensor of shape [N, C, S, S] Return: dict: str -> tensor: dict of losses with the following entries: * `loss_densepose_U`: has value 0 * `loss_densepose_V`: has value 0 * `loss_densepose_I`: has value 0 """ conf_type = self.confidence_model_cfg.uv_confidence.type if self.confidence_model_cfg.uv_confidence.enabled: loss_uv = ( densepose_predictor_outputs.u.sum() + densepose_predictor_outputs.v.sum() ) * 0 if conf_type == DensePoseUVConfidenceType.IID_ISO: loss_uv += densepose_predictor_outputs.sigma_2.sum() * 0 elif conf_type == DensePoseUVConfidenceType.INDEP_ANISO: loss_uv += ( densepose_predictor_outputs.sigma_2.sum() + densepose_predictor_outputs.kappa_u.sum() + densepose_predictor_outputs.kappa_v.sum() ) * 0 return {"loss_densepose_UV": loss_uv} else: return super().produce_fake_densepose_losses_uv(densepose_predictor_outputs) def produce_densepose_losses_uv( self, proposals_with_gt: List[Instances], densepose_predictor_outputs: Any, packed_annotations: Any, interpolator: BilinearInterpolationHelper, j_valid_fg: torch.Tensor, ) -> LossDict: conf_type = self.confidence_model_cfg.uv_confidence.type if self.confidence_model_cfg.uv_confidence.enabled: u_gt = packed_annotations.u_gt[j_valid_fg] u_est = interpolator.extract_at_points(densepose_predictor_outputs.u)[j_valid_fg] v_gt = packed_annotations.v_gt[j_valid_fg] v_est = interpolator.extract_at_points(densepose_predictor_outputs.v)[j_valid_fg] sigma_2_est = interpolator.extract_at_points(densepose_predictor_outputs.sigma_2)[ j_valid_fg ] if conf_type == DensePoseUVConfidenceType.IID_ISO: return { "loss_densepose_UV": ( self.uv_loss_with_confidences(u_est, v_est, sigma_2_est, u_gt, v_gt) * self.w_points ) } elif conf_type in [DensePoseUVConfidenceType.INDEP_ANISO]: kappa_u_est = interpolator.extract_at_points(densepose_predictor_outputs.kappa_u)[ j_valid_fg ] kappa_v_est = interpolator.extract_at_points(densepose_predictor_outputs.kappa_v)[ j_valid_fg ] return { "loss_densepose_UV": ( self.uv_loss_with_confidences( u_est, v_est, sigma_2_est, kappa_u_est, kappa_v_est, u_gt, v_gt ) * self.w_points ) } return super().produce_densepose_losses_uv( proposals_with_gt, densepose_predictor_outputs, packed_annotations, interpolator, j_valid_fg, ) class IIDIsotropicGaussianUVLoss(nn.Module): """ Loss for the case of iid residuals with isotropic covariance: $Sigma_i = sigma_i^2 I$ The loss (negative log likelihood) is then: $1/2 sum_{i=1}^n (log(2 pi) + 2 log sigma_i^2 + ||delta_i||^2 / sigma_i^2)$, where $delta_i=(u - u', v - v')$ is a 2D vector containing UV coordinates difference between estimated and ground truth UV values For details, see: N. Neverova, D. Novotny, A. Vedaldi "Correlated Uncertainty for Learning Dense Correspondences from Noisy Labels", p. 918--926, in Proc. NIPS 2019 """ def __init__(self, sigma_lower_bound: float): super(IIDIsotropicGaussianUVLoss, self).__init__() self.sigma_lower_bound = sigma_lower_bound self.log2pi = math.log(2 * math.pi) def forward( self, u: torch.Tensor, v: torch.Tensor, sigma_u: torch.Tensor, target_u: torch.Tensor, target_v: torch.Tensor, ): # compute $\sigma_i^2$ # use sigma_lower_bound to avoid degenerate solution for variance # (sigma -> 0) sigma2 = F.softplus(sigma_u) + self.sigma_lower_bound # compute \|delta_i\|^2 # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and `int`. delta_t_delta = (u - target_u) ** 2 + (v - target_v) ** 2 # the total loss from the formula above: loss = 0.5 * (self.log2pi + 2 * torch.log(sigma2) + delta_t_delta / sigma2) return loss.sum() class IndepAnisotropicGaussianUVLoss(nn.Module): """ Loss for the case of independent residuals with anisotropic covariances: $Sigma_i = sigma_i^2 I + r_i r_i^T$ The loss (negative log likelihood) is then: $1/2 sum_{i=1}^n (log(2 pi) + log sigma_i^2 (sigma_i^2 + ||r_i||^2) + ||delta_i||^2 / sigma_i^2 - ^2 / (sigma_i^2 * (sigma_i^2 + ||r_i||^2)))$, where $delta_i=(u - u', v - v')$ is a 2D vector containing UV coordinates difference between estimated and ground truth UV values For details, see: N. Neverova, D. Novotny, A. Vedaldi "Correlated Uncertainty for Learning Dense Correspondences from Noisy Labels", p. 918--926, in Proc. NIPS 2019 """ def __init__(self, sigma_lower_bound: float): super(IndepAnisotropicGaussianUVLoss, self).__init__() self.sigma_lower_bound = sigma_lower_bound self.log2pi = math.log(2 * math.pi) def forward( self, u: torch.Tensor, v: torch.Tensor, sigma_u: torch.Tensor, kappa_u_est: torch.Tensor, kappa_v_est: torch.Tensor, target_u: torch.Tensor, target_v: torch.Tensor, ): # compute $\sigma_i^2$ sigma2 = F.softplus(sigma_u) + self.sigma_lower_bound # compute \|r_i\|^2 # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and `int`. r_sqnorm2 = kappa_u_est**2 + kappa_v_est**2 delta_u = u - target_u delta_v = v - target_v # compute \|delta_i\|^2 # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and `int`. delta_sqnorm = delta_u**2 + delta_v**2 delta_u_r_u = delta_u * kappa_u_est delta_v_r_v = delta_v * kappa_v_est # compute the scalar product delta_r = delta_u_r_u + delta_v_r_v # compute squared scalar product ^2 # pyre-fixme[58]: `**` is not supported for operand types `Tensor` and `int`. delta_r_sqnorm = delta_r**2 denom2 = sigma2 * (sigma2 + r_sqnorm2) loss = 0.5 * ( self.log2pi + torch.log(denom2) + delta_sqnorm / sigma2 - delta_r_sqnorm / denom2 ) return loss.sum() ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/cse.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from typing import Any, List from torch import nn from detectron2.config import CfgNode from detectron2.structures import Instances from .cycle_pix2shape import PixToShapeCycleLoss from .cycle_shape2shape import ShapeToShapeCycleLoss from .embed import EmbeddingLoss from .embed_utils import CseAnnotationsAccumulator from .mask_or_segm import MaskOrSegmentationLoss from .registry import DENSEPOSE_LOSS_REGISTRY from .soft_embed import SoftEmbeddingLoss from .utils import BilinearInterpolationHelper, LossDict, extract_packed_annotations_from_matches @DENSEPOSE_LOSS_REGISTRY.register() class DensePoseCseLoss: """ """ _EMBED_LOSS_REGISTRY = { EmbeddingLoss.__name__: EmbeddingLoss, SoftEmbeddingLoss.__name__: SoftEmbeddingLoss, } def __init__(self, cfg: CfgNode): """ Initialize CSE loss from configuration options Args: cfg (CfgNode): configuration options """ self.w_segm = cfg.MODEL.ROI_DENSEPOSE_HEAD.INDEX_WEIGHTS self.w_embed = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_LOSS_WEIGHT self.segm_loss = MaskOrSegmentationLoss(cfg) self.embed_loss = DensePoseCseLoss.create_embed_loss(cfg) self.do_shape2shape = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.ENABLED if self.do_shape2shape: self.w_shape2shape = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.WEIGHT self.shape2shape_loss = ShapeToShapeCycleLoss(cfg) self.do_pix2shape = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.ENABLED if self.do_pix2shape: self.w_pix2shape = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.WEIGHT self.pix2shape_loss = PixToShapeCycleLoss(cfg) @classmethod def create_embed_loss(cls, cfg: CfgNode): # registry not used here, since embedding losses are currently local # and are not used anywhere else return cls._EMBED_LOSS_REGISTRY[cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_LOSS_NAME](cfg) def __call__( self, proposals_with_gt: List[Instances], densepose_predictor_outputs: Any, embedder: nn.Module, ) -> LossDict: if not len(proposals_with_gt): return self.produce_fake_losses(densepose_predictor_outputs, embedder) accumulator = CseAnnotationsAccumulator() packed_annotations = extract_packed_annotations_from_matches(proposals_with_gt, accumulator) if packed_annotations is None: return self.produce_fake_losses(densepose_predictor_outputs, embedder) h, w = densepose_predictor_outputs.embedding.shape[2:] interpolator = BilinearInterpolationHelper.from_matches( packed_annotations, (h, w), ) meshid_to_embed_losses = self.embed_loss( proposals_with_gt, densepose_predictor_outputs, packed_annotations, interpolator, embedder, ) embed_loss_dict = { f"loss_densepose_E{meshid}": self.w_embed * meshid_to_embed_losses[meshid] for meshid in meshid_to_embed_losses } all_loss_dict = { "loss_densepose_S": self.w_segm * self.segm_loss(proposals_with_gt, densepose_predictor_outputs, packed_annotations), **embed_loss_dict, } if self.do_shape2shape: all_loss_dict["loss_shape2shape"] = self.w_shape2shape * self.shape2shape_loss(embedder) if self.do_pix2shape: all_loss_dict["loss_pix2shape"] = self.w_pix2shape * self.pix2shape_loss( proposals_with_gt, densepose_predictor_outputs, packed_annotations, embedder ) return all_loss_dict def produce_fake_losses( self, densepose_predictor_outputs: Any, embedder: nn.Module ) -> LossDict: meshname_to_embed_losses = self.embed_loss.fake_values( densepose_predictor_outputs, embedder=embedder ) embed_loss_dict = { f"loss_densepose_E{mesh_name}": meshname_to_embed_losses[mesh_name] for mesh_name in meshname_to_embed_losses } all_loss_dict = { "loss_densepose_S": self.segm_loss.fake_value(densepose_predictor_outputs), **embed_loss_dict, } if self.do_shape2shape: all_loss_dict["loss_shape2shape"] = self.shape2shape_loss.fake_value(embedder) if self.do_pix2shape: all_loss_dict["loss_pix2shape"] = self.pix2shape_loss.fake_value( densepose_predictor_outputs, embedder ) return all_loss_dict ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/cycle_pix2shape.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from typing import Any, List import torch from torch import nn from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.structures import Instances from densepose.data.meshes.catalog import MeshCatalog from densepose.modeling.cse.utils import normalize_embeddings, squared_euclidean_distance_matrix from .embed_utils import PackedCseAnnotations from .mask import extract_data_for_mask_loss_from_matches def _create_pixel_dist_matrix(grid_size: int) -> torch.Tensor: rows = torch.arange(grid_size) cols = torch.arange(grid_size) # at index `i` contains [row, col], where # row = i // grid_size # col = i % grid_size pix_coords = ( torch.stack(torch.meshgrid(rows, cols), -1).reshape((grid_size * grid_size, 2)).float() ) return squared_euclidean_distance_matrix(pix_coords, pix_coords) def _sample_fg_pixels_randperm(fg_mask: torch.Tensor, sample_size: int) -> torch.Tensor: fg_mask_flattened = fg_mask.reshape((-1,)) num_pixels = int(fg_mask_flattened.sum().item()) fg_pixel_indices = fg_mask_flattened.nonzero(as_tuple=True)[0] if (sample_size <= 0) or (num_pixels <= sample_size): return fg_pixel_indices sample_indices = torch.randperm(num_pixels, device=fg_mask.device)[:sample_size] return fg_pixel_indices[sample_indices] def _sample_fg_pixels_multinomial(fg_mask: torch.Tensor, sample_size: int) -> torch.Tensor: fg_mask_flattened = fg_mask.reshape((-1,)) num_pixels = int(fg_mask_flattened.sum().item()) if (sample_size <= 0) or (num_pixels <= sample_size): return fg_mask_flattened.nonzero(as_tuple=True)[0] return fg_mask_flattened.float().multinomial(sample_size, replacement=False) class PixToShapeCycleLoss(nn.Module): """ Cycle loss for pixel-vertex correspondence """ def __init__(self, cfg: CfgNode): super().__init__() self.shape_names = list(cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDERS.keys()) self.embed_size = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_SIZE self.norm_p = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.NORM_P self.use_all_meshes_not_gt_only = ( cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.USE_ALL_MESHES_NOT_GT_ONLY ) self.num_pixels_to_sample = ( cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.NUM_PIXELS_TO_SAMPLE ) self.pix_sigma = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.PIXEL_SIGMA self.temperature_pix_to_vertex = ( cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.TEMPERATURE_PIXEL_TO_VERTEX ) self.temperature_vertex_to_pix = ( cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.PIX_TO_SHAPE_CYCLE_LOSS.TEMPERATURE_VERTEX_TO_PIXEL ) self.pixel_dists = _create_pixel_dist_matrix(cfg.MODEL.ROI_DENSEPOSE_HEAD.HEATMAP_SIZE) def forward( self, proposals_with_gt: List[Instances], densepose_predictor_outputs: Any, packed_annotations: PackedCseAnnotations, embedder: nn.Module, ): """ Args: proposals_with_gt (list of Instances): detections with associated ground truth data; each item corresponds to instances detected on 1 image; the number of items corresponds to the number of images in a batch densepose_predictor_outputs: an object of a dataclass that contains predictor outputs with estimated values; assumed to have the following attributes: * embedding - embedding estimates, tensor of shape [N, D, S, S], where N = number of instances (= sum N_i, where N_i is the number of instances on image i) D = embedding space dimensionality (MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_SIZE) S = output size (width and height) packed_annotations (PackedCseAnnotations): contains various data useful for loss computation, each data is packed into a single tensor embedder (nn.Module): module that computes vertex embeddings for different meshes """ pix_embeds = densepose_predictor_outputs.embedding if self.pixel_dists.device != pix_embeds.device: # should normally be done only once self.pixel_dists = self.pixel_dists.to(device=pix_embeds.device) with torch.no_grad(): mask_loss_data = extract_data_for_mask_loss_from_matches( proposals_with_gt, densepose_predictor_outputs.coarse_segm ) # GT masks - tensor of shape [N, S, S] of int64 masks_gt = mask_loss_data.masks_gt.long() # pyre-ignore[16] assert len(pix_embeds) == len(masks_gt), ( f"Number of instances with embeddings {len(pix_embeds)} != " f"number of instances with GT masks {len(masks_gt)}" ) losses = [] mesh_names = ( self.shape_names if self.use_all_meshes_not_gt_only else [ MeshCatalog.get_mesh_name(mesh_id.item()) for mesh_id in packed_annotations.vertex_mesh_ids_gt.unique() ] ) for pixel_embeddings, mask_gt in zip(pix_embeds, masks_gt): # pixel_embeddings [D, S, S] # mask_gt [S, S] for mesh_name in mesh_names: mesh_vertex_embeddings = embedder(mesh_name) # pixel indices [M] pixel_indices_flattened = _sample_fg_pixels_randperm( mask_gt, self.num_pixels_to_sample ) # pixel distances [M, M] pixel_dists = self.pixel_dists.to(pixel_embeddings.device)[ torch.meshgrid(pixel_indices_flattened, pixel_indices_flattened) ] # pixel embeddings [M, D] pixel_embeddings_sampled = normalize_embeddings( pixel_embeddings.reshape((self.embed_size, -1))[:, pixel_indices_flattened].T ) # pixel-vertex similarity [M, K] sim_matrix = pixel_embeddings_sampled.mm(mesh_vertex_embeddings.T) c_pix_vertex = F.softmax(sim_matrix / self.temperature_pix_to_vertex, dim=1) c_vertex_pix = F.softmax(sim_matrix.T / self.temperature_vertex_to_pix, dim=1) c_cycle = c_pix_vertex.mm(c_vertex_pix) loss_cycle = torch.norm(pixel_dists * c_cycle, p=self.norm_p) losses.append(loss_cycle) if len(losses) == 0: return pix_embeds.sum() * 0 return torch.stack(losses, dim=0).mean() def fake_value(self, densepose_predictor_outputs: Any, embedder: nn.Module): losses = [ embedder(mesh_name).sum() * 0 for mesh_name in embedder.mesh_names # pyre-ignore[29] ] losses.append(densepose_predictor_outputs.embedding.sum() * 0) return torch.mean(torch.stack(losses)) ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/cycle_shape2shape.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import random from typing import Tuple import torch from torch import nn from torch.nn import functional as F from detectron2.config import CfgNode from densepose.structures.mesh import create_mesh from .utils import sample_random_indices class ShapeToShapeCycleLoss(nn.Module): """ Cycle Loss for Shapes. Inspired by: "Mapping in a Cycle: Sinkhorn Regularized Unsupervised Learning for Point Cloud Shapes". """ def __init__(self, cfg: CfgNode): super().__init__() self.shape_names = list(cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDERS.keys()) self.all_shape_pairs = [ (x, y) for i, x in enumerate(self.shape_names) for y in self.shape_names[i + 1 :] ] random.shuffle(self.all_shape_pairs) self.cur_pos = 0 self.norm_p = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.NORM_P self.temperature = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.TEMPERATURE self.max_num_vertices = ( cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.SHAPE_TO_SHAPE_CYCLE_LOSS.MAX_NUM_VERTICES ) def _sample_random_pair(self) -> Tuple[str, str]: """ Produce a random pair of different mesh names Return: tuple(str, str): a pair of different mesh names """ if self.cur_pos >= len(self.all_shape_pairs): random.shuffle(self.all_shape_pairs) self.cur_pos = 0 shape_pair = self.all_shape_pairs[self.cur_pos] self.cur_pos += 1 return shape_pair def forward(self, embedder: nn.Module): """ Do a forward pass with a random pair (src, dst) pair of shapes Args: embedder (nn.Module): module that computes vertex embeddings for different meshes """ src_mesh_name, dst_mesh_name = self._sample_random_pair() return self._forward_one_pair(embedder, src_mesh_name, dst_mesh_name) def fake_value(self, embedder: nn.Module): losses = [] for mesh_name in embedder.mesh_names: # pyre-ignore[29] losses.append(embedder(mesh_name).sum() * 0) return torch.mean(torch.stack(losses)) def _get_embeddings_and_geodists_for_mesh( self, embedder: nn.Module, mesh_name: str ) -> Tuple[torch.Tensor, torch.Tensor]: """ Produces embeddings and geodesic distance tensors for a given mesh. May subsample the mesh, if it contains too many vertices (controlled by SHAPE_CYCLE_LOSS_MAX_NUM_VERTICES parameter). Args: embedder (nn.Module): module that computes embeddings for mesh vertices mesh_name (str): mesh name Return: embeddings (torch.Tensor of size [N, D]): embeddings for selected mesh vertices (N = number of selected vertices, D = embedding space dim) geodists (torch.Tensor of size [N, N]): geodesic distances for the selected mesh vertices (N = number of selected vertices) """ embeddings = embedder(mesh_name) indices = sample_random_indices( embeddings.shape[0], self.max_num_vertices, embeddings.device ) mesh = create_mesh(mesh_name, embeddings.device) geodists = mesh.geodists if indices is not None: embeddings = embeddings[indices] geodists = geodists[torch.meshgrid(indices, indices)] return embeddings, geodists def _forward_one_pair( self, embedder: nn.Module, mesh_name_1: str, mesh_name_2: str ) -> torch.Tensor: """ Do a forward pass with a selected pair of meshes Args: embedder (nn.Module): module that computes vertex embeddings for different meshes mesh_name_1 (str): first mesh name mesh_name_2 (str): second mesh name Return: Tensor containing the loss value """ embeddings_1, geodists_1 = self._get_embeddings_and_geodists_for_mesh(embedder, mesh_name_1) embeddings_2, geodists_2 = self._get_embeddings_and_geodists_for_mesh(embedder, mesh_name_2) sim_matrix_12 = embeddings_1.mm(embeddings_2.T) c_12 = F.softmax(sim_matrix_12 / self.temperature, dim=1) c_21 = F.softmax(sim_matrix_12.T / self.temperature, dim=1) c_11 = c_12.mm(c_21) c_22 = c_21.mm(c_12) loss_cycle_11 = torch.norm(geodists_1 * c_11, p=self.norm_p) loss_cycle_22 = torch.norm(geodists_2 * c_22, p=self.norm_p) return loss_cycle_11 + loss_cycle_22 ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/embed.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from typing import Any, Dict, List import torch from torch import nn from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.structures import Instances from densepose.data.meshes.catalog import MeshCatalog from densepose.modeling.cse.utils import normalize_embeddings, squared_euclidean_distance_matrix from .embed_utils import PackedCseAnnotations from .utils import BilinearInterpolationHelper class EmbeddingLoss: """ Computes losses for estimated embeddings given annotated vertices. Instances in a minibatch that correspond to the same mesh are grouped together. For each group, loss is computed as cross-entropy for unnormalized scores given ground truth mesh vertex ids. Scores are based on squared distances between estimated vertex embeddings and mesh vertex embeddings. """ def __init__(self, cfg: CfgNode): """ Initialize embedding loss from config """ self.embdist_gauss_sigma = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDING_DIST_GAUSS_SIGMA def __call__( self, proposals_with_gt: List[Instances], densepose_predictor_outputs: Any, packed_annotations: PackedCseAnnotations, interpolator: BilinearInterpolationHelper, embedder: nn.Module, ) -> Dict[int, torch.Tensor]: """ Produces losses for estimated embeddings given annotated vertices. Embeddings for all the vertices of a mesh are computed by the embedder. Embeddings for observed pixels are estimated by a predictor. Losses are computed as cross-entropy for squared distances between observed vertex embeddings and all mesh vertex embeddings given ground truth vertex IDs. Args: proposals_with_gt (list of Instances): detections with associated ground truth data; each item corresponds to instances detected on 1 image; the number of items corresponds to the number of images in a batch densepose_predictor_outputs: an object of a dataclass that contains predictor outputs with estimated values; assumed to have the following attributes: * embedding - embedding estimates, tensor of shape [N, D, S, S], where N = number of instances (= sum N_i, where N_i is the number of instances on image i) D = embedding space dimensionality (MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_SIZE) S = output size (width and height) packed_annotations (PackedCseAnnotations): contains various data useful for loss computation, each data is packed into a single tensor interpolator (BilinearInterpolationHelper): bilinear interpolation helper embedder (nn.Module): module that computes vertex embeddings for different meshes Return: dict(int -> tensor): losses for different mesh IDs """ losses = {} for mesh_id_tensor in packed_annotations.vertex_mesh_ids_gt.unique(): mesh_id = mesh_id_tensor.item() mesh_name = MeshCatalog.get_mesh_name(mesh_id) # valid points are those that fall into estimated bbox # and correspond to the current mesh j_valid = interpolator.j_valid * ( # pyre-ignore[16] packed_annotations.vertex_mesh_ids_gt == mesh_id ) if not torch.any(j_valid): continue # extract estimated embeddings for valid points # -> tensor [J, D] vertex_embeddings_i = normalize_embeddings( interpolator.extract_at_points( densepose_predictor_outputs.embedding, slice_fine_segm=slice(None), w_ylo_xlo=interpolator.w_ylo_xlo[:, None], # pyre-ignore[16] w_ylo_xhi=interpolator.w_ylo_xhi[:, None], # pyre-ignore[16] w_yhi_xlo=interpolator.w_yhi_xlo[:, None], # pyre-ignore[16] w_yhi_xhi=interpolator.w_yhi_xhi[:, None], # pyre-ignore[16] )[j_valid, :] ) # extract vertex ids for valid points # -> tensor [J] vertex_indices_i = packed_annotations.vertex_ids_gt[j_valid] # embeddings for all mesh vertices # -> tensor [K, D] mesh_vertex_embeddings = embedder(mesh_name) # unnormalized scores for valid points # -> tensor [J, K] scores = squared_euclidean_distance_matrix( vertex_embeddings_i, mesh_vertex_embeddings ) / (-self.embdist_gauss_sigma) losses[mesh_name] = F.cross_entropy(scores, vertex_indices_i, ignore_index=-1) # pyre-fixme[29]: # `Union[BoundMethod[typing.Callable(torch.Tensor.__iter__)[[Named(self, # torch.Tensor)], typing.Iterator[typing.Any]], torch.Tensor], nn.Module, # torch.Tensor]` is not a function. for mesh_name in embedder.mesh_names: if mesh_name not in losses: losses[mesh_name] = self.fake_value( densepose_predictor_outputs, embedder, mesh_name ) return losses def fake_values(self, densepose_predictor_outputs: Any, embedder: nn.Module): losses = {} # pyre-fixme[29]: # `Union[BoundMethod[typing.Callable(torch.Tensor.__iter__)[[Named(self, # torch.Tensor)], typing.Iterator[typing.Any]], torch.Tensor], nn.Module, # torch.Tensor]` is not a function. for mesh_name in embedder.mesh_names: losses[mesh_name] = self.fake_value(densepose_predictor_outputs, embedder, mesh_name) return losses def fake_value(self, densepose_predictor_outputs: Any, embedder: nn.Module, mesh_name: str): return densepose_predictor_outputs.embedding.sum() * 0 + embedder(mesh_name).sum() * 0 ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/embed_utils.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from dataclasses import dataclass from typing import Any, Optional import torch from detectron2.structures import BoxMode, Instances from .utils import AnnotationsAccumulator @dataclass class PackedCseAnnotations: x_gt: torch.Tensor y_gt: torch.Tensor coarse_segm_gt: Optional[torch.Tensor] vertex_mesh_ids_gt: torch.Tensor vertex_ids_gt: torch.Tensor bbox_xywh_gt: torch.Tensor bbox_xywh_est: torch.Tensor point_bbox_with_dp_indices: torch.Tensor point_bbox_indices: torch.Tensor bbox_indices: torch.Tensor class CseAnnotationsAccumulator(AnnotationsAccumulator): """ Accumulates annotations by batches that correspond to objects detected on individual images. Can pack them together into single tensors. """ def __init__(self): self.x_gt = [] self.y_gt = [] self.s_gt = [] self.vertex_mesh_ids_gt = [] self.vertex_ids_gt = [] self.bbox_xywh_gt = [] self.bbox_xywh_est = [] self.point_bbox_with_dp_indices = [] self.point_bbox_indices = [] self.bbox_indices = [] self.nxt_bbox_with_dp_index = 0 self.nxt_bbox_index = 0 def accumulate(self, instances_one_image: Instances): """ Accumulate instances data for one image Args: instances_one_image (Instances): instances data to accumulate """ boxes_xywh_est = BoxMode.convert( instances_one_image.proposal_boxes.tensor.clone(), BoxMode.XYXY_ABS, BoxMode.XYWH_ABS ) boxes_xywh_gt = BoxMode.convert( instances_one_image.gt_boxes.tensor.clone(), BoxMode.XYXY_ABS, BoxMode.XYWH_ABS ) n_matches = len(boxes_xywh_gt) assert n_matches == len( boxes_xywh_est ), f"Got {len(boxes_xywh_est)} proposal boxes and {len(boxes_xywh_gt)} GT boxes" if not n_matches: # no detection - GT matches return if ( not hasattr(instances_one_image, "gt_densepose") or instances_one_image.gt_densepose is None ): # no densepose GT for the detections, just increase the bbox index self.nxt_bbox_index += n_matches return for box_xywh_est, box_xywh_gt, dp_gt in zip( boxes_xywh_est, boxes_xywh_gt, instances_one_image.gt_densepose ): if (dp_gt is not None) and (len(dp_gt.x) > 0): self._do_accumulate(box_xywh_gt, box_xywh_est, dp_gt) self.nxt_bbox_index += 1 def _do_accumulate(self, box_xywh_gt: torch.Tensor, box_xywh_est: torch.Tensor, dp_gt: Any): """ Accumulate instances data for one image, given that the data is not empty Args: box_xywh_gt (tensor): GT bounding box box_xywh_est (tensor): estimated bounding box dp_gt: GT densepose data with the following attributes: - x: normalized X coordinates - y: normalized Y coordinates - segm: tensor of size [S, S] with coarse segmentation - """ self.x_gt.append(dp_gt.x) self.y_gt.append(dp_gt.y) if hasattr(dp_gt, "segm"): self.s_gt.append(dp_gt.segm.unsqueeze(0)) self.vertex_ids_gt.append(dp_gt.vertex_ids) self.vertex_mesh_ids_gt.append(torch.full_like(dp_gt.vertex_ids, dp_gt.mesh_id)) self.bbox_xywh_gt.append(box_xywh_gt.view(-1, 4)) self.bbox_xywh_est.append(box_xywh_est.view(-1, 4)) self.point_bbox_with_dp_indices.append( torch.full_like(dp_gt.vertex_ids, self.nxt_bbox_with_dp_index) ) self.point_bbox_indices.append(torch.full_like(dp_gt.vertex_ids, self.nxt_bbox_index)) self.bbox_indices.append(self.nxt_bbox_index) self.nxt_bbox_with_dp_index += 1 def pack(self) -> Optional[PackedCseAnnotations]: """ Pack data into tensors """ if not len(self.x_gt): # TODO: # returning proper empty annotations would require # creating empty tensors of appropriate shape and # type on an appropriate device; # we return None so far to indicate empty annotations return None return PackedCseAnnotations( x_gt=torch.cat(self.x_gt, 0), y_gt=torch.cat(self.y_gt, 0), vertex_mesh_ids_gt=torch.cat(self.vertex_mesh_ids_gt, 0), vertex_ids_gt=torch.cat(self.vertex_ids_gt, 0), # ignore segmentation annotations, if not all the instances contain those coarse_segm_gt=torch.cat(self.s_gt, 0) if len(self.s_gt) == len(self.bbox_xywh_gt) else None, bbox_xywh_gt=torch.cat(self.bbox_xywh_gt, 0), bbox_xywh_est=torch.cat(self.bbox_xywh_est, 0), point_bbox_with_dp_indices=torch.cat(self.point_bbox_with_dp_indices, 0), point_bbox_indices=torch.cat(self.point_bbox_indices, 0), bbox_indices=torch.as_tensor( self.bbox_indices, dtype=torch.long, device=self.x_gt[0].device ), ) ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/mask.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from dataclasses import dataclass from typing import Any, Iterable, List, Optional import torch from torch.nn import functional as F from detectron2.structures import Instances @dataclass class DataForMaskLoss: """ Contains mask GT and estimated data for proposals from multiple images: """ # tensor of size (K, H, W) containing GT labels masks_gt: Optional[torch.Tensor] = None # tensor of size (K, C, H, W) containing estimated scores masks_est: Optional[torch.Tensor] = None def extract_data_for_mask_loss_from_matches( proposals_targets: Iterable[Instances], estimated_segm: torch.Tensor ) -> DataForMaskLoss: """ Extract data for mask loss from instances that contain matched GT and estimated bounding boxes. Args: proposals_targets: Iterable[Instances] matched GT and estimated results, each item in the iterable corresponds to data in 1 image estimated_segm: tensor(K, C, S, S) of float - raw unnormalized segmentation scores, here S is the size to which GT masks are to be resized Return: masks_est: tensor(K, C, S, S) of float - class scores masks_gt: tensor(K, S, S) of int64 - labels """ data = DataForMaskLoss() masks_gt = [] offset = 0 assert estimated_segm.shape[2] == estimated_segm.shape[3], ( f"Expected estimated segmentation to have a square shape, " f"but the actual shape is {estimated_segm.shape[2:]}" ) mask_size = estimated_segm.shape[2] num_proposals = sum(inst.proposal_boxes.tensor.size(0) for inst in proposals_targets) num_estimated = estimated_segm.shape[0] assert ( num_proposals == num_estimated ), "The number of proposals {} must be equal to the number of estimates {}".format( num_proposals, num_estimated ) for proposals_targets_per_image in proposals_targets: n_i = proposals_targets_per_image.proposal_boxes.tensor.size(0) if not n_i: continue gt_masks_per_image = proposals_targets_per_image.gt_masks.crop_and_resize( proposals_targets_per_image.proposal_boxes.tensor, mask_size ).to(device=estimated_segm.device) masks_gt.append(gt_masks_per_image) offset += n_i if masks_gt: data.masks_est = estimated_segm data.masks_gt = torch.cat(masks_gt, dim=0) return data class MaskLoss: """ Mask loss as cross-entropy for raw unnormalized scores given ground truth labels. Mask ground truth labels are defined for the whole image and not only the bounding box of interest. They are stored as objects that are assumed to implement the `crop_and_resize` interface (e.g. BitMasks, PolygonMasks). """ def __call__( self, proposals_with_gt: List[Instances], densepose_predictor_outputs: Any ) -> torch.Tensor: """ Computes segmentation loss as cross-entropy for raw unnormalized scores given ground truth labels. Args: proposals_with_gt (list of Instances): detections with associated ground truth data densepose_predictor_outputs: an object of a dataclass that contains predictor outputs with estimated values; assumed to have the following attribute: * coarse_segm (tensor of shape [N, D, S, S]): coarse segmentation estimates as raw unnormalized scores where N is the number of detections, S is the estimate size ( = width = height) and D is the number of coarse segmentation channels. Return: Cross entropy for raw unnormalized scores for coarse segmentation given ground truth labels from masks """ if not len(proposals_with_gt): return self.fake_value(densepose_predictor_outputs) # densepose outputs are computed for all images and all bounding boxes; # i.e. if a batch has 4 images with (3, 1, 2, 1) proposals respectively, # the outputs will have size(0) == 3+1+2+1 == 7 with torch.no_grad(): mask_loss_data = extract_data_for_mask_loss_from_matches( proposals_with_gt, densepose_predictor_outputs.coarse_segm ) if (mask_loss_data.masks_gt is None) or (mask_loss_data.masks_est is None): return self.fake_value(densepose_predictor_outputs) return F.cross_entropy(mask_loss_data.masks_est, mask_loss_data.masks_gt.long()) def fake_value(self, densepose_predictor_outputs: Any) -> torch.Tensor: """ Fake segmentation loss used when no suitable ground truth data was found in a batch. The loss has a value 0 and is primarily used to construct the computation graph, so that `DistributedDataParallel` has similar graphs on all GPUs and can perform reduction properly. Args: densepose_predictor_outputs: DensePose predictor outputs, an object of a dataclass that is assumed to have `coarse_segm` attribute Return: Zero value loss with proper computation graph """ return densepose_predictor_outputs.coarse_segm.sum() * 0 ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/mask_or_segm.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from typing import Any, List import torch from detectron2.config import CfgNode from detectron2.structures import Instances from .mask import MaskLoss from .segm import SegmentationLoss class MaskOrSegmentationLoss: """ Mask or segmentation loss as cross-entropy for raw unnormalized scores given ground truth labels. Ground truth labels are either defined by coarse segmentation annotation, or by mask annotation, depending on the config value MODEL.ROI_DENSEPOSE_HEAD.COARSE_SEGM_TRAINED_BY_MASKS """ def __init__(self, cfg: CfgNode): """ Initialize segmentation loss from configuration options Args: cfg (CfgNode): configuration options """ self.segm_trained_by_masks = cfg.MODEL.ROI_DENSEPOSE_HEAD.COARSE_SEGM_TRAINED_BY_MASKS if self.segm_trained_by_masks: self.mask_loss = MaskLoss() self.segm_loss = SegmentationLoss(cfg) def __call__( self, proposals_with_gt: List[Instances], densepose_predictor_outputs: Any, packed_annotations: Any, ) -> torch.Tensor: """ Compute segmentation loss as cross-entropy between aligned unnormalized score estimates and ground truth; with ground truth given either by masks, or by coarse segmentation annotations. Args: proposals_with_gt (list of Instances): detections with associated ground truth data densepose_predictor_outputs: an object of a dataclass that contains predictor outputs with estimated values; assumed to have the following attributes: * coarse_segm - coarse segmentation estimates, tensor of shape [N, D, S, S] packed_annotations: packed annotations for efficient loss computation Return: tensor: loss value as cross-entropy for raw unnormalized scores given ground truth labels """ if self.segm_trained_by_masks: return self.mask_loss(proposals_with_gt, densepose_predictor_outputs) return self.segm_loss(proposals_with_gt, densepose_predictor_outputs, packed_annotations) def fake_value(self, densepose_predictor_outputs: Any) -> torch.Tensor: """ Fake segmentation loss used when no suitable ground truth data was found in a batch. The loss has a value 0 and is primarily used to construct the computation graph, so that `DistributedDataParallel` has similar graphs on all GPUs and can perform reduction properly. Args: densepose_predictor_outputs: DensePose predictor outputs, an object of a dataclass that is assumed to have `coarse_segm` attribute Return: Zero value loss with proper computation graph """ return densepose_predictor_outputs.coarse_segm.sum() * 0 ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/registry.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.utils.registry import Registry DENSEPOSE_LOSS_REGISTRY = Registry("DENSEPOSE_LOSS") ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/segm.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from typing import Any, List import torch from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.structures import Instances from .utils import resample_data class SegmentationLoss: """ Segmentation loss as cross-entropy for raw unnormalized scores given ground truth labels. Segmentation ground truth labels are defined for the bounding box of interest at some fixed resolution [S, S], where S = MODEL.ROI_DENSEPOSE_HEAD.HEATMAP_SIZE. """ def __init__(self, cfg: CfgNode): """ Initialize segmentation loss from configuration options Args: cfg (CfgNode): configuration options """ self.heatmap_size = cfg.MODEL.ROI_DENSEPOSE_HEAD.HEATMAP_SIZE self.n_segm_chan = cfg.MODEL.ROI_DENSEPOSE_HEAD.NUM_COARSE_SEGM_CHANNELS def __call__( self, proposals_with_gt: List[Instances], densepose_predictor_outputs: Any, packed_annotations: Any, ) -> torch.Tensor: """ Compute segmentation loss as cross-entropy on aligned segmentation ground truth and estimated scores. Args: proposals_with_gt (list of Instances): detections with associated ground truth data densepose_predictor_outputs: an object of a dataclass that contains predictor outputs with estimated values; assumed to have the following attributes: * coarse_segm - coarse segmentation estimates, tensor of shape [N, D, S, S] packed_annotations: packed annotations for efficient loss computation; the following attributes are used: - coarse_segm_gt - bbox_xywh_gt - bbox_xywh_est """ if packed_annotations.coarse_segm_gt is None: return self.fake_value(densepose_predictor_outputs) coarse_segm_est = densepose_predictor_outputs.coarse_segm[packed_annotations.bbox_indices] with torch.no_grad(): coarse_segm_gt = resample_data( packed_annotations.coarse_segm_gt.unsqueeze(1), packed_annotations.bbox_xywh_gt, packed_annotations.bbox_xywh_est, self.heatmap_size, self.heatmap_size, mode="nearest", padding_mode="zeros", ).squeeze(1) if self.n_segm_chan == 2: coarse_segm_gt = coarse_segm_gt > 0 return F.cross_entropy(coarse_segm_est, coarse_segm_gt.long()) def fake_value(self, densepose_predictor_outputs: Any) -> torch.Tensor: """ Fake segmentation loss used when no suitable ground truth data was found in a batch. The loss has a value 0 and is primarily used to construct the computation graph, so that `DistributedDataParallel` has similar graphs on all GPUs and can perform reduction properly. Args: densepose_predictor_outputs: DensePose predictor outputs, an object of a dataclass that is assumed to have `coarse_segm` attribute Return: Zero value loss with proper computation graph """ return densepose_predictor_outputs.coarse_segm.sum() * 0 ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/soft_embed.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from typing import Any, Dict, List import torch from torch import nn from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.structures import Instances from densepose.data.meshes.catalog import MeshCatalog from densepose.modeling.cse.utils import normalize_embeddings, squared_euclidean_distance_matrix from densepose.structures.mesh import create_mesh from .embed_utils import PackedCseAnnotations from .utils import BilinearInterpolationHelper class SoftEmbeddingLoss: """ Computes losses for estimated embeddings given annotated vertices. Instances in a minibatch that correspond to the same mesh are grouped together. For each group, loss is computed as cross-entropy for unnormalized scores given ground truth mesh vertex ids. Scores are based on: 1) squared distances between estimated vertex embeddings and mesh vertex embeddings; 2) geodesic distances between vertices of a mesh """ def __init__(self, cfg: CfgNode): """ Initialize embedding loss from config """ self.embdist_gauss_sigma = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBEDDING_DIST_GAUSS_SIGMA self.geodist_gauss_sigma = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.GEODESIC_DIST_GAUSS_SIGMA def __call__( self, proposals_with_gt: List[Instances], densepose_predictor_outputs: Any, packed_annotations: PackedCseAnnotations, interpolator: BilinearInterpolationHelper, embedder: nn.Module, ) -> Dict[int, torch.Tensor]: """ Produces losses for estimated embeddings given annotated vertices. Embeddings for all the vertices of a mesh are computed by the embedder. Embeddings for observed pixels are estimated by a predictor. Losses are computed as cross-entropy for unnormalized scores given ground truth vertex IDs. 1) squared distances between estimated vertex embeddings and mesh vertex embeddings; 2) geodesic distances between vertices of a mesh Args: proposals_with_gt (list of Instances): detections with associated ground truth data; each item corresponds to instances detected on 1 image; the number of items corresponds to the number of images in a batch densepose_predictor_outputs: an object of a dataclass that contains predictor outputs with estimated values; assumed to have the following attributes: * embedding - embedding estimates, tensor of shape [N, D, S, S], where N = number of instances (= sum N_i, where N_i is the number of instances on image i) D = embedding space dimensionality (MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_SIZE) S = output size (width and height) packed_annotations (PackedCseAnnotations): contains various data useful for loss computation, each data is packed into a single tensor interpolator (BilinearInterpolationHelper): bilinear interpolation helper embedder (nn.Module): module that computes vertex embeddings for different meshes Return: dict(int -> tensor): losses for different mesh IDs """ losses = {} for mesh_id_tensor in packed_annotations.vertex_mesh_ids_gt.unique(): mesh_id = mesh_id_tensor.item() mesh_name = MeshCatalog.get_mesh_name(mesh_id) # valid points are those that fall into estimated bbox # and correspond to the current mesh j_valid = interpolator.j_valid * ( # pyre-ignore[16] packed_annotations.vertex_mesh_ids_gt == mesh_id ) if not torch.any(j_valid): continue # extract estimated embeddings for valid points # -> tensor [J, D] vertex_embeddings_i = normalize_embeddings( interpolator.extract_at_points( densepose_predictor_outputs.embedding, slice_fine_segm=slice(None), w_ylo_xlo=interpolator.w_ylo_xlo[:, None], # pyre-ignore[16] w_ylo_xhi=interpolator.w_ylo_xhi[:, None], # pyre-ignore[16] w_yhi_xlo=interpolator.w_yhi_xlo[:, None], # pyre-ignore[16] w_yhi_xhi=interpolator.w_yhi_xhi[:, None], # pyre-ignore[16] )[j_valid, :] ) # extract vertex ids for valid points # -> tensor [J] vertex_indices_i = packed_annotations.vertex_ids_gt[j_valid] # embeddings for all mesh vertices # -> tensor [K, D] mesh_vertex_embeddings = embedder(mesh_name) # softmax values of geodesic distances for GT mesh vertices # -> tensor [J, K] mesh = create_mesh(mesh_name, mesh_vertex_embeddings.device) geodist_softmax_values = F.softmax( mesh.geodists[vertex_indices_i] / (-self.geodist_gauss_sigma), dim=1 ) # logsoftmax values for valid points # -> tensor [J, K] embdist_logsoftmax_values = F.log_softmax( squared_euclidean_distance_matrix(vertex_embeddings_i, mesh_vertex_embeddings) / (-self.embdist_gauss_sigma), dim=1, ) losses[mesh_name] = (-geodist_softmax_values * embdist_logsoftmax_values).sum(1).mean() # pyre-fixme[29]: # `Union[BoundMethod[typing.Callable(torch.Tensor.__iter__)[[Named(self, # torch.Tensor)], typing.Iterator[typing.Any]], torch.Tensor], nn.Module, # torch.Tensor]` is not a function. for mesh_name in embedder.mesh_names: if mesh_name not in losses: losses[mesh_name] = self.fake_value( densepose_predictor_outputs, embedder, mesh_name ) return losses def fake_values(self, densepose_predictor_outputs: Any, embedder: nn.Module): losses = {} # pyre-fixme[29]: # `Union[BoundMethod[typing.Callable(torch.Tensor.__iter__)[[Named(self, # torch.Tensor)], typing.Iterator[typing.Any]], torch.Tensor], nn.Module, # torch.Tensor]` is not a function. for mesh_name in embedder.mesh_names: losses[mesh_name] = self.fake_value(densepose_predictor_outputs, embedder, mesh_name) return losses def fake_value(self, densepose_predictor_outputs: Any, embedder: nn.Module, mesh_name: str): return densepose_predictor_outputs.embedding.sum() * 0 + embedder(mesh_name).sum() * 0 ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/losses/utils.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Any, Dict, List, Optional, Tuple import torch from torch.nn import functional as F from detectron2.structures import BoxMode, Instances from densepose import DensePoseDataRelative LossDict = Dict[str, torch.Tensor] def _linear_interpolation_utilities(v_norm, v0_src, size_src, v0_dst, size_dst, size_z): """ Computes utility values for linear interpolation at points v. The points are given as normalized offsets in the source interval (v0_src, v0_src + size_src), more precisely: v = v0_src + v_norm * size_src / 256.0 The computed utilities include lower points v_lo, upper points v_hi, interpolation weights v_w and flags j_valid indicating whether the points falls into the destination interval (v0_dst, v0_dst + size_dst). Args: v_norm (:obj: `torch.Tensor`): tensor of size N containing normalized point offsets v0_src (:obj: `torch.Tensor`): tensor of size N containing left bounds of source intervals for normalized points size_src (:obj: `torch.Tensor`): tensor of size N containing source interval sizes for normalized points v0_dst (:obj: `torch.Tensor`): tensor of size N containing left bounds of destination intervals size_dst (:obj: `torch.Tensor`): tensor of size N containing destination interval sizes size_z (int): interval size for data to be interpolated Returns: v_lo (:obj: `torch.Tensor`): int tensor of size N containing indices of lower values used for interpolation, all values are integers from [0, size_z - 1] v_hi (:obj: `torch.Tensor`): int tensor of size N containing indices of upper values used for interpolation, all values are integers from [0, size_z - 1] v_w (:obj: `torch.Tensor`): float tensor of size N containing interpolation weights j_valid (:obj: `torch.Tensor`): uint8 tensor of size N containing 0 for points outside the estimation interval (v0_est, v0_est + size_est) and 1 otherwise """ v = v0_src + v_norm * size_src / 256.0 j_valid = (v - v0_dst >= 0) * (v - v0_dst < size_dst) v_grid = (v - v0_dst) * size_z / size_dst v_lo = v_grid.floor().long().clamp(min=0, max=size_z - 1) v_hi = (v_lo + 1).clamp(max=size_z - 1) v_grid = torch.min(v_hi.float(), v_grid) v_w = v_grid - v_lo.float() return v_lo, v_hi, v_w, j_valid class BilinearInterpolationHelper: """ Args: packed_annotations: object that contains packed annotations j_valid (:obj: `torch.Tensor`): uint8 tensor of size M containing 0 for points to be discarded and 1 for points to be selected y_lo (:obj: `torch.Tensor`): int tensor of indices of upper values in z_est for each point y_hi (:obj: `torch.Tensor`): int tensor of indices of lower values in z_est for each point x_lo (:obj: `torch.Tensor`): int tensor of indices of left values in z_est for each point x_hi (:obj: `torch.Tensor`): int tensor of indices of right values in z_est for each point w_ylo_xlo (:obj: `torch.Tensor`): float tensor of size M; contains upper-left value weight for each point w_ylo_xhi (:obj: `torch.Tensor`): float tensor of size M; contains upper-right value weight for each point w_yhi_xlo (:obj: `torch.Tensor`): float tensor of size M; contains lower-left value weight for each point w_yhi_xhi (:obj: `torch.Tensor`): float tensor of size M; contains lower-right value weight for each point """ def __init__( self, packed_annotations: Any, j_valid: torch.Tensor, y_lo: torch.Tensor, y_hi: torch.Tensor, x_lo: torch.Tensor, x_hi: torch.Tensor, w_ylo_xlo: torch.Tensor, w_ylo_xhi: torch.Tensor, w_yhi_xlo: torch.Tensor, w_yhi_xhi: torch.Tensor, ): for k, v in locals().items(): if k != "self": setattr(self, k, v) @staticmethod def from_matches( packed_annotations: Any, densepose_outputs_size_hw: Tuple[int, int] ) -> "BilinearInterpolationHelper": """ Args: packed_annotations: annotations packed into tensors, the following attributes are required: - bbox_xywh_gt - bbox_xywh_est - x_gt - y_gt - point_bbox_with_dp_indices - point_bbox_indices densepose_outputs_size_hw (tuple [int, int]): resolution of DensePose predictor outputs (H, W) Return: An instance of `BilinearInterpolationHelper` used to perform interpolation for the given annotation points and output resolution """ zh, zw = densepose_outputs_size_hw x0_gt, y0_gt, w_gt, h_gt = packed_annotations.bbox_xywh_gt[ packed_annotations.point_bbox_with_dp_indices ].unbind(dim=1) x0_est, y0_est, w_est, h_est = packed_annotations.bbox_xywh_est[ packed_annotations.point_bbox_with_dp_indices ].unbind(dim=1) x_lo, x_hi, x_w, jx_valid = _linear_interpolation_utilities( packed_annotations.x_gt, x0_gt, w_gt, x0_est, w_est, zw ) y_lo, y_hi, y_w, jy_valid = _linear_interpolation_utilities( packed_annotations.y_gt, y0_gt, h_gt, y0_est, h_est, zh ) j_valid = jx_valid * jy_valid w_ylo_xlo = (1.0 - x_w) * (1.0 - y_w) w_ylo_xhi = x_w * (1.0 - y_w) w_yhi_xlo = (1.0 - x_w) * y_w w_yhi_xhi = x_w * y_w return BilinearInterpolationHelper( packed_annotations, j_valid, y_lo, y_hi, x_lo, x_hi, w_ylo_xlo, # pyre-ignore[6] w_ylo_xhi, # pyre-fixme[6]: Expected `Tensor` for 9th param but got `float`. w_yhi_xlo, w_yhi_xhi, ) def extract_at_points( self, z_est, slice_fine_segm=None, w_ylo_xlo=None, w_ylo_xhi=None, w_yhi_xlo=None, w_yhi_xhi=None, ): """ Extract ground truth values z_gt for valid point indices and estimated values z_est using bilinear interpolation over top-left (y_lo, x_lo), top-right (y_lo, x_hi), bottom-left (y_hi, x_lo) and bottom-right (y_hi, x_hi) values in z_est with corresponding weights: w_ylo_xlo, w_ylo_xhi, w_yhi_xlo and w_yhi_xhi. Use slice_fine_segm to slice dim=1 in z_est """ slice_fine_segm = ( self.packed_annotations.fine_segm_labels_gt if slice_fine_segm is None else slice_fine_segm ) w_ylo_xlo = self.w_ylo_xlo if w_ylo_xlo is None else w_ylo_xlo w_ylo_xhi = self.w_ylo_xhi if w_ylo_xhi is None else w_ylo_xhi w_yhi_xlo = self.w_yhi_xlo if w_yhi_xlo is None else w_yhi_xlo w_yhi_xhi = self.w_yhi_xhi if w_yhi_xhi is None else w_yhi_xhi index_bbox = self.packed_annotations.point_bbox_indices z_est_sampled = ( z_est[index_bbox, slice_fine_segm, self.y_lo, self.x_lo] * w_ylo_xlo + z_est[index_bbox, slice_fine_segm, self.y_lo, self.x_hi] * w_ylo_xhi + z_est[index_bbox, slice_fine_segm, self.y_hi, self.x_lo] * w_yhi_xlo + z_est[index_bbox, slice_fine_segm, self.y_hi, self.x_hi] * w_yhi_xhi ) return z_est_sampled def resample_data( z, bbox_xywh_src, bbox_xywh_dst, wout, hout, mode: str = "nearest", padding_mode: str = "zeros" ): """ Args: z (:obj: `torch.Tensor`): tensor of size (N,C,H,W) with data to be resampled bbox_xywh_src (:obj: `torch.Tensor`): tensor of size (N,4) containing source bounding boxes in format XYWH bbox_xywh_dst (:obj: `torch.Tensor`): tensor of size (N,4) containing destination bounding boxes in format XYWH Return: zresampled (:obj: `torch.Tensor`): tensor of size (N, C, Hout, Wout) with resampled values of z, where D is the discretization size """ n = bbox_xywh_src.size(0) assert n == bbox_xywh_dst.size(0), ( "The number of " "source ROIs for resampling ({}) should be equal to the number " "of destination ROIs ({})".format(bbox_xywh_src.size(0), bbox_xywh_dst.size(0)) ) x0src, y0src, wsrc, hsrc = bbox_xywh_src.unbind(dim=1) x0dst, y0dst, wdst, hdst = bbox_xywh_dst.unbind(dim=1) x0dst_norm = 2 * (x0dst - x0src) / wsrc - 1 y0dst_norm = 2 * (y0dst - y0src) / hsrc - 1 x1dst_norm = 2 * (x0dst + wdst - x0src) / wsrc - 1 y1dst_norm = 2 * (y0dst + hdst - y0src) / hsrc - 1 grid_w = torch.arange(wout, device=z.device, dtype=torch.float) / wout grid_h = torch.arange(hout, device=z.device, dtype=torch.float) / hout grid_w_expanded = grid_w[None, None, :].expand(n, hout, wout) grid_h_expanded = grid_h[None, :, None].expand(n, hout, wout) dx_expanded = (x1dst_norm - x0dst_norm)[:, None, None].expand(n, hout, wout) dy_expanded = (y1dst_norm - y0dst_norm)[:, None, None].expand(n, hout, wout) x0_expanded = x0dst_norm[:, None, None].expand(n, hout, wout) y0_expanded = y0dst_norm[:, None, None].expand(n, hout, wout) grid_x = grid_w_expanded * dx_expanded + x0_expanded grid_y = grid_h_expanded * dy_expanded + y0_expanded grid = torch.stack((grid_x, grid_y), dim=3) # resample Z from (N, C, H, W) into (N, C, Hout, Wout) zresampled = F.grid_sample(z, grid, mode=mode, padding_mode=padding_mode, align_corners=True) return zresampled class AnnotationsAccumulator(ABC): """ Abstract class for an accumulator for annotations that can produce dense annotations packed into tensors. """ @abstractmethod def accumulate(self, instances_one_image: Instances): """ Accumulate instances data for one image Args: instances_one_image (Instances): instances data to accumulate """ pass @abstractmethod def pack(self) -> Any: """ Pack data into tensors """ pass @dataclass class PackedChartBasedAnnotations: """ Packed annotations for chart-based model training. The following attributes are defined: - fine_segm_labels_gt (tensor [K] of `int64`): GT fine segmentation point labels - x_gt (tensor [K] of `float32`): GT normalized X point coordinates - y_gt (tensor [K] of `float32`): GT normalized Y point coordinates - u_gt (tensor [K] of `float32`): GT point U values - v_gt (tensor [K] of `float32`): GT point V values - coarse_segm_gt (tensor [N, S, S] of `float32`): GT segmentation for bounding boxes - bbox_xywh_gt (tensor [N, 4] of `float32`): selected GT bounding boxes in XYWH format - bbox_xywh_est (tensor [N, 4] of `float32`): selected matching estimated bounding boxes in XYWH format - point_bbox_with_dp_indices (tensor [K] of `int64`): indices of bounding boxes with DensePose annotations that correspond to the point data - point_bbox_indices (tensor [K] of `int64`): indices of bounding boxes (not necessarily the selected ones with DensePose data) that correspond to the point data - bbox_indices (tensor [N] of `int64`): global indices of selected bounding boxes with DensePose annotations; these indices could be used to access features that are computed for all bounding boxes, not only the ones with DensePose annotations. Here K is the total number of points and N is the total number of instances with DensePose annotations. """ fine_segm_labels_gt: torch.Tensor x_gt: torch.Tensor y_gt: torch.Tensor u_gt: torch.Tensor v_gt: torch.Tensor coarse_segm_gt: Optional[torch.Tensor] bbox_xywh_gt: torch.Tensor bbox_xywh_est: torch.Tensor point_bbox_with_dp_indices: torch.Tensor point_bbox_indices: torch.Tensor bbox_indices: torch.Tensor class ChartBasedAnnotationsAccumulator(AnnotationsAccumulator): """ Accumulates annotations by batches that correspond to objects detected on individual images. Can pack them together into single tensors. """ def __init__(self): self.i_gt = [] self.x_gt = [] self.y_gt = [] self.u_gt = [] self.v_gt = [] self.s_gt = [] self.bbox_xywh_gt = [] self.bbox_xywh_est = [] self.point_bbox_with_dp_indices = [] self.point_bbox_indices = [] self.bbox_indices = [] self.nxt_bbox_with_dp_index = 0 self.nxt_bbox_index = 0 def accumulate(self, instances_one_image: Instances): """ Accumulate instances data for one image Args: instances_one_image (Instances): instances data to accumulate """ boxes_xywh_est = BoxMode.convert( instances_one_image.proposal_boxes.tensor.clone(), BoxMode.XYXY_ABS, BoxMode.XYWH_ABS ) boxes_xywh_gt = BoxMode.convert( instances_one_image.gt_boxes.tensor.clone(), BoxMode.XYXY_ABS, BoxMode.XYWH_ABS ) n_matches = len(boxes_xywh_gt) assert n_matches == len( boxes_xywh_est ), f"Got {len(boxes_xywh_est)} proposal boxes and {len(boxes_xywh_gt)} GT boxes" if not n_matches: # no detection - GT matches return if ( not hasattr(instances_one_image, "gt_densepose") or instances_one_image.gt_densepose is None ): # no densepose GT for the detections, just increase the bbox index self.nxt_bbox_index += n_matches return for box_xywh_est, box_xywh_gt, dp_gt in zip( boxes_xywh_est, boxes_xywh_gt, instances_one_image.gt_densepose ): if (dp_gt is not None) and (len(dp_gt.x) > 0): self._do_accumulate(box_xywh_gt, box_xywh_est, dp_gt) self.nxt_bbox_index += 1 def _do_accumulate( self, box_xywh_gt: torch.Tensor, box_xywh_est: torch.Tensor, dp_gt: DensePoseDataRelative ): """ Accumulate instances data for one image, given that the data is not empty Args: box_xywh_gt (tensor): GT bounding box box_xywh_est (tensor): estimated bounding box dp_gt (DensePoseDataRelative): GT densepose data """ self.i_gt.append(dp_gt.i) self.x_gt.append(dp_gt.x) self.y_gt.append(dp_gt.y) self.u_gt.append(dp_gt.u) self.v_gt.append(dp_gt.v) if hasattr(dp_gt, "segm"): self.s_gt.append(dp_gt.segm.unsqueeze(0)) self.bbox_xywh_gt.append(box_xywh_gt.view(-1, 4)) self.bbox_xywh_est.append(box_xywh_est.view(-1, 4)) self.point_bbox_with_dp_indices.append( torch.full_like(dp_gt.i, self.nxt_bbox_with_dp_index) ) self.point_bbox_indices.append(torch.full_like(dp_gt.i, self.nxt_bbox_index)) self.bbox_indices.append(self.nxt_bbox_index) self.nxt_bbox_with_dp_index += 1 def pack(self) -> Optional[PackedChartBasedAnnotations]: """ Pack data into tensors """ if not len(self.i_gt): # TODO: # returning proper empty annotations would require # creating empty tensors of appropriate shape and # type on an appropriate device; # we return None so far to indicate empty annotations return None return PackedChartBasedAnnotations( fine_segm_labels_gt=torch.cat(self.i_gt, 0).long(), x_gt=torch.cat(self.x_gt, 0), y_gt=torch.cat(self.y_gt, 0), u_gt=torch.cat(self.u_gt, 0), v_gt=torch.cat(self.v_gt, 0), # ignore segmentation annotations, if not all the instances contain those coarse_segm_gt=torch.cat(self.s_gt, 0) if len(self.s_gt) == len(self.bbox_xywh_gt) else None, bbox_xywh_gt=torch.cat(self.bbox_xywh_gt, 0), bbox_xywh_est=torch.cat(self.bbox_xywh_est, 0), point_bbox_with_dp_indices=torch.cat(self.point_bbox_with_dp_indices, 0).long(), point_bbox_indices=torch.cat(self.point_bbox_indices, 0).long(), bbox_indices=torch.as_tensor( self.bbox_indices, dtype=torch.long, device=self.x_gt[0].device ).long(), ) def extract_packed_annotations_from_matches( proposals_with_targets: List[Instances], accumulator: AnnotationsAccumulator ) -> Any: for proposals_targets_per_image in proposals_with_targets: accumulator.accumulate(proposals_targets_per_image) return accumulator.pack() def sample_random_indices( n_indices: int, n_samples: int, device: Optional[torch.device] = None ) -> Optional[torch.Tensor]: """ Samples `n_samples` random indices from range `[0..n_indices - 1]`. If `n_indices` is smaller than `n_samples`, returns `None` meaning that all indices are selected. Args: n_indices (int): total number of indices n_samples (int): number of indices to sample device (torch.device): the desired device of returned tensor Return: Tensor of selected vertex indices, or `None`, if all vertices are selected """ if (n_samples <= 0) or (n_indices <= n_samples): return None indices = torch.randperm(n_indices, device=device)[:n_samples] return indices ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/predictors/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .chart import DensePoseChartPredictor from .chart_confidence import DensePoseChartConfidencePredictorMixin from .chart_with_confidence import DensePoseChartWithConfidencePredictor from .cse import DensePoseEmbeddingPredictor from .cse_confidence import DensePoseEmbeddingConfidencePredictorMixin from .cse_with_confidence import DensePoseEmbeddingWithConfidencePredictor from .registry import DENSEPOSE_PREDICTOR_REGISTRY ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/predictors/chart.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch from torch import nn from detectron2.config import CfgNode from detectron2.layers import ConvTranspose2d, interpolate from ...structures import DensePoseChartPredictorOutput from ..utils import initialize_module_params from .registry import DENSEPOSE_PREDICTOR_REGISTRY @DENSEPOSE_PREDICTOR_REGISTRY.register() class DensePoseChartPredictor(nn.Module): """ Predictor (last layers of a DensePose model) that takes DensePose head outputs as an input and produces 4 tensors which represent DensePose results for predefined body parts (patches / charts): * coarse segmentation, a tensor of shape [N, K, Hout, Wout] * fine segmentation, a tensor of shape [N, C, Hout, Wout] * U coordinates, a tensor of shape [N, C, Hout, Wout] * V coordinates, a tensor of shape [N, C, Hout, Wout] where - N is the number of instances - K is the number of coarse segmentation channels ( 2 = foreground / background, 15 = one of 14 body parts / background) - C is the number of fine segmentation channels ( 24 fine body parts / background) - Hout and Wout are height and width of predictions """ def __init__(self, cfg: CfgNode, input_channels: int): """ Initialize predictor using configuration options Args: cfg (CfgNode): configuration options input_channels (int): input tensor size along the channel dimension """ super().__init__() dim_in = input_channels n_segm_chan = cfg.MODEL.ROI_DENSEPOSE_HEAD.NUM_COARSE_SEGM_CHANNELS dim_out_patches = cfg.MODEL.ROI_DENSEPOSE_HEAD.NUM_PATCHES + 1 kernel_size = cfg.MODEL.ROI_DENSEPOSE_HEAD.DECONV_KERNEL # coarse segmentation self.ann_index_lowres = ConvTranspose2d( dim_in, n_segm_chan, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) # fine segmentation self.index_uv_lowres = ConvTranspose2d( dim_in, dim_out_patches, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) # U self.u_lowres = ConvTranspose2d( dim_in, dim_out_patches, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) # V self.v_lowres = ConvTranspose2d( dim_in, dim_out_patches, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) self.scale_factor = cfg.MODEL.ROI_DENSEPOSE_HEAD.UP_SCALE initialize_module_params(self) def interp2d(self, tensor_nchw: torch.Tensor): """ Bilinear interpolation method to be used for upscaling Args: tensor_nchw (tensor): tensor of shape (N, C, H, W) Return: tensor of shape (N, C, Hout, Wout), where Hout and Wout are computed by applying the scale factor to H and W """ return interpolate( tensor_nchw, scale_factor=self.scale_factor, mode="bilinear", align_corners=False ) def forward(self, head_outputs: torch.Tensor): """ Perform forward step on DensePose head outputs Args: head_outputs (tensor): DensePose head outputs, tensor of shape [N, D, H, W] Return: An instance of DensePoseChartPredictorOutput """ return DensePoseChartPredictorOutput( coarse_segm=self.interp2d(self.ann_index_lowres(head_outputs)), fine_segm=self.interp2d(self.index_uv_lowres(head_outputs)), u=self.interp2d(self.u_lowres(head_outputs)), v=self.interp2d(self.v_lowres(head_outputs)), ) ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/predictors/chart_confidence.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any import torch from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.layers import ConvTranspose2d from ...structures import decorate_predictor_output_class_with_confidences from ..confidence import DensePoseConfidenceModelConfig, DensePoseUVConfidenceType from ..utils import initialize_module_params class DensePoseChartConfidencePredictorMixin: """ Predictor contains the last layers of a DensePose model that take DensePose head outputs as an input and produce model outputs. Confidence predictor mixin is used to generate confidences for segmentation and UV tensors estimated by some base predictor. Several assumptions need to hold for the base predictor: 1) the `forward` method must return SIUV tuple as the first result ( S = coarse segmentation, I = fine segmentation, U and V are intrinsic chart coordinates) 2) `interp2d` method must be defined to perform bilinear interpolation; the same method is typically used for SIUV and confidences Confidence predictor mixin provides confidence estimates, as described in: N. Neverova et al., Correlated Uncertainty for Learning Dense Correspondences from Noisy Labels, NeurIPS 2019 A. Sanakoyeu et al., Transferring Dense Pose to Proximal Animal Classes, CVPR 2020 """ def __init__(self, cfg: CfgNode, input_channels: int): """ Initialize confidence predictor using configuration options. Args: cfg (CfgNode): configuration options input_channels (int): number of input channels """ # we rely on base predictor to call nn.Module.__init__ super().__init__(cfg, input_channels) # pyre-ignore[19] self.confidence_model_cfg = DensePoseConfidenceModelConfig.from_cfg(cfg) self._initialize_confidence_estimation_layers(cfg, input_channels) self._registry = {} initialize_module_params(self) # pyre-ignore[6] def _initialize_confidence_estimation_layers(self, cfg: CfgNode, dim_in: int): """ Initialize confidence estimation layers based on configuration options Args: cfg (CfgNode): configuration options dim_in (int): number of input channels """ dim_out_patches = cfg.MODEL.ROI_DENSEPOSE_HEAD.NUM_PATCHES + 1 kernel_size = cfg.MODEL.ROI_DENSEPOSE_HEAD.DECONV_KERNEL if self.confidence_model_cfg.uv_confidence.enabled: if self.confidence_model_cfg.uv_confidence.type == DensePoseUVConfidenceType.IID_ISO: self.sigma_2_lowres = ConvTranspose2d( # pyre-ignore[16] dim_in, dim_out_patches, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) elif ( self.confidence_model_cfg.uv_confidence.type == DensePoseUVConfidenceType.INDEP_ANISO ): self.sigma_2_lowres = ConvTranspose2d( dim_in, dim_out_patches, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) self.kappa_u_lowres = ConvTranspose2d( # pyre-ignore[16] dim_in, dim_out_patches, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) self.kappa_v_lowres = ConvTranspose2d( # pyre-ignore[16] dim_in, dim_out_patches, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) else: raise ValueError( f"Unknown confidence model type: " f"{self.confidence_model_cfg.confidence_model_type}" ) if self.confidence_model_cfg.segm_confidence.enabled: self.fine_segm_confidence_lowres = ConvTranspose2d( # pyre-ignore[16] dim_in, 1, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) self.coarse_segm_confidence_lowres = ConvTranspose2d( # pyre-ignore[16] dim_in, 1, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) def forward(self, head_outputs: torch.Tensor): """ Perform forward operation on head outputs used as inputs for the predictor. Calls forward method from the base predictor and uses its outputs to compute confidences. Args: head_outputs (Tensor): head outputs used as predictor inputs Return: An instance of outputs with confidences, see `decorate_predictor_output_class_with_confidences` """ # assuming base class returns SIUV estimates in its first result base_predictor_outputs = super().forward(head_outputs) # pyre-ignore[16] # create output instance by extending base predictor outputs: output = self._create_output_instance(base_predictor_outputs) if self.confidence_model_cfg.uv_confidence.enabled: if self.confidence_model_cfg.uv_confidence.type == DensePoseUVConfidenceType.IID_ISO: # assuming base class defines interp2d method for bilinear interpolation output.sigma_2 = self.interp2d(self.sigma_2_lowres(head_outputs)) # pyre-ignore[16] elif ( self.confidence_model_cfg.uv_confidence.type == DensePoseUVConfidenceType.INDEP_ANISO ): # assuming base class defines interp2d method for bilinear interpolation output.sigma_2 = self.interp2d(self.sigma_2_lowres(head_outputs)) output.kappa_u = self.interp2d(self.kappa_u_lowres(head_outputs)) # pyre-ignore[16] output.kappa_v = self.interp2d(self.kappa_v_lowres(head_outputs)) # pyre-ignore[16] else: raise ValueError( f"Unknown confidence model type: " f"{self.confidence_model_cfg.confidence_model_type}" ) if self.confidence_model_cfg.segm_confidence.enabled: # base predictor outputs are assumed to have `fine_segm` and `coarse_segm` attributes # base predictor is assumed to define `interp2d` method for bilinear interpolation output.fine_segm_confidence = ( F.softplus( self.interp2d(self.fine_segm_confidence_lowres(head_outputs)) # pyre-ignore[16] ) + self.confidence_model_cfg.segm_confidence.epsilon ) output.fine_segm = base_predictor_outputs.fine_segm * torch.repeat_interleave( output.fine_segm_confidence, base_predictor_outputs.fine_segm.shape[1], dim=1 ) output.coarse_segm_confidence = ( F.softplus( self.interp2d( self.coarse_segm_confidence_lowres(head_outputs) # pyre-ignore[16] ) ) + self.confidence_model_cfg.segm_confidence.epsilon ) output.coarse_segm = base_predictor_outputs.coarse_segm * torch.repeat_interleave( output.coarse_segm_confidence, base_predictor_outputs.coarse_segm.shape[1], dim=1 ) return output def _create_output_instance(self, base_predictor_outputs: Any): """ Create an instance of predictor outputs by copying the outputs from the base predictor and initializing confidence Args: base_predictor_outputs: an instance of base predictor outputs (the outputs type is assumed to be a dataclass) Return: An instance of outputs with confidences """ PredictorOutput = decorate_predictor_output_class_with_confidences( type(base_predictor_outputs) # pyre-ignore[6] ) # base_predictor_outputs is assumed to be a dataclass # reassign all the fields from base_predictor_outputs (no deep copy!), add new fields output = PredictorOutput( **base_predictor_outputs.__dict__, coarse_segm_confidence=None, fine_segm_confidence=None, sigma_1=None, sigma_2=None, kappa_u=None, kappa_v=None, ) return output ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/predictors/chart_with_confidence.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from . import DensePoseChartConfidencePredictorMixin, DensePoseChartPredictor from .registry import DENSEPOSE_PREDICTOR_REGISTRY @DENSEPOSE_PREDICTOR_REGISTRY.register() class DensePoseChartWithConfidencePredictor( DensePoseChartConfidencePredictorMixin, DensePoseChartPredictor ): """ Predictor that combines chart and chart confidence estimation """ pass ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/predictors/cse.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import torch from torch import nn from detectron2.config import CfgNode from detectron2.layers import ConvTranspose2d, interpolate from ...structures import DensePoseEmbeddingPredictorOutput from ..utils import initialize_module_params from .registry import DENSEPOSE_PREDICTOR_REGISTRY @DENSEPOSE_PREDICTOR_REGISTRY.register() class DensePoseEmbeddingPredictor(nn.Module): """ Last layers of a DensePose model that take DensePose head outputs as an input and produce model outputs for continuous surface embeddings (CSE). """ def __init__(self, cfg: CfgNode, input_channels: int): """ Initialize predictor using configuration options Args: cfg (CfgNode): configuration options input_channels (int): input tensor size along the channel dimension """ super().__init__() dim_in = input_channels n_segm_chan = cfg.MODEL.ROI_DENSEPOSE_HEAD.NUM_COARSE_SEGM_CHANNELS embed_size = cfg.MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_SIZE kernel_size = cfg.MODEL.ROI_DENSEPOSE_HEAD.DECONV_KERNEL # coarse segmentation self.coarse_segm_lowres = ConvTranspose2d( dim_in, n_segm_chan, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) # embedding self.embed_lowres = ConvTranspose2d( dim_in, embed_size, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) self.scale_factor = cfg.MODEL.ROI_DENSEPOSE_HEAD.UP_SCALE initialize_module_params(self) def interp2d(self, tensor_nchw: torch.Tensor): """ Bilinear interpolation method to be used for upscaling Args: tensor_nchw (tensor): tensor of shape (N, C, H, W) Return: tensor of shape (N, C, Hout, Wout), where Hout and Wout are computed by applying the scale factor to H and W """ return interpolate( tensor_nchw, scale_factor=self.scale_factor, mode="bilinear", align_corners=False ) def forward(self, head_outputs): """ Perform forward step on DensePose head outputs Args: head_outputs (tensor): DensePose head outputs, tensor of shape [N, D, H, W] """ embed_lowres = self.embed_lowres(head_outputs) coarse_segm_lowres = self.coarse_segm_lowres(head_outputs) embed = self.interp2d(embed_lowres) coarse_segm = self.interp2d(coarse_segm_lowres) return DensePoseEmbeddingPredictorOutput(embedding=embed, coarse_segm=coarse_segm) ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/predictors/cse_confidence.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any import torch from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.layers import ConvTranspose2d from densepose.modeling.confidence import DensePoseConfidenceModelConfig from densepose.modeling.utils import initialize_module_params from densepose.structures import decorate_cse_predictor_output_class_with_confidences class DensePoseEmbeddingConfidencePredictorMixin: """ Predictor contains the last layers of a DensePose model that take DensePose head outputs as an input and produce model outputs. Confidence predictor mixin is used to generate confidences for coarse segmentation estimated by some base predictor. Several assumptions need to hold for the base predictor: 1) the `forward` method must return CSE DensePose head outputs, tensor of shape [N, D, H, W] 2) `interp2d` method must be defined to perform bilinear interpolation; the same method is typically used for masks and confidences Confidence predictor mixin provides confidence estimates, as described in: N. Neverova et al., Correlated Uncertainty for Learning Dense Correspondences from Noisy Labels, NeurIPS 2019 A. Sanakoyeu et al., Transferring Dense Pose to Proximal Animal Classes, CVPR 2020 """ def __init__(self, cfg: CfgNode, input_channels: int): """ Initialize confidence predictor using configuration options. Args: cfg (CfgNode): configuration options input_channels (int): number of input channels """ # we rely on base predictor to call nn.Module.__init__ super().__init__(cfg, input_channels) # pyre-ignore[19] self.confidence_model_cfg = DensePoseConfidenceModelConfig.from_cfg(cfg) self._initialize_confidence_estimation_layers(cfg, input_channels) self._registry = {} initialize_module_params(self) # pyre-ignore[6] def _initialize_confidence_estimation_layers(self, cfg: CfgNode, dim_in: int): """ Initialize confidence estimation layers based on configuration options Args: cfg (CfgNode): configuration options dim_in (int): number of input channels """ kernel_size = cfg.MODEL.ROI_DENSEPOSE_HEAD.DECONV_KERNEL if self.confidence_model_cfg.segm_confidence.enabled: self.coarse_segm_confidence_lowres = ConvTranspose2d( # pyre-ignore[16] dim_in, 1, kernel_size, stride=2, padding=int(kernel_size / 2 - 1) ) def forward(self, head_outputs: torch.Tensor): """ Perform forward operation on head outputs used as inputs for the predictor. Calls forward method from the base predictor and uses its outputs to compute confidences. Args: head_outputs (Tensor): head outputs used as predictor inputs Return: An instance of outputs with confidences, see `decorate_cse_predictor_output_class_with_confidences` """ # assuming base class returns SIUV estimates in its first result base_predictor_outputs = super().forward(head_outputs) # pyre-ignore[16] # create output instance by extending base predictor outputs: output = self._create_output_instance(base_predictor_outputs) if self.confidence_model_cfg.segm_confidence.enabled: # base predictor outputs are assumed to have `coarse_segm` attribute # base predictor is assumed to define `interp2d` method for bilinear interpolation output.coarse_segm_confidence = ( F.softplus( self.interp2d( # pyre-ignore[16] self.coarse_segm_confidence_lowres(head_outputs) # pyre-ignore[16] ) ) + self.confidence_model_cfg.segm_confidence.epsilon ) output.coarse_segm = base_predictor_outputs.coarse_segm * torch.repeat_interleave( output.coarse_segm_confidence, base_predictor_outputs.coarse_segm.shape[1], dim=1 ) return output def _create_output_instance(self, base_predictor_outputs: Any): """ Create an instance of predictor outputs by copying the outputs from the base predictor and initializing confidence Args: base_predictor_outputs: an instance of base predictor outputs (the outputs type is assumed to be a dataclass) Return: An instance of outputs with confidences """ PredictorOutput = decorate_cse_predictor_output_class_with_confidences( type(base_predictor_outputs) # pyre-ignore[6] ) # base_predictor_outputs is assumed to be a dataclass # reassign all the fields from base_predictor_outputs (no deep copy!), add new fields output = PredictorOutput( **base_predictor_outputs.__dict__, coarse_segm_confidence=None, ) return output ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/predictors/cse_with_confidence.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from . import DensePoseEmbeddingConfidencePredictorMixin, DensePoseEmbeddingPredictor from .registry import DENSEPOSE_PREDICTOR_REGISTRY @DENSEPOSE_PREDICTOR_REGISTRY.register() class DensePoseEmbeddingWithConfidencePredictor( DensePoseEmbeddingConfidencePredictorMixin, DensePoseEmbeddingPredictor ): """ Predictor that combines CSE and CSE confidence estimation """ pass ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/predictors/registry.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.utils.registry import Registry DENSEPOSE_PREDICTOR_REGISTRY = Registry("DENSEPOSE_PREDICTOR") ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/roi_heads/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .v1convx import DensePoseV1ConvXHead from .deeplab import DensePoseDeepLabHead from .registry import ROI_DENSEPOSE_HEAD_REGISTRY from .roi_head import Decoder, DensePoseROIHeads ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/roi_heads/deeplab.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import fvcore.nn.weight_init as weight_init import torch from torch import nn from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.layers import Conv2d from .registry import ROI_DENSEPOSE_HEAD_REGISTRY @ROI_DENSEPOSE_HEAD_REGISTRY.register() class DensePoseDeepLabHead(nn.Module): """ DensePose head using DeepLabV3 model from "Rethinking Atrous Convolution for Semantic Image Segmentation" . """ def __init__(self, cfg: CfgNode, input_channels: int): super(DensePoseDeepLabHead, self).__init__() # fmt: off hidden_dim = cfg.MODEL.ROI_DENSEPOSE_HEAD.CONV_HEAD_DIM kernel_size = cfg.MODEL.ROI_DENSEPOSE_HEAD.CONV_HEAD_KERNEL norm = cfg.MODEL.ROI_DENSEPOSE_HEAD.DEEPLAB.NORM self.n_stacked_convs = cfg.MODEL.ROI_DENSEPOSE_HEAD.NUM_STACKED_CONVS self.use_nonlocal = cfg.MODEL.ROI_DENSEPOSE_HEAD.DEEPLAB.NONLOCAL_ON # fmt: on pad_size = kernel_size // 2 n_channels = input_channels self.ASPP = ASPP(input_channels, [6, 12, 56], n_channels) # 6, 12, 56 self.add_module("ASPP", self.ASPP) if self.use_nonlocal: self.NLBlock = NONLocalBlock2D(input_channels, bn_layer=True) self.add_module("NLBlock", self.NLBlock) # weight_init.c2_msra_fill(self.ASPP) for i in range(self.n_stacked_convs): norm_module = nn.GroupNorm(32, hidden_dim) if norm == "GN" else None layer = Conv2d( n_channels, hidden_dim, kernel_size, stride=1, padding=pad_size, bias=not norm, norm=norm_module, ) weight_init.c2_msra_fill(layer) n_channels = hidden_dim layer_name = self._get_layer_name(i) self.add_module(layer_name, layer) self.n_out_channels = hidden_dim # initialize_module_params(self) def forward(self, features): x0 = features x = self.ASPP(x0) if self.use_nonlocal: x = self.NLBlock(x) output = x for i in range(self.n_stacked_convs): layer_name = self._get_layer_name(i) x = getattr(self, layer_name)(x) x = F.relu(x) output = x return output def _get_layer_name(self, i: int): layer_name = "body_conv_fcn{}".format(i + 1) return layer_name # Copied from # https://github.com/pytorch/vision/blob/master/torchvision/models/segmentation/deeplabv3.py # See https://arxiv.org/pdf/1706.05587.pdf for details class ASPPConv(nn.Sequential): def __init__(self, in_channels, out_channels, dilation): modules = [ nn.Conv2d( in_channels, out_channels, 3, padding=dilation, dilation=dilation, bias=False ), nn.GroupNorm(32, out_channels), nn.ReLU(), ] super(ASPPConv, self).__init__(*modules) class ASPPPooling(nn.Sequential): def __init__(self, in_channels, out_channels): super(ASPPPooling, self).__init__( nn.AdaptiveAvgPool2d(1), nn.Conv2d(in_channels, out_channels, 1, bias=False), nn.GroupNorm(32, out_channels), nn.ReLU(), ) def forward(self, x): size = x.shape[-2:] x = super(ASPPPooling, self).forward(x) return F.interpolate(x, size=size, mode="bilinear", align_corners=False) class ASPP(nn.Module): def __init__(self, in_channels, atrous_rates, out_channels): super(ASPP, self).__init__() modules = [] modules.append( nn.Sequential( nn.Conv2d(in_channels, out_channels, 1, bias=False), nn.GroupNorm(32, out_channels), nn.ReLU(), ) ) rate1, rate2, rate3 = tuple(atrous_rates) modules.append(ASPPConv(in_channels, out_channels, rate1)) modules.append(ASPPConv(in_channels, out_channels, rate2)) modules.append(ASPPConv(in_channels, out_channels, rate3)) modules.append(ASPPPooling(in_channels, out_channels)) self.convs = nn.ModuleList(modules) self.project = nn.Sequential( nn.Conv2d(5 * out_channels, out_channels, 1, bias=False), # nn.BatchNorm2d(out_channels), nn.ReLU() # nn.Dropout(0.5) ) def forward(self, x): res = [] for conv in self.convs: res.append(conv(x)) res = torch.cat(res, dim=1) return self.project(res) # copied from # https://github.com/AlexHex7/Non-local_pytorch/blob/master/lib/non_local_embedded_gaussian.py # See https://arxiv.org/abs/1711.07971 for details class _NonLocalBlockND(nn.Module): def __init__( self, in_channels, inter_channels=None, dimension=3, sub_sample=True, bn_layer=True ): super(_NonLocalBlockND, self).__init__() assert dimension in [1, 2, 3] self.dimension = dimension self.sub_sample = sub_sample self.in_channels = in_channels self.inter_channels = inter_channels if self.inter_channels is None: self.inter_channels = in_channels // 2 if self.inter_channels == 0: self.inter_channels = 1 if dimension == 3: conv_nd = nn.Conv3d max_pool_layer = nn.MaxPool3d(kernel_size=(1, 2, 2)) bn = nn.GroupNorm # (32, hidden_dim) #nn.BatchNorm3d elif dimension == 2: conv_nd = nn.Conv2d max_pool_layer = nn.MaxPool2d(kernel_size=(2, 2)) bn = nn.GroupNorm # (32, hidden_dim)nn.BatchNorm2d else: conv_nd = nn.Conv1d max_pool_layer = nn.MaxPool1d(kernel_size=2) bn = nn.GroupNorm # (32, hidden_dim)nn.BatchNorm1d self.g = conv_nd( in_channels=self.in_channels, out_channels=self.inter_channels, kernel_size=1, stride=1, padding=0, ) if bn_layer: self.W = nn.Sequential( conv_nd( in_channels=self.inter_channels, out_channels=self.in_channels, kernel_size=1, stride=1, padding=0, ), bn(32, self.in_channels), ) nn.init.constant_(self.W[1].weight, 0) nn.init.constant_(self.W[1].bias, 0) else: self.W = conv_nd( in_channels=self.inter_channels, out_channels=self.in_channels, kernel_size=1, stride=1, padding=0, ) nn.init.constant_(self.W.weight, 0) nn.init.constant_(self.W.bias, 0) self.theta = conv_nd( in_channels=self.in_channels, out_channels=self.inter_channels, kernel_size=1, stride=1, padding=0, ) self.phi = conv_nd( in_channels=self.in_channels, out_channels=self.inter_channels, kernel_size=1, stride=1, padding=0, ) if sub_sample: self.g = nn.Sequential(self.g, max_pool_layer) self.phi = nn.Sequential(self.phi, max_pool_layer) def forward(self, x): """ :param x: (b, c, t, h, w) :return: """ batch_size = x.size(0) g_x = self.g(x).view(batch_size, self.inter_channels, -1) g_x = g_x.permute(0, 2, 1) theta_x = self.theta(x).view(batch_size, self.inter_channels, -1) theta_x = theta_x.permute(0, 2, 1) phi_x = self.phi(x).view(batch_size, self.inter_channels, -1) f = torch.matmul(theta_x, phi_x) f_div_C = F.softmax(f, dim=-1) y = torch.matmul(f_div_C, g_x) y = y.permute(0, 2, 1).contiguous() y = y.view(batch_size, self.inter_channels, *x.size()[2:]) W_y = self.W(y) z = W_y + x return z class NONLocalBlock2D(_NonLocalBlockND): def __init__(self, in_channels, inter_channels=None, sub_sample=True, bn_layer=True): super(NONLocalBlock2D, self).__init__( in_channels, inter_channels=inter_channels, dimension=2, sub_sample=sub_sample, bn_layer=bn_layer, ) ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/roi_heads/registry.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.utils.registry import Registry ROI_DENSEPOSE_HEAD_REGISTRY = Registry("ROI_DENSEPOSE_HEAD") ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/roi_heads/roi_head.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from typing import Dict, List, Optional import fvcore.nn.weight_init as weight_init import torch import torch.nn as nn from torch.nn import functional as F from detectron2.layers import Conv2d, ShapeSpec, get_norm from detectron2.modeling import ROI_HEADS_REGISTRY, StandardROIHeads from detectron2.modeling.poolers import ROIPooler from detectron2.modeling.roi_heads import select_foreground_proposals from detectron2.structures import ImageList, Instances from .. import ( build_densepose_data_filter, build_densepose_embedder, build_densepose_head, build_densepose_losses, build_densepose_predictor, densepose_inference, ) class Decoder(nn.Module): """ A semantic segmentation head described in detail in the Panoptic Feature Pyramid Networks paper (https://arxiv.org/abs/1901.02446). It takes FPN features as input and merges information from all levels of the FPN into single output. """ def __init__(self, cfg, input_shape: Dict[str, ShapeSpec], in_features): super(Decoder, self).__init__() # fmt: off self.in_features = in_features feature_strides = {k: v.stride for k, v in input_shape.items()} feature_channels = {k: v.channels for k, v in input_shape.items()} num_classes = cfg.MODEL.ROI_DENSEPOSE_HEAD.DECODER_NUM_CLASSES conv_dims = cfg.MODEL.ROI_DENSEPOSE_HEAD.DECODER_CONV_DIMS self.common_stride = cfg.MODEL.ROI_DENSEPOSE_HEAD.DECODER_COMMON_STRIDE norm = cfg.MODEL.ROI_DENSEPOSE_HEAD.DECODER_NORM # fmt: on self.scale_heads = [] for in_feature in self.in_features: head_ops = [] head_length = max( 1, int(np.log2(feature_strides[in_feature]) - np.log2(self.common_stride)) ) for k in range(head_length): conv = Conv2d( feature_channels[in_feature] if k == 0 else conv_dims, conv_dims, kernel_size=3, stride=1, padding=1, bias=not norm, norm=get_norm(norm, conv_dims), activation=F.relu, ) weight_init.c2_msra_fill(conv) head_ops.append(conv) if feature_strides[in_feature] != self.common_stride: head_ops.append( nn.Upsample(scale_factor=2, mode="bilinear", align_corners=False) ) self.scale_heads.append(nn.Sequential(*head_ops)) self.add_module(in_feature, self.scale_heads[-1]) self.predictor = Conv2d(conv_dims, num_classes, kernel_size=1, stride=1, padding=0) weight_init.c2_msra_fill(self.predictor) def forward(self, features: List[torch.Tensor]): for i, _ in enumerate(self.in_features): if i == 0: x = self.scale_heads[i](features[i]) else: x = x + self.scale_heads[i](features[i]) x = self.predictor(x) return x @ROI_HEADS_REGISTRY.register() class DensePoseROIHeads(StandardROIHeads): """ A Standard ROIHeads which contains an addition of DensePose head. """ def __init__(self, cfg, input_shape): super().__init__(cfg, input_shape) self._init_densepose_head(cfg, input_shape) def _init_densepose_head(self, cfg, input_shape): # fmt: off self.densepose_on = cfg.MODEL.DENSEPOSE_ON if not self.densepose_on: return self.densepose_data_filter = build_densepose_data_filter(cfg) dp_pooler_resolution = cfg.MODEL.ROI_DENSEPOSE_HEAD.POOLER_RESOLUTION dp_pooler_sampling_ratio = cfg.MODEL.ROI_DENSEPOSE_HEAD.POOLER_SAMPLING_RATIO dp_pooler_type = cfg.MODEL.ROI_DENSEPOSE_HEAD.POOLER_TYPE self.use_decoder = cfg.MODEL.ROI_DENSEPOSE_HEAD.DECODER_ON # fmt: on if self.use_decoder: dp_pooler_scales = (1.0 / input_shape[self.in_features[0]].stride,) else: dp_pooler_scales = tuple(1.0 / input_shape[k].stride for k in self.in_features) in_channels = [input_shape[f].channels for f in self.in_features][0] if self.use_decoder: self.decoder = Decoder(cfg, input_shape, self.in_features) self.densepose_pooler = ROIPooler( output_size=dp_pooler_resolution, scales=dp_pooler_scales, sampling_ratio=dp_pooler_sampling_ratio, pooler_type=dp_pooler_type, ) self.densepose_head = build_densepose_head(cfg, in_channels) self.densepose_predictor = build_densepose_predictor( cfg, self.densepose_head.n_out_channels ) self.densepose_losses = build_densepose_losses(cfg) self.embedder = build_densepose_embedder(cfg) def _forward_densepose(self, features: Dict[str, torch.Tensor], instances: List[Instances]): """ Forward logic of the densepose prediction branch. Args: features (dict[str, Tensor]): input data as a mapping from feature map name to tensor. Axis 0 represents the number of images `N` in the input data; axes 1-3 are channels, height, and width, which may vary between feature maps (e.g., if a feature pyramid is used). instances (list[Instances]): length `N` list of `Instances`. The i-th `Instances` contains instances for the i-th input image, In training, they can be the proposals. In inference, they can be the predicted boxes. Returns: In training, a dict of losses. In inference, update `instances` with new fields "densepose" and return it. """ if not self.densepose_on: return {} if self.training else instances features_list = [features[f] for f in self.in_features] if self.training: proposals, _ = select_foreground_proposals(instances, self.num_classes) features_list, proposals = self.densepose_data_filter(features_list, proposals) if len(proposals) > 0: proposal_boxes = [x.proposal_boxes for x in proposals] if self.use_decoder: # pyre-fixme[29]: `Union[nn.Module, torch.Tensor]` is not a # function. features_list = [self.decoder(features_list)] features_dp = self.densepose_pooler(features_list, proposal_boxes) densepose_head_outputs = self.densepose_head(features_dp) densepose_predictor_outputs = self.densepose_predictor(densepose_head_outputs) densepose_loss_dict = self.densepose_losses( proposals, densepose_predictor_outputs, embedder=self.embedder ) return densepose_loss_dict else: pred_boxes = [x.pred_boxes for x in instances] if self.use_decoder: # pyre-fixme[29]: `Union[nn.Module, torch.Tensor]` is not a function. features_list = [self.decoder(features_list)] features_dp = self.densepose_pooler(features_list, pred_boxes) if len(features_dp) > 0: densepose_head_outputs = self.densepose_head(features_dp) densepose_predictor_outputs = self.densepose_predictor(densepose_head_outputs) else: densepose_predictor_outputs = None densepose_inference(densepose_predictor_outputs, instances) return instances def forward( self, images: ImageList, features: Dict[str, torch.Tensor], proposals: List[Instances], targets: Optional[List[Instances]] = None, ): instances, losses = super().forward(images, features, proposals, targets) del targets, images if self.training: losses.update(self._forward_densepose(features, instances)) return instances, losses def forward_with_given_boxes( self, features: Dict[str, torch.Tensor], instances: List[Instances] ): """ Use the given boxes in `instances` to produce other (non-box) per-ROI outputs. This is useful for downstream tasks where a box is known, but need to obtain other attributes (outputs of other heads). Test-time augmentation also uses this. Args: features: same as in `forward()` instances (list[Instances]): instances to predict other outputs. Expect the keys "pred_boxes" and "pred_classes" to exist. Returns: instances (list[Instances]): the same `Instances` objects, with extra fields such as `pred_masks` or `pred_keypoints`. """ instances = super().forward_with_given_boxes(features, instances) instances = self._forward_densepose(features, instances) return instances ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/roi_heads/v1convx.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch from torch import nn from torch.nn import functional as F from detectron2.config import CfgNode from detectron2.layers import Conv2d from ..utils import initialize_module_params from .registry import ROI_DENSEPOSE_HEAD_REGISTRY @ROI_DENSEPOSE_HEAD_REGISTRY.register() class DensePoseV1ConvXHead(nn.Module): """ Fully convolutional DensePose head. """ def __init__(self, cfg: CfgNode, input_channels: int): """ Initialize DensePose fully convolutional head Args: cfg (CfgNode): configuration options input_channels (int): number of input channels """ super(DensePoseV1ConvXHead, self).__init__() # fmt: off hidden_dim = cfg.MODEL.ROI_DENSEPOSE_HEAD.CONV_HEAD_DIM kernel_size = cfg.MODEL.ROI_DENSEPOSE_HEAD.CONV_HEAD_KERNEL self.n_stacked_convs = cfg.MODEL.ROI_DENSEPOSE_HEAD.NUM_STACKED_CONVS # fmt: on pad_size = kernel_size // 2 n_channels = input_channels for i in range(self.n_stacked_convs): layer = Conv2d(n_channels, hidden_dim, kernel_size, stride=1, padding=pad_size) layer_name = self._get_layer_name(i) self.add_module(layer_name, layer) n_channels = hidden_dim self.n_out_channels = n_channels initialize_module_params(self) def forward(self, features: torch.Tensor): """ Apply DensePose fully convolutional head to the input features Args: features (tensor): input features Result: A tensor of DensePose head outputs """ x = features output = x for i in range(self.n_stacked_convs): layer_name = self._get_layer_name(i) x = getattr(self, layer_name)(x) x = F.relu(x) output = x return output def _get_layer_name(self, i: int): layer_name = "body_conv_fcn{}".format(i + 1) return layer_name ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/test_time_augmentation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import numpy as np import torch from fvcore.transforms import HFlipTransform, TransformList from torch.nn import functional as F from detectron2.data.transforms import RandomRotation, RotationTransform, apply_transform_gens from detectron2.modeling.postprocessing import detector_postprocess from detectron2.modeling.test_time_augmentation import DatasetMapperTTA, GeneralizedRCNNWithTTA from ..converters import HFlipConverter class DensePoseDatasetMapperTTA(DatasetMapperTTA): def __init__(self, cfg): super().__init__(cfg=cfg) self.angles = cfg.TEST.AUG.ROTATION_ANGLES def __call__(self, dataset_dict): ret = super().__call__(dataset_dict=dataset_dict) numpy_image = dataset_dict["image"].permute(1, 2, 0).numpy() for angle in self.angles: rotate = RandomRotation(angle=angle, expand=True) new_numpy_image, tfms = apply_transform_gens([rotate], np.copy(numpy_image)) torch_image = torch.from_numpy(np.ascontiguousarray(new_numpy_image.transpose(2, 0, 1))) dic = copy.deepcopy(dataset_dict) # In DatasetMapperTTA, there is a pre_tfm transform (resize or no-op) that is # added at the beginning of each TransformList. That's '.transforms[0]'. dic["transforms"] = TransformList( [ret[-1]["transforms"].transforms[0]] + tfms.transforms ) dic["image"] = torch_image ret.append(dic) return ret class DensePoseGeneralizedRCNNWithTTA(GeneralizedRCNNWithTTA): def __init__(self, cfg, model, transform_data, tta_mapper=None, batch_size=1): """ Args: cfg (CfgNode): model (GeneralizedRCNN): a GeneralizedRCNN to apply TTA on. transform_data (DensePoseTransformData): contains symmetry label transforms used for horizontal flip tta_mapper (callable): takes a dataset dict and returns a list of augmented versions of the dataset dict. Defaults to `DatasetMapperTTA(cfg)`. batch_size (int): batch the augmented images into this batch size for inference. """ self._transform_data = transform_data.to(model.device) super().__init__(cfg=cfg, model=model, tta_mapper=tta_mapper, batch_size=batch_size) # the implementation follows closely the one from detectron2/modeling def _inference_one_image(self, input): """ Args: input (dict): one dataset dict with "image" field being a CHW tensor Returns: dict: one output dict """ orig_shape = (input["height"], input["width"]) # For some reason, resize with uint8 slightly increases box AP but decreases densepose AP input["image"] = input["image"].to(torch.uint8) augmented_inputs, tfms = self._get_augmented_inputs(input) # Detect boxes from all augmented versions with self._turn_off_roi_heads(["mask_on", "keypoint_on", "densepose_on"]): # temporarily disable roi heads all_boxes, all_scores, all_classes = self._get_augmented_boxes(augmented_inputs, tfms) merged_instances = self._merge_detections(all_boxes, all_scores, all_classes, orig_shape) if self.cfg.MODEL.MASK_ON or self.cfg.MODEL.DENSEPOSE_ON: # Use the detected boxes to obtain new fields augmented_instances = self._rescale_detected_boxes( augmented_inputs, merged_instances, tfms ) # run forward on the detected boxes outputs = self._batch_inference(augmented_inputs, augmented_instances) # Delete now useless variables to avoid being out of memory del augmented_inputs, augmented_instances # average the predictions if self.cfg.MODEL.MASK_ON: merged_instances.pred_masks = self._reduce_pred_masks(outputs, tfms) if self.cfg.MODEL.DENSEPOSE_ON: merged_instances.pred_densepose = self._reduce_pred_densepose(outputs, tfms) # postprocess merged_instances = detector_postprocess(merged_instances, *orig_shape) return {"instances": merged_instances} else: return {"instances": merged_instances} def _get_augmented_boxes(self, augmented_inputs, tfms): # Heavily based on detectron2/modeling/test_time_augmentation.py # Only difference is that RotationTransform is excluded from bbox computation # 1: forward with all augmented images outputs = self._batch_inference(augmented_inputs) # 2: union the results all_boxes = [] all_scores = [] all_classes = [] for output, tfm in zip(outputs, tfms): # Need to inverse the transforms on boxes, to obtain results on original image if not any(isinstance(t, RotationTransform) for t in tfm.transforms): # Some transforms can't compute bbox correctly pred_boxes = output.pred_boxes.tensor original_pred_boxes = tfm.inverse().apply_box(pred_boxes.cpu().numpy()) all_boxes.append(torch.from_numpy(original_pred_boxes).to(pred_boxes.device)) all_scores.extend(output.scores) all_classes.extend(output.pred_classes) all_boxes = torch.cat(all_boxes, dim=0) return all_boxes, all_scores, all_classes def _reduce_pred_densepose(self, outputs, tfms): # Should apply inverse transforms on densepose preds. # We assume only rotation, resize & flip are used. pred_masks is a scale-invariant # representation, so we handle the other ones specially for idx, (output, tfm) in enumerate(zip(outputs, tfms)): for t in tfm.transforms: for attr in ["coarse_segm", "fine_segm", "u", "v"]: setattr( output.pred_densepose, attr, _inverse_rotation( getattr(output.pred_densepose, attr), output.pred_boxes.tensor, t ), ) if any(isinstance(t, HFlipTransform) for t in tfm.transforms): output.pred_densepose = HFlipConverter.convert( output.pred_densepose, self._transform_data ) self._incremental_avg_dp(outputs[0].pred_densepose, output.pred_densepose, idx) return outputs[0].pred_densepose # incrementally computed average: u_(n + 1) = u_n + (x_(n+1) - u_n) / (n + 1). def _incremental_avg_dp(self, avg, new_el, idx): for attr in ["coarse_segm", "fine_segm", "u", "v"]: setattr(avg, attr, (getattr(avg, attr) * idx + getattr(new_el, attr)) / (idx + 1)) if idx: # Deletion of the > 0 index intermediary values to prevent GPU OOM setattr(new_el, attr, None) return avg def _inverse_rotation(densepose_attrs, boxes, transform): # resample outputs to image size and rotate back the densepose preds # on the rotated images to the space of the original image if len(boxes) == 0 or not isinstance(transform, RotationTransform): return densepose_attrs boxes = boxes.int().cpu().numpy() wh_boxes = boxes[:, 2:] - boxes[:, :2] # bboxes in the rotated space inv_boxes = rotate_box_inverse(transform, boxes).astype(int) # bboxes in original image wh_diff = (inv_boxes[:, 2:] - inv_boxes[:, :2] - wh_boxes) // 2 # diff between new/old bboxes rotation_matrix = torch.tensor([transform.rm_image]).to(device=densepose_attrs.device).float() rotation_matrix[:, :, -1] = 0 # To apply grid_sample for rotation, we need to have enough space to fit the original and # rotated bboxes. l_bds and r_bds are the left/right bounds that will be used to # crop the difference once the rotation is done l_bds = np.maximum(0, -wh_diff) for i in range(len(densepose_attrs)): if min(wh_boxes[i]) <= 0: continue densepose_attr = densepose_attrs[[i]].clone() # 1. Interpolate densepose attribute to size of the rotated bbox densepose_attr = F.interpolate(densepose_attr, wh_boxes[i].tolist()[::-1], mode="bilinear") # 2. Pad the interpolated attribute so it has room for the original + rotated bbox densepose_attr = F.pad(densepose_attr, tuple(np.repeat(np.maximum(0, wh_diff[i]), 2))) # 3. Compute rotation grid and transform grid = F.affine_grid(rotation_matrix, size=densepose_attr.shape) densepose_attr = F.grid_sample(densepose_attr, grid) # 4. Compute right bounds and crop the densepose_attr to the size of the original bbox r_bds = densepose_attr.shape[2:][::-1] - l_bds[i] densepose_attr = densepose_attr[:, :, l_bds[i][1] : r_bds[1], l_bds[i][0] : r_bds[0]] if min(densepose_attr.shape) > 0: # Interpolate back to the original size of the densepose attribute densepose_attr = F.interpolate( densepose_attr, densepose_attrs.shape[-2:], mode="bilinear" ) # Adding a very small probability to the background class to fill padded zones densepose_attr[:, 0] += 1e-10 densepose_attrs[i] = densepose_attr return densepose_attrs def rotate_box_inverse(rot_tfm, rotated_box): """ rotated_box is a N * 4 array of [x0, y0, x1, y1] boxes When a bbox is rotated, it gets bigger, because we need to surround the tilted bbox So when a bbox is rotated then inverse-rotated, it is much bigger than the original This function aims to invert the rotation on the box, but also resize it to its original size """ # 1. Compute the inverse rotation of the rotated bboxes (bigger than it ) invrot_box = rot_tfm.inverse().apply_box(rotated_box) h, w = rotated_box[:, 3] - rotated_box[:, 1], rotated_box[:, 2] - rotated_box[:, 0] ih, iw = invrot_box[:, 3] - invrot_box[:, 1], invrot_box[:, 2] - invrot_box[:, 0] assert 2 * rot_tfm.abs_sin**2 != 1, "45 degrees angle can't be inverted" # 2. Inverse the corresponding computation in the rotation transform # to get the original height/width of the rotated boxes orig_h = (h * rot_tfm.abs_cos - w * rot_tfm.abs_sin) / (1 - 2 * rot_tfm.abs_sin**2) orig_w = (w * rot_tfm.abs_cos - h * rot_tfm.abs_sin) / (1 - 2 * rot_tfm.abs_sin**2) # 3. Resize the inverse-rotated bboxes to their original size invrot_box[:, 0] += (iw - orig_w) / 2 invrot_box[:, 1] += (ih - orig_h) / 2 invrot_box[:, 2] -= (iw - orig_w) / 2 invrot_box[:, 3] -= (ih - orig_h) / 2 return invrot_box ================================================ FILE: detectron2/projects/DensePose/densepose/modeling/utils.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from torch import nn def initialize_module_params(module: nn.Module) -> None: for name, param in module.named_parameters(): if "bias" in name: nn.init.constant_(param, 0) elif "weight" in name: nn.init.kaiming_normal_(param, mode="fan_out", nonlinearity="relu") ================================================ FILE: detectron2/projects/DensePose/densepose/structures/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .chart import DensePoseChartPredictorOutput from .chart_confidence import decorate_predictor_output_class_with_confidences from .cse_confidence import decorate_cse_predictor_output_class_with_confidences from .chart_result import ( DensePoseChartResult, DensePoseChartResultWithConfidences, quantize_densepose_chart_result, compress_quantized_densepose_chart_result, decompress_compressed_densepose_chart_result, ) from .cse import DensePoseEmbeddingPredictorOutput from .data_relative import DensePoseDataRelative from .list import DensePoseList from .mesh import Mesh, create_mesh from .transform_data import DensePoseTransformData, normalized_coords_transform ================================================ FILE: detectron2/projects/DensePose/densepose/structures/chart.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from dataclasses import dataclass from typing import Union import torch @dataclass class DensePoseChartPredictorOutput: """ Predictor output that contains segmentation and inner coordinates predictions for predefined body parts: * coarse segmentation, a tensor of shape [N, K, Hout, Wout] * fine segmentation, a tensor of shape [N, C, Hout, Wout] * U coordinates, a tensor of shape [N, C, Hout, Wout] * V coordinates, a tensor of shape [N, C, Hout, Wout] where - N is the number of instances - K is the number of coarse segmentation channels ( 2 = foreground / background, 15 = one of 14 body parts / background) - C is the number of fine segmentation channels ( 24 fine body parts / background) - Hout and Wout are height and width of predictions """ coarse_segm: torch.Tensor fine_segm: torch.Tensor u: torch.Tensor v: torch.Tensor def __len__(self): """ Number of instances (N) in the output """ return self.coarse_segm.size(0) def __getitem__( self, item: Union[int, slice, torch.BoolTensor] ) -> "DensePoseChartPredictorOutput": """ Get outputs for the selected instance(s) Args: item (int or slice or tensor): selected items """ if isinstance(item, int): return DensePoseChartPredictorOutput( coarse_segm=self.coarse_segm[item].unsqueeze(0), fine_segm=self.fine_segm[item].unsqueeze(0), u=self.u[item].unsqueeze(0), v=self.v[item].unsqueeze(0), ) else: return DensePoseChartPredictorOutput( coarse_segm=self.coarse_segm[item], fine_segm=self.fine_segm[item], u=self.u[item], v=self.v[item], ) def to(self, device: torch.device): """ Transfers all tensors to the given device """ coarse_segm = self.coarse_segm.to(device) fine_segm = self.fine_segm.to(device) u = self.u.to(device) v = self.v.to(device) return DensePoseChartPredictorOutput(coarse_segm=coarse_segm, fine_segm=fine_segm, u=u, v=v) ================================================ FILE: detectron2/projects/DensePose/densepose/structures/chart_confidence.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from dataclasses import make_dataclass from functools import lru_cache from typing import Any, Optional import torch @lru_cache(maxsize=None) def decorate_predictor_output_class_with_confidences(BasePredictorOutput: type) -> type: """ Create a new output class from an existing one by adding new attributes related to confidence estimation: - sigma_1 (tensor) - sigma_2 (tensor) - kappa_u (tensor) - kappa_v (tensor) - fine_segm_confidence (tensor) - coarse_segm_confidence (tensor) Details on confidence estimation parameters can be found in: N. Neverova, D. Novotny, A. Vedaldi "Correlated Uncertainty for Learning Dense Correspondences from Noisy Labels", p. 918--926, in Proc. NIPS 2019 A. Sanakoyeu et al., Transferring Dense Pose to Proximal Animal Classes, CVPR 2020 The new class inherits the provided `BasePredictorOutput` class, it's name is composed of the name of the provided class and "WithConfidences" suffix. Args: BasePredictorOutput (type): output type to which confidence data is to be added, assumed to be a dataclass Return: New dataclass derived from the provided one that has attributes for confidence estimation """ PredictorOutput = make_dataclass( BasePredictorOutput.__name__ + "WithConfidences", fields=[ ("sigma_1", Optional[torch.Tensor], None), ("sigma_2", Optional[torch.Tensor], None), ("kappa_u", Optional[torch.Tensor], None), ("kappa_v", Optional[torch.Tensor], None), ("fine_segm_confidence", Optional[torch.Tensor], None), ("coarse_segm_confidence", Optional[torch.Tensor], None), ], bases=(BasePredictorOutput,), ) # add possibility to index PredictorOutput def slice_if_not_none(data, item): if data is None: return None if isinstance(item, int): return data[item].unsqueeze(0) return data[item] def PredictorOutput_getitem(self, item): PredictorOutput = type(self) base_predictor_output_sliced = super(PredictorOutput, self).__getitem__(item) return PredictorOutput( **base_predictor_output_sliced.__dict__, coarse_segm_confidence=slice_if_not_none(self.coarse_segm_confidence, item), fine_segm_confidence=slice_if_not_none(self.fine_segm_confidence, item), sigma_1=slice_if_not_none(self.sigma_1, item), sigma_2=slice_if_not_none(self.sigma_2, item), kappa_u=slice_if_not_none(self.kappa_u, item), kappa_v=slice_if_not_none(self.kappa_v, item), ) PredictorOutput.__getitem__ = PredictorOutput_getitem def PredictorOutput_to(self, device: torch.device): """ Transfers all tensors to the given device """ PredictorOutput = type(self) base_predictor_output_to = super(PredictorOutput, self).to(device) # pyre-ignore[16] def to_device_if_tensor(var: Any): if isinstance(var, torch.Tensor): return var.to(device) return var return PredictorOutput( **base_predictor_output_to.__dict__, sigma_1=to_device_if_tensor(self.sigma_1), sigma_2=to_device_if_tensor(self.sigma_2), kappa_u=to_device_if_tensor(self.kappa_u), kappa_v=to_device_if_tensor(self.kappa_v), fine_segm_confidence=to_device_if_tensor(self.fine_segm_confidence), coarse_segm_confidence=to_device_if_tensor(self.coarse_segm_confidence), ) PredictorOutput.to = PredictorOutput_to return PredictorOutput ================================================ FILE: detectron2/projects/DensePose/densepose/structures/chart_result.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from dataclasses import dataclass from typing import Any, Optional, Tuple import torch @dataclass class DensePoseChartResult: """ DensePose results for chart-based methods represented by labels and inner coordinates (U, V) of individual charts. Each chart is a 2D manifold that has an associated label and is parameterized by two coordinates U and V. Both U and V take values in [0, 1]. Thus the results are represented by two tensors: - labels (tensor [H, W] of long): contains estimated label for each pixel of the detection bounding box of size (H, W) - uv (tensor [2, H, W] of float): contains estimated U and V coordinates for each pixel of the detection bounding box of size (H, W) """ labels: torch.Tensor uv: torch.Tensor def to(self, device: torch.device): """ Transfers all tensors to the given device """ labels = self.labels.to(device) uv = self.uv.to(device) return DensePoseChartResult(labels=labels, uv=uv) @dataclass class DensePoseChartResultWithConfidences: """ We add confidence values to DensePoseChartResult Thus the results are represented by two tensors: - labels (tensor [H, W] of long): contains estimated label for each pixel of the detection bounding box of size (H, W) - uv (tensor [2, H, W] of float): contains estimated U and V coordinates for each pixel of the detection bounding box of size (H, W) Plus one [H, W] tensor of float for each confidence type """ labels: torch.Tensor uv: torch.Tensor sigma_1: Optional[torch.Tensor] = None sigma_2: Optional[torch.Tensor] = None kappa_u: Optional[torch.Tensor] = None kappa_v: Optional[torch.Tensor] = None fine_segm_confidence: Optional[torch.Tensor] = None coarse_segm_confidence: Optional[torch.Tensor] = None def to(self, device: torch.device): """ Transfers all tensors to the given device, except if their value is None """ def to_device_if_tensor(var: Any): if isinstance(var, torch.Tensor): return var.to(device) return var return DensePoseChartResultWithConfidences( labels=self.labels.to(device), uv=self.uv.to(device), sigma_1=to_device_if_tensor(self.sigma_1), sigma_2=to_device_if_tensor(self.sigma_2), kappa_u=to_device_if_tensor(self.kappa_u), kappa_v=to_device_if_tensor(self.kappa_v), fine_segm_confidence=to_device_if_tensor(self.fine_segm_confidence), coarse_segm_confidence=to_device_if_tensor(self.coarse_segm_confidence), ) @dataclass class DensePoseChartResultQuantized: """ DensePose results for chart-based methods represented by labels and quantized inner coordinates (U, V) of individual charts. Each chart is a 2D manifold that has an associated label and is parameterized by two coordinates U and V. Both U and V take values in [0, 1]. Quantized coordinates Uq and Vq have uint8 values which are obtained as: Uq = U * 255 (hence 0 <= Uq <= 255) Vq = V * 255 (hence 0 <= Vq <= 255) Thus the results are represented by one tensor: - labels_uv_uint8 (tensor [3, H, W] of uint8): contains estimated label and quantized coordinates Uq and Vq for each pixel of the detection bounding box of size (H, W) """ labels_uv_uint8: torch.Tensor def to(self, device: torch.device): """ Transfers all tensors to the given device """ labels_uv_uint8 = self.labels_uv_uint8.to(device) return DensePoseChartResultQuantized(labels_uv_uint8=labels_uv_uint8) @dataclass class DensePoseChartResultCompressed: """ DensePose results for chart-based methods represented by a PNG-encoded string. The tensor of quantized DensePose results of size [3, H, W] is considered as an image with 3 color channels. PNG compression is applied and the result is stored as a Base64-encoded string. The following attributes are defined: - shape_chw (tuple of 3 int): contains shape of the result tensor (number of channels, height, width) - labels_uv_str (str): contains Base64-encoded results tensor of size [3, H, W] compressed with PNG compression methods """ shape_chw: Tuple[int, int, int] labels_uv_str: str def quantize_densepose_chart_result(result: DensePoseChartResult) -> DensePoseChartResultQuantized: """ Applies quantization to DensePose chart-based result. Args: result (DensePoseChartResult): DensePose chart-based result Return: Quantized DensePose chart-based result (DensePoseChartResultQuantized) """ h, w = result.labels.shape labels_uv_uint8 = torch.zeros([3, h, w], dtype=torch.uint8, device=result.labels.device) labels_uv_uint8[0] = result.labels labels_uv_uint8[1:] = (result.uv * 255).clamp(0, 255).byte() return DensePoseChartResultQuantized(labels_uv_uint8=labels_uv_uint8) def compress_quantized_densepose_chart_result( result: DensePoseChartResultQuantized, ) -> DensePoseChartResultCompressed: """ Compresses quantized DensePose chart-based result Args: result (DensePoseChartResultQuantized): quantized DensePose chart-based result Return: Compressed DensePose chart-based result (DensePoseChartResultCompressed) """ import base64 import numpy as np from io import BytesIO from PIL import Image labels_uv_uint8_np_chw = result.labels_uv_uint8.cpu().numpy() labels_uv_uint8_np_hwc = np.moveaxis(labels_uv_uint8_np_chw, 0, -1) im = Image.fromarray(labels_uv_uint8_np_hwc) fstream = BytesIO() im.save(fstream, format="png", optimize=True) labels_uv_str = base64.encodebytes(fstream.getvalue()).decode() shape_chw = labels_uv_uint8_np_chw.shape return DensePoseChartResultCompressed(labels_uv_str=labels_uv_str, shape_chw=shape_chw) def decompress_compressed_densepose_chart_result( result: DensePoseChartResultCompressed, ) -> DensePoseChartResultQuantized: """ Decompresses DensePose chart-based result encoded into a base64 string Args: result (DensePoseChartResultCompressed): compressed DensePose chart result Return: Quantized DensePose chart-based result (DensePoseChartResultQuantized) """ import base64 import numpy as np from io import BytesIO from PIL import Image fstream = BytesIO(base64.decodebytes(result.labels_uv_str.encode())) im = Image.open(fstream) labels_uv_uint8_np_chw = np.moveaxis(np.array(im, dtype=np.uint8), -1, 0) return DensePoseChartResultQuantized( labels_uv_uint8=torch.from_numpy(labels_uv_uint8_np_chw.reshape(result.shape_chw)) ) ================================================ FILE: detectron2/projects/DensePose/densepose/structures/cse.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from dataclasses import dataclass from typing import Union import torch @dataclass class DensePoseEmbeddingPredictorOutput: """ Predictor output that contains embedding and coarse segmentation data: * embedding: float tensor of size [N, D, H, W], contains estimated embeddings * coarse_segm: float tensor of size [N, K, H, W] Here D = MODEL.ROI_DENSEPOSE_HEAD.CSE.EMBED_SIZE K = MODEL.ROI_DENSEPOSE_HEAD.NUM_COARSE_SEGM_CHANNELS """ embedding: torch.Tensor coarse_segm: torch.Tensor def __len__(self): """ Number of instances (N) in the output """ return self.coarse_segm.size(0) def __getitem__( self, item: Union[int, slice, torch.BoolTensor] ) -> "DensePoseEmbeddingPredictorOutput": """ Get outputs for the selected instance(s) Args: item (int or slice or tensor): selected items """ if isinstance(item, int): return DensePoseEmbeddingPredictorOutput( coarse_segm=self.coarse_segm[item].unsqueeze(0), embedding=self.embedding[item].unsqueeze(0), ) else: return DensePoseEmbeddingPredictorOutput( coarse_segm=self.coarse_segm[item], embedding=self.embedding[item] ) def to(self, device: torch.device): """ Transfers all tensors to the given device """ coarse_segm = self.coarse_segm.to(device) embedding = self.embedding.to(device) return DensePoseEmbeddingPredictorOutput(coarse_segm=coarse_segm, embedding=embedding) ================================================ FILE: detectron2/projects/DensePose/densepose/structures/cse_confidence.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from dataclasses import make_dataclass from functools import lru_cache from typing import Any, Optional import torch @lru_cache(maxsize=None) def decorate_cse_predictor_output_class_with_confidences(BasePredictorOutput: type) -> type: """ Create a new output class from an existing one by adding new attributes related to confidence estimation: - coarse_segm_confidence (tensor) Details on confidence estimation parameters can be found in: N. Neverova, D. Novotny, A. Vedaldi "Correlated Uncertainty for Learning Dense Correspondences from Noisy Labels", p. 918--926, in Proc. NIPS 2019 A. Sanakoyeu et al., Transferring Dense Pose to Proximal Animal Classes, CVPR 2020 The new class inherits the provided `BasePredictorOutput` class, it's name is composed of the name of the provided class and "WithConfidences" suffix. Args: BasePredictorOutput (type): output type to which confidence data is to be added, assumed to be a dataclass Return: New dataclass derived from the provided one that has attributes for confidence estimation """ PredictorOutput = make_dataclass( BasePredictorOutput.__name__ + "WithConfidences", fields=[ ("coarse_segm_confidence", Optional[torch.Tensor], None), ], bases=(BasePredictorOutput,), ) # add possibility to index PredictorOutput def slice_if_not_none(data, item): if data is None: return None if isinstance(item, int): return data[item].unsqueeze(0) return data[item] def PredictorOutput_getitem(self, item): PredictorOutput = type(self) base_predictor_output_sliced = super(PredictorOutput, self).__getitem__(item) return PredictorOutput( **base_predictor_output_sliced.__dict__, coarse_segm_confidence=slice_if_not_none(self.coarse_segm_confidence, item), ) PredictorOutput.__getitem__ = PredictorOutput_getitem def PredictorOutput_to(self, device: torch.device): """ Transfers all tensors to the given device """ PredictorOutput = type(self) base_predictor_output_to = super(PredictorOutput, self).to(device) # pyre-ignore[16] def to_device_if_tensor(var: Any): if isinstance(var, torch.Tensor): return var.to(device) return var return PredictorOutput( **base_predictor_output_to.__dict__, coarse_segm_confidence=to_device_if_tensor(self.coarse_segm_confidence), ) PredictorOutput.to = PredictorOutput_to return PredictorOutput ================================================ FILE: detectron2/projects/DensePose/densepose/structures/data_relative.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import torch from torch.nn import functional as F from densepose.data.meshes.catalog import MeshCatalog from densepose.structures.mesh import load_mesh_symmetry from densepose.structures.transform_data import DensePoseTransformData class DensePoseDataRelative(object): """ Dense pose relative annotations that can be applied to any bounding box: x - normalized X coordinates [0, 255] of annotated points y - normalized Y coordinates [0, 255] of annotated points i - body part labels 0,...,24 for annotated points u - body part U coordinates [0, 1] for annotated points v - body part V coordinates [0, 1] for annotated points segm - 256x256 segmentation mask with values 0,...,14 To obtain absolute x and y data wrt some bounding box one needs to first divide the data by 256, multiply by the respective bounding box size and add bounding box offset: x_img = x0 + x_norm * w / 256.0 y_img = y0 + y_norm * h / 256.0 Segmentation masks are typically sampled to get image-based masks. """ # Key for normalized X coordinates in annotation dict X_KEY = "dp_x" # Key for normalized Y coordinates in annotation dict Y_KEY = "dp_y" # Key for U part coordinates in annotation dict (used in chart-based annotations) U_KEY = "dp_U" # Key for V part coordinates in annotation dict (used in chart-based annotations) V_KEY = "dp_V" # Key for I point labels in annotation dict (used in chart-based annotations) I_KEY = "dp_I" # Key for segmentation mask in annotation dict S_KEY = "dp_masks" # Key for vertex ids (used in continuous surface embeddings annotations) VERTEX_IDS_KEY = "dp_vertex" # Key for mesh id (used in continuous surface embeddings annotations) MESH_NAME_KEY = "ref_model" # Number of body parts in segmentation masks N_BODY_PARTS = 14 # Number of parts in point labels N_PART_LABELS = 24 MASK_SIZE = 256 def __init__(self, annotation, cleanup=False): self.x = torch.as_tensor(annotation[DensePoseDataRelative.X_KEY]) self.y = torch.as_tensor(annotation[DensePoseDataRelative.Y_KEY]) if ( DensePoseDataRelative.I_KEY in annotation and DensePoseDataRelative.U_KEY in annotation and DensePoseDataRelative.V_KEY in annotation ): self.i = torch.as_tensor(annotation[DensePoseDataRelative.I_KEY]) self.u = torch.as_tensor(annotation[DensePoseDataRelative.U_KEY]) self.v = torch.as_tensor(annotation[DensePoseDataRelative.V_KEY]) if ( DensePoseDataRelative.VERTEX_IDS_KEY in annotation and DensePoseDataRelative.MESH_NAME_KEY in annotation ): self.vertex_ids = torch.as_tensor( annotation[DensePoseDataRelative.VERTEX_IDS_KEY], dtype=torch.long ) self.mesh_id = MeshCatalog.get_mesh_id(annotation[DensePoseDataRelative.MESH_NAME_KEY]) if DensePoseDataRelative.S_KEY in annotation: self.segm = DensePoseDataRelative.extract_segmentation_mask(annotation) self.device = torch.device("cpu") if cleanup: DensePoseDataRelative.cleanup_annotation(annotation) def to(self, device): if self.device == device: return self new_data = DensePoseDataRelative.__new__(DensePoseDataRelative) new_data.x = self.x.to(device) new_data.y = self.y.to(device) for attr in ["i", "u", "v", "vertex_ids", "segm"]: if hasattr(self, attr): setattr(new_data, attr, getattr(self, attr).to(device)) if hasattr(self, "mesh_id"): new_data.mesh_id = self.mesh_id new_data.device = device return new_data @staticmethod def extract_segmentation_mask(annotation): import pycocotools.mask as mask_utils # TODO: annotation instance is accepted if it contains either # DensePose segmentation or instance segmentation. However, here we # only rely on DensePose segmentation poly_specs = annotation[DensePoseDataRelative.S_KEY] if isinstance(poly_specs, torch.Tensor): # data is already given as mask tensors, no need to decode return poly_specs segm = torch.zeros((DensePoseDataRelative.MASK_SIZE,) * 2, dtype=torch.float32) if isinstance(poly_specs, dict): if poly_specs: mask = mask_utils.decode(poly_specs) segm[mask > 0] = 1 else: for i in range(len(poly_specs)): poly_i = poly_specs[i] if poly_i: mask_i = mask_utils.decode(poly_i) segm[mask_i > 0] = i + 1 return segm @staticmethod def validate_annotation(annotation): for key in [ DensePoseDataRelative.X_KEY, DensePoseDataRelative.Y_KEY, ]: if key not in annotation: return False, "no {key} data in the annotation".format(key=key) valid_for_iuv_setting = all( key in annotation for key in [ DensePoseDataRelative.I_KEY, DensePoseDataRelative.U_KEY, DensePoseDataRelative.V_KEY, ] ) valid_for_cse_setting = all( key in annotation for key in [ DensePoseDataRelative.VERTEX_IDS_KEY, DensePoseDataRelative.MESH_NAME_KEY, ] ) if not valid_for_iuv_setting and not valid_for_cse_setting: return ( False, "expected either {} (IUV setting) or {} (CSE setting) annotations".format( ", ".join( [ DensePoseDataRelative.I_KEY, DensePoseDataRelative.U_KEY, DensePoseDataRelative.V_KEY, ] ), ", ".join( [ DensePoseDataRelative.VERTEX_IDS_KEY, DensePoseDataRelative.MESH_NAME_KEY, ] ), ), ) return True, None @staticmethod def cleanup_annotation(annotation): for key in [ DensePoseDataRelative.X_KEY, DensePoseDataRelative.Y_KEY, DensePoseDataRelative.I_KEY, DensePoseDataRelative.U_KEY, DensePoseDataRelative.V_KEY, DensePoseDataRelative.S_KEY, DensePoseDataRelative.VERTEX_IDS_KEY, DensePoseDataRelative.MESH_NAME_KEY, ]: if key in annotation: del annotation[key] def apply_transform(self, transforms, densepose_transform_data): self._transform_pts(transforms, densepose_transform_data) if hasattr(self, "segm"): self._transform_segm(transforms, densepose_transform_data) def _transform_pts(self, transforms, dp_transform_data): import detectron2.data.transforms as T # NOTE: This assumes that HorizFlipTransform is the only one that does flip do_hflip = sum(isinstance(t, T.HFlipTransform) for t in transforms.transforms) % 2 == 1 if do_hflip: self.x = self.MASK_SIZE - self.x if hasattr(self, "i"): self._flip_iuv_semantics(dp_transform_data) if hasattr(self, "vertex_ids"): self._flip_vertices() for t in transforms.transforms: if isinstance(t, T.RotationTransform): xy_scale = np.array((t.w, t.h)) / DensePoseDataRelative.MASK_SIZE xy = t.apply_coords(np.stack((self.x, self.y), axis=1) * xy_scale) self.x, self.y = torch.tensor(xy / xy_scale, dtype=self.x.dtype).T def _flip_iuv_semantics(self, dp_transform_data: DensePoseTransformData) -> None: i_old = self.i.clone() uv_symmetries = dp_transform_data.uv_symmetries pt_label_symmetries = dp_transform_data.point_label_symmetries for i in range(self.N_PART_LABELS): if i + 1 in i_old: annot_indices_i = i_old == i + 1 if pt_label_symmetries[i + 1] != i + 1: self.i[annot_indices_i] = pt_label_symmetries[i + 1] u_loc = (self.u[annot_indices_i] * 255).long() v_loc = (self.v[annot_indices_i] * 255).long() self.u[annot_indices_i] = uv_symmetries["U_transforms"][i][v_loc, u_loc].to( device=self.u.device ) self.v[annot_indices_i] = uv_symmetries["V_transforms"][i][v_loc, u_loc].to( device=self.v.device ) def _flip_vertices(self): mesh_info = MeshCatalog[MeshCatalog.get_mesh_name(self.mesh_id)] mesh_symmetry = ( load_mesh_symmetry(mesh_info.symmetry) if mesh_info.symmetry is not None else None ) self.vertex_ids = mesh_symmetry["vertex_transforms"][self.vertex_ids] def _transform_segm(self, transforms, dp_transform_data): import detectron2.data.transforms as T # NOTE: This assumes that HorizFlipTransform is the only one that does flip do_hflip = sum(isinstance(t, T.HFlipTransform) for t in transforms.transforms) % 2 == 1 if do_hflip: self.segm = torch.flip(self.segm, [1]) self._flip_segm_semantics(dp_transform_data) for t in transforms.transforms: if isinstance(t, T.RotationTransform): self._transform_segm_rotation(t) def _flip_segm_semantics(self, dp_transform_data): old_segm = self.segm.clone() mask_label_symmetries = dp_transform_data.mask_label_symmetries for i in range(self.N_BODY_PARTS): if mask_label_symmetries[i + 1] != i + 1: self.segm[old_segm == i + 1] = mask_label_symmetries[i + 1] def _transform_segm_rotation(self, rotation): self.segm = F.interpolate(self.segm[None, None, :], (rotation.h, rotation.w)).numpy() self.segm = torch.tensor(rotation.apply_segmentation(self.segm[0, 0]))[None, None, :] self.segm = F.interpolate(self.segm, [DensePoseDataRelative.MASK_SIZE] * 2)[0, 0] ================================================ FILE: detectron2/projects/DensePose/densepose/structures/list.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch from densepose.structures.data_relative import DensePoseDataRelative class DensePoseList(object): _TORCH_DEVICE_CPU = torch.device("cpu") def __init__(self, densepose_datas, boxes_xyxy_abs, image_size_hw, device=_TORCH_DEVICE_CPU): assert len(densepose_datas) == len( boxes_xyxy_abs ), "Attempt to initialize DensePoseList with {} DensePose datas " "and {} boxes".format( len(densepose_datas), len(boxes_xyxy_abs) ) self.densepose_datas = [] for densepose_data in densepose_datas: assert isinstance(densepose_data, DensePoseDataRelative) or densepose_data is None, ( "Attempt to initialize DensePoseList with DensePose datas " "of type {}, expected DensePoseDataRelative".format(type(densepose_data)) ) densepose_data_ondevice = ( densepose_data.to(device) if densepose_data is not None else None ) self.densepose_datas.append(densepose_data_ondevice) self.boxes_xyxy_abs = boxes_xyxy_abs.to(device) self.image_size_hw = image_size_hw self.device = device def to(self, device): if self.device == device: return self return DensePoseList(self.densepose_datas, self.boxes_xyxy_abs, self.image_size_hw, device) def __iter__(self): return iter(self.densepose_datas) def __len__(self): return len(self.densepose_datas) def __repr__(self): s = self.__class__.__name__ + "(" s += "num_instances={}, ".format(len(self.densepose_datas)) s += "image_width={}, ".format(self.image_size_hw[1]) s += "image_height={})".format(self.image_size_hw[0]) return s def __getitem__(self, item): if isinstance(item, int): densepose_data_rel = self.densepose_datas[item] return densepose_data_rel elif isinstance(item, slice): densepose_datas_rel = self.densepose_datas[item] boxes_xyxy_abs = self.boxes_xyxy_abs[item] return DensePoseList( densepose_datas_rel, boxes_xyxy_abs, self.image_size_hw, self.device ) elif isinstance(item, torch.Tensor) and (item.dtype == torch.bool): densepose_datas_rel = [self.densepose_datas[i] for i, x in enumerate(item) if x > 0] boxes_xyxy_abs = self.boxes_xyxy_abs[item] return DensePoseList( densepose_datas_rel, boxes_xyxy_abs, self.image_size_hw, self.device ) else: densepose_datas_rel = [self.densepose_datas[i] for i in item] boxes_xyxy_abs = self.boxes_xyxy_abs[item] return DensePoseList( densepose_datas_rel, boxes_xyxy_abs, self.image_size_hw, self.device ) ================================================ FILE: detectron2/projects/DensePose/densepose/structures/mesh.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import pickle from functools import lru_cache from typing import Dict, Optional, Tuple import torch from detectron2.utils.file_io import PathManager from densepose.data.meshes.catalog import MeshCatalog, MeshInfo def _maybe_copy_to_device( attribute: Optional[torch.Tensor], device: torch.device ) -> Optional[torch.Tensor]: if attribute is None: return None return attribute.to(device) class Mesh: def __init__( self, vertices: Optional[torch.Tensor] = None, faces: Optional[torch.Tensor] = None, geodists: Optional[torch.Tensor] = None, symmetry: Optional[Dict[str, torch.Tensor]] = None, texcoords: Optional[torch.Tensor] = None, mesh_info: Optional[MeshInfo] = None, device: Optional[torch.device] = None, ): """ Args: vertices (tensor [N, 3] of float32): vertex coordinates in 3D faces (tensor [M, 3] of long): triangular face represented as 3 vertex indices geodists (tensor [N, N] of float32): geodesic distances from vertex `i` to vertex `j` (optional, default: None) symmetry (dict: str -> tensor): various mesh symmetry data: - "vertex_transforms": vertex mapping under horizontal flip, tensor of size [N] of type long; vertex `i` is mapped to vertex `tensor[i]` (optional, default: None) texcoords (tensor [N, 2] of float32): texture coordinates, i.e. global and normalized mesh UVs (optional, default: None) mesh_info (MeshInfo type): necessary to load the attributes on-the-go, can be used instead of passing all the variables one by one device (torch.device): device of the Mesh. If not provided, will use the device of the vertices """ self._vertices = vertices self._faces = faces self._geodists = geodists self._symmetry = symmetry self._texcoords = texcoords self.mesh_info = mesh_info self.device = device assert self._vertices is not None or self.mesh_info is not None all_fields = [self._vertices, self._faces, self._geodists, self._texcoords] if self.device is None: for field in all_fields: if field is not None: self.device = field.device break if self.device is None and symmetry is not None: for key in symmetry: self.device = symmetry[key].device break self.device = torch.device("cpu") if self.device is None else self.device assert all([var.device == self.device for var in all_fields if var is not None]) if symmetry: assert all(symmetry[key].device == self.device for key in symmetry) if texcoords and vertices: assert len(vertices) == len(texcoords) def to(self, device: torch.device): device_symmetry = self._symmetry if device_symmetry: device_symmetry = {key: value.to(device) for key, value in device_symmetry.items()} return Mesh( _maybe_copy_to_device(self._vertices, device), _maybe_copy_to_device(self._faces, device), _maybe_copy_to_device(self._geodists, device), device_symmetry, _maybe_copy_to_device(self._texcoords, device), self.mesh_info, device, ) @property def vertices(self): if self._vertices is None and self.mesh_info is not None: self._vertices = load_mesh_data(self.mesh_info.data, "vertices", self.device) return self._vertices @property def faces(self): if self._faces is None and self.mesh_info is not None: self._faces = load_mesh_data(self.mesh_info.data, "faces", self.device) return self._faces @property def geodists(self): if self._geodists is None and self.mesh_info is not None: self._geodists = load_mesh_auxiliary_data(self.mesh_info.geodists, self.device) return self._geodists @property def symmetry(self): if self._symmetry is None and self.mesh_info is not None: self._symmetry = load_mesh_symmetry(self.mesh_info.symmetry, self.device) return self._symmetry @property def texcoords(self): if self._texcoords is None and self.mesh_info is not None: self._texcoords = load_mesh_auxiliary_data(self.mesh_info.texcoords, self.device) return self._texcoords def get_geodists(self): if self.geodists is None: self.geodists = self._compute_geodists() return self.geodists def _compute_geodists(self): # TODO: compute using Laplace-Beltrami geodists = None return geodists def load_mesh_data( mesh_fpath: str, field: str, device: Optional[torch.device] = None ) -> Tuple[Optional[torch.Tensor], Optional[torch.Tensor]]: with PathManager.open(mesh_fpath, "rb") as hFile: # pyre-fixme[7]: Expected `Tuple[Optional[Tensor], Optional[Tensor]]` but # got `Tensor`. return torch.as_tensor(pickle.load(hFile)[field], dtype=torch.float).to( # pyre-ignore[6] device ) return None def load_mesh_auxiliary_data( fpath: str, device: Optional[torch.device] = None ) -> Optional[torch.Tensor]: fpath_local = PathManager.get_local_path(fpath) with PathManager.open(fpath_local, "rb") as hFile: return torch.as_tensor(pickle.load(hFile), dtype=torch.float).to(device) # pyre-ignore[6] return None @lru_cache() def load_mesh_symmetry( symmetry_fpath: str, device: Optional[torch.device] = None ) -> Optional[Dict[str, torch.Tensor]]: with PathManager.open(symmetry_fpath, "rb") as hFile: symmetry_loaded = pickle.load(hFile) # pyre-ignore[6] symmetry = { "vertex_transforms": torch.as_tensor( symmetry_loaded["vertex_transforms"], dtype=torch.long ).to(device), } return symmetry return None @lru_cache() def create_mesh(mesh_name: str, device: Optional[torch.device] = None) -> Mesh: return Mesh(mesh_info=MeshCatalog[mesh_name], device=device) ================================================ FILE: detectron2/projects/DensePose/densepose/structures/transform_data.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import BinaryIO, Dict, Union import torch def normalized_coords_transform(x0, y0, w, h): """ Coordinates transform that maps top left corner to (-1, -1) and bottom right corner to (1, 1). Used for torch.grid_sample to initialize the grid """ def f(p): return (2 * (p[0] - x0) / w - 1, 2 * (p[1] - y0) / h - 1) return f class DensePoseTransformData(object): # Horizontal symmetry label transforms used for horizontal flip MASK_LABEL_SYMMETRIES = [0, 1, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 14] # fmt: off POINT_LABEL_SYMMETRIES = [ 0, 1, 2, 4, 3, 6, 5, 8, 7, 10, 9, 12, 11, 14, 13, 16, 15, 18, 17, 20, 19, 22, 21, 24, 23] # noqa # fmt: on def __init__(self, uv_symmetries: Dict[str, torch.Tensor], device: torch.device): self.mask_label_symmetries = DensePoseTransformData.MASK_LABEL_SYMMETRIES self.point_label_symmetries = DensePoseTransformData.POINT_LABEL_SYMMETRIES self.uv_symmetries = uv_symmetries self.device = torch.device("cpu") def to(self, device: torch.device, copy: bool = False) -> "DensePoseTransformData": """ Convert transform data to the specified device Args: device (torch.device): device to convert the data to copy (bool): flag that specifies whether to copy or to reference the data in case the device is the same Return: An instance of `DensePoseTransformData` with data stored on the specified device """ if self.device == device and not copy: return self uv_symmetry_map = {} for key in self.uv_symmetries: uv_symmetry_map[key] = self.uv_symmetries[key].to(device=device, copy=copy) return DensePoseTransformData(uv_symmetry_map, device) @staticmethod def load(io: Union[str, BinaryIO]): """ Args: io: (str or binary file-like object): input file to load data from Returns: An instance of `DensePoseTransformData` with transforms loaded from the file """ import scipy.io uv_symmetry_map = scipy.io.loadmat(io) uv_symmetry_map_torch = {} for key in ["U_transforms", "V_transforms"]: uv_symmetry_map_torch[key] = [] map_src = uv_symmetry_map[key] map_dst = uv_symmetry_map_torch[key] for i in range(map_src.shape[1]): map_dst.append(torch.from_numpy(map_src[0, i]).to(dtype=torch.float)) uv_symmetry_map_torch[key] = torch.stack(map_dst, dim=0) transform_data = DensePoseTransformData(uv_symmetry_map_torch, device=torch.device("cpu")) return transform_data ================================================ FILE: detectron2/projects/DensePose/densepose/utils/__init__.py ================================================ ================================================ FILE: detectron2/projects/DensePose/densepose/utils/dbhelper.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from typing import Any, Dict, Optional, Tuple class EntrySelector(object): """ Base class for entry selectors """ @staticmethod def from_string(spec: str) -> "EntrySelector": if spec == "*": return AllEntrySelector() return FieldEntrySelector(spec) class AllEntrySelector(EntrySelector): """ Selector that accepts all entries """ SPECIFIER = "*" def __call__(self, entry): return True class FieldEntrySelector(EntrySelector): """ Selector that accepts only entries that match provided field specifier(s). Only a limited set of specifiers is supported for now: ::=[] ::=[] is a valid identifier ::= "int" | "str" ::= "=" ::= "," ::= ":" ::= | ::= ::= "-" is a string without spaces and special symbols (e.g. , , , ) """ _SPEC_DELIM = "," _TYPE_DELIM = ":" _RANGE_DELIM = "-" _EQUAL = "=" _ERROR_PREFIX = "Invalid field selector specifier" class _FieldEntryValuePredicate(object): """ Predicate that checks strict equality for the specified entry field """ def __init__(self, name: str, typespec: Optional[str], value: str): import builtins self.name = name self.type = getattr(builtins, typespec) if typespec is not None else str self.value = value def __call__(self, entry): return entry[self.name] == self.type(self.value) class _FieldEntryRangePredicate(object): """ Predicate that checks whether an entry field falls into the specified range """ def __init__(self, name: str, typespec: Optional[str], vmin: str, vmax: str): import builtins self.name = name self.type = getattr(builtins, typespec) if typespec is not None else str self.vmin = vmin self.vmax = vmax def __call__(self, entry): return (entry[self.name] >= self.type(self.vmin)) and ( entry[self.name] <= self.type(self.vmax) ) def __init__(self, spec: str): self._predicates = self._parse_specifier_into_predicates(spec) def __call__(self, entry: Dict[str, Any]): for predicate in self._predicates: if not predicate(entry): return False return True def _parse_specifier_into_predicates(self, spec: str): predicates = [] specs = spec.split(self._SPEC_DELIM) for subspec in specs: eq_idx = subspec.find(self._EQUAL) if eq_idx > 0: field_name_with_type = subspec[:eq_idx] field_name, field_type = self._parse_field_name_type(field_name_with_type) field_value_or_range = subspec[eq_idx + 1 :] if self._is_range_spec(field_value_or_range): vmin, vmax = self._get_range_spec(field_value_or_range) predicate = FieldEntrySelector._FieldEntryRangePredicate( field_name, field_type, vmin, vmax ) else: predicate = FieldEntrySelector._FieldEntryValuePredicate( field_name, field_type, field_value_or_range ) predicates.append(predicate) elif eq_idx == 0: self._parse_error(f'"{subspec}", field name is empty!') else: self._parse_error(f'"{subspec}", should have format ' "=!") return predicates def _parse_field_name_type(self, field_name_with_type: str) -> Tuple[str, Optional[str]]: type_delim_idx = field_name_with_type.find(self._TYPE_DELIM) if type_delim_idx > 0: field_name = field_name_with_type[:type_delim_idx] field_type = field_name_with_type[type_delim_idx + 1 :] elif type_delim_idx == 0: self._parse_error(f'"{field_name_with_type}", field name is empty!') else: field_name = field_name_with_type field_type = None # pyre-fixme[61]: `field_name` may not be initialized here. # pyre-fixme[61]: `field_type` may not be initialized here. return field_name, field_type def _is_range_spec(self, field_value_or_range): delim_idx = field_value_or_range.find(self._RANGE_DELIM) return delim_idx > 0 def _get_range_spec(self, field_value_or_range): if self._is_range_spec(field_value_or_range): delim_idx = field_value_or_range.find(self._RANGE_DELIM) vmin = field_value_or_range[:delim_idx] vmax = field_value_or_range[delim_idx + 1 :] return vmin, vmax else: self._parse_error('"field_value_or_range", range of values expected!') def _parse_error(self, msg): raise ValueError(f"{self._ERROR_PREFIX}: {msg}") ================================================ FILE: detectron2/projects/DensePose/densepose/utils/logger.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging def verbosity_to_level(verbosity) -> int: if verbosity is not None: if verbosity == 0: return logging.WARNING elif verbosity == 1: return logging.INFO elif verbosity >= 2: return logging.DEBUG return logging.WARNING ================================================ FILE: detectron2/projects/DensePose/densepose/utils/transform.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.data import MetadataCatalog from detectron2.utils.file_io import PathManager from densepose import DensePoseTransformData def load_for_dataset(dataset_name): path = MetadataCatalog.get(dataset_name).densepose_transform_src densepose_transform_data_fpath = PathManager.get_local_path(path) return DensePoseTransformData.load(densepose_transform_data_fpath) def load_from_cfg(cfg): return load_for_dataset(cfg.DATASETS.TEST[0]) ================================================ FILE: detectron2/projects/DensePose/densepose/vis/__init__.py ================================================ ================================================ FILE: detectron2/projects/DensePose/densepose/vis/base.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np import cv2 import torch Image = np.ndarray Boxes = torch.Tensor class MatrixVisualizer(object): """ Base visualizer for matrix data """ def __init__( self, inplace=True, cmap=cv2.COLORMAP_PARULA, val_scale=1.0, alpha=0.7, interp_method_matrix=cv2.INTER_LINEAR, interp_method_mask=cv2.INTER_NEAREST, ): self.inplace = inplace self.cmap = cmap self.val_scale = val_scale self.alpha = alpha self.interp_method_matrix = interp_method_matrix self.interp_method_mask = interp_method_mask def visualize(self, image_bgr, mask, matrix, bbox_xywh): self._check_image(image_bgr) self._check_mask_matrix(mask, matrix) if self.inplace: image_target_bgr = image_bgr else: image_target_bgr = image_bgr * 0 x, y, w, h = [int(v) for v in bbox_xywh] if w <= 0 or h <= 0: return image_bgr mask, matrix = self._resize(mask, matrix, w, h) mask_bg = np.tile((mask == 0)[:, :, np.newaxis], [1, 1, 3]) matrix_scaled = matrix.astype(np.float32) * self.val_scale _EPSILON = 1e-6 if np.any(matrix_scaled > 255 + _EPSILON): logger = logging.getLogger(__name__) logger.warning( f"Matrix has values > {255 + _EPSILON} after " f"scaling, clipping to [0..255]" ) matrix_scaled_8u = matrix_scaled.clip(0, 255).astype(np.uint8) matrix_vis = cv2.applyColorMap(matrix_scaled_8u, self.cmap) matrix_vis[mask_bg] = image_target_bgr[y : y + h, x : x + w, :][mask_bg] image_target_bgr[y : y + h, x : x + w, :] = ( image_target_bgr[y : y + h, x : x + w, :] * (1.0 - self.alpha) + matrix_vis * self.alpha ) return image_target_bgr.astype(np.uint8) def _resize(self, mask, matrix, w, h): if (w != mask.shape[1]) or (h != mask.shape[0]): mask = cv2.resize(mask, (w, h), self.interp_method_mask) if (w != matrix.shape[1]) or (h != matrix.shape[0]): matrix = cv2.resize(matrix, (w, h), self.interp_method_matrix) return mask, matrix def _check_image(self, image_rgb): assert len(image_rgb.shape) == 3 assert image_rgb.shape[2] == 3 assert image_rgb.dtype == np.uint8 def _check_mask_matrix(self, mask, matrix): assert len(matrix.shape) == 2 assert len(mask.shape) == 2 assert mask.dtype == np.uint8 class RectangleVisualizer(object): _COLOR_GREEN = (18, 127, 15) def __init__(self, color=_COLOR_GREEN, thickness=1): self.color = color self.thickness = thickness def visualize(self, image_bgr, bbox_xywh, color=None, thickness=None): x, y, w, h = bbox_xywh color = color or self.color thickness = thickness or self.thickness cv2.rectangle(image_bgr, (int(x), int(y)), (int(x + w), int(y + h)), color, thickness) return image_bgr class PointsVisualizer(object): _COLOR_GREEN = (18, 127, 15) def __init__(self, color_bgr=_COLOR_GREEN, r=5): self.color_bgr = color_bgr self.r = r def visualize(self, image_bgr, pts_xy, colors_bgr=None, rs=None): for j, pt_xy in enumerate(pts_xy): x, y = pt_xy color_bgr = colors_bgr[j] if colors_bgr is not None else self.color_bgr r = rs[j] if rs is not None else self.r cv2.circle(image_bgr, (x, y), r, color_bgr, -1) return image_bgr class TextVisualizer(object): _COLOR_GRAY = (218, 227, 218) _COLOR_WHITE = (255, 255, 255) def __init__( self, font_face=cv2.FONT_HERSHEY_SIMPLEX, font_color_bgr=_COLOR_GRAY, font_scale=0.35, font_line_type=cv2.LINE_AA, font_line_thickness=1, fill_color_bgr=_COLOR_WHITE, fill_color_transparency=1.0, frame_color_bgr=_COLOR_WHITE, frame_color_transparency=1.0, frame_thickness=1, ): self.font_face = font_face self.font_color_bgr = font_color_bgr self.font_scale = font_scale self.font_line_type = font_line_type self.font_line_thickness = font_line_thickness self.fill_color_bgr = fill_color_bgr self.fill_color_transparency = fill_color_transparency self.frame_color_bgr = frame_color_bgr self.frame_color_transparency = frame_color_transparency self.frame_thickness = frame_thickness def visualize(self, image_bgr, txt, topleft_xy): txt_w, txt_h = self.get_text_size_wh(txt) topleft_xy = tuple(map(int, topleft_xy)) x, y = topleft_xy if self.frame_color_transparency < 1.0: t = self.frame_thickness image_bgr[y - t : y + txt_h + t, x - t : x + txt_w + t, :] = ( image_bgr[y - t : y + txt_h + t, x - t : x + txt_w + t, :] * self.frame_color_transparency + np.array(self.frame_color_bgr) * (1.0 - self.frame_color_transparency) ).astype(np.float) if self.fill_color_transparency < 1.0: image_bgr[y : y + txt_h, x : x + txt_w, :] = ( image_bgr[y : y + txt_h, x : x + txt_w, :] * self.fill_color_transparency + np.array(self.fill_color_bgr) * (1.0 - self.fill_color_transparency) ).astype(np.float) cv2.putText( image_bgr, txt, topleft_xy, self.font_face, self.font_scale, self.font_color_bgr, self.font_line_thickness, self.font_line_type, ) return image_bgr def get_text_size_wh(self, txt): ((txt_w, txt_h), _) = cv2.getTextSize( txt, self.font_face, self.font_scale, self.font_line_thickness ) return txt_w, txt_h class CompoundVisualizer(object): def __init__(self, visualizers): self.visualizers = visualizers def visualize(self, image_bgr, data): assert len(data) == len( self.visualizers ), "The number of datas {} should match the number of visualizers" " {}".format( len(data), len(self.visualizers) ) image = image_bgr for i, visualizer in enumerate(self.visualizers): image = visualizer.visualize(image, data[i]) return image def __str__(self): visualizer_str = ", ".join([str(v) for v in self.visualizers]) return "Compound Visualizer [{}]".format(visualizer_str) ================================================ FILE: detectron2/projects/DensePose/densepose/vis/bounding_box.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .base import RectangleVisualizer, TextVisualizer class BoundingBoxVisualizer(object): def __init__(self): self.rectangle_visualizer = RectangleVisualizer() def visualize(self, image_bgr, boxes_xywh): for bbox_xywh in boxes_xywh: image_bgr = self.rectangle_visualizer.visualize(image_bgr, bbox_xywh) return image_bgr class ScoredBoundingBoxVisualizer(object): def __init__(self, bbox_visualizer_params=None, score_visualizer_params=None, **kwargs): if bbox_visualizer_params is None: bbox_visualizer_params = {} if score_visualizer_params is None: score_visualizer_params = {} self.visualizer_bbox = RectangleVisualizer(**bbox_visualizer_params) self.visualizer_score = TextVisualizer(**score_visualizer_params) def visualize(self, image_bgr, scored_bboxes): boxes_xywh, box_scores = scored_bboxes assert len(boxes_xywh) == len( box_scores ), "Number of bounding boxes {} should be equal to the number of scores {}".format( len(boxes_xywh), len(box_scores) ) for i, box_xywh in enumerate(boxes_xywh): score_i = box_scores[i] image_bgr = self.visualizer_bbox.visualize(image_bgr, box_xywh) score_txt = "{0:6.4f}".format(score_i) topleft_xy = box_xywh[0], box_xywh[1] image_bgr = self.visualizer_score.visualize(image_bgr, score_txt, topleft_xy) return image_bgr ================================================ FILE: detectron2/projects/DensePose/densepose/vis/densepose_data_points.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from typing import Iterable, Optional, Tuple import cv2 from densepose.structures import DensePoseDataRelative from .base import Boxes, Image, MatrixVisualizer, PointsVisualizer class DensePoseDataCoarseSegmentationVisualizer(object): """ Visualizer for ground truth segmentation """ def __init__(self, inplace=True, cmap=cv2.COLORMAP_PARULA, alpha=0.7, **kwargs): self.mask_visualizer = MatrixVisualizer( inplace=inplace, cmap=cmap, val_scale=255.0 / DensePoseDataRelative.N_BODY_PARTS, alpha=alpha, ) def visualize( self, image_bgr: Image, bbox_densepose_datas: Optional[Tuple[Iterable[Boxes], Iterable[DensePoseDataRelative]]], ) -> Image: if bbox_densepose_datas is None: return image_bgr for bbox_xywh, densepose_data in zip(*bbox_densepose_datas): matrix = densepose_data.segm.numpy() mask = np.zeros(matrix.shape, dtype=np.uint8) mask[matrix > 0] = 1 image_bgr = self.mask_visualizer.visualize(image_bgr, mask, matrix, bbox_xywh.numpy()) return image_bgr class DensePoseDataPointsVisualizer(object): def __init__(self, densepose_data_to_value_fn=None, cmap=cv2.COLORMAP_PARULA, **kwargs): self.points_visualizer = PointsVisualizer() self.densepose_data_to_value_fn = densepose_data_to_value_fn self.cmap = cmap def visualize( self, image_bgr: Image, bbox_densepose_datas: Optional[Tuple[Iterable[Boxes], Iterable[DensePoseDataRelative]]], ) -> Image: if bbox_densepose_datas is None: return image_bgr for bbox_xywh, densepose_data in zip(*bbox_densepose_datas): x0, y0, w, h = bbox_xywh.numpy() x = densepose_data.x.numpy() * w / 255.0 + x0 y = densepose_data.y.numpy() * h / 255.0 + y0 pts_xy = zip(x, y) if self.densepose_data_to_value_fn is None: image_bgr = self.points_visualizer.visualize(image_bgr, pts_xy) else: v = self.densepose_data_to_value_fn(densepose_data) img_colors_bgr = cv2.applyColorMap(v, self.cmap) colors_bgr = [ [int(v) for v in img_color_bgr.ravel()] for img_color_bgr in img_colors_bgr ] image_bgr = self.points_visualizer.visualize(image_bgr, pts_xy, colors_bgr) return image_bgr def _densepose_data_u_for_cmap(densepose_data): u = np.clip(densepose_data.u.numpy(), 0, 1) * 255.0 return u.astype(np.uint8) def _densepose_data_v_for_cmap(densepose_data): v = np.clip(densepose_data.v.numpy(), 0, 1) * 255.0 return v.astype(np.uint8) def _densepose_data_i_for_cmap(densepose_data): i = ( np.clip(densepose_data.i.numpy(), 0.0, DensePoseDataRelative.N_PART_LABELS) * 255.0 / DensePoseDataRelative.N_PART_LABELS ) return i.astype(np.uint8) class DensePoseDataPointsUVisualizer(DensePoseDataPointsVisualizer): def __init__(self, **kwargs): super(DensePoseDataPointsUVisualizer, self).__init__( densepose_data_to_value_fn=_densepose_data_u_for_cmap, **kwargs ) class DensePoseDataPointsVVisualizer(DensePoseDataPointsVisualizer): def __init__(self, **kwargs): super(DensePoseDataPointsVVisualizer, self).__init__( densepose_data_to_value_fn=_densepose_data_v_for_cmap, **kwargs ) class DensePoseDataPointsIVisualizer(DensePoseDataPointsVisualizer): def __init__(self, **kwargs): super(DensePoseDataPointsIVisualizer, self).__init__( densepose_data_to_value_fn=_densepose_data_i_for_cmap, **kwargs ) ================================================ FILE: detectron2/projects/DensePose/densepose/vis/densepose_outputs_iuv.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from typing import Optional, Tuple import cv2 from densepose.structures import DensePoseDataRelative from ..structures import DensePoseChartPredictorOutput from .base import Boxes, Image, MatrixVisualizer class DensePoseOutputsVisualizer(object): def __init__( self, inplace=True, cmap=cv2.COLORMAP_PARULA, alpha=0.7, to_visualize=None, **kwargs ): assert to_visualize in "IUV", "can only visualize IUV" self.to_visualize = to_visualize if self.to_visualize == "I": val_scale = 255.0 / DensePoseDataRelative.N_PART_LABELS else: val_scale = 1.0 self.mask_visualizer = MatrixVisualizer( inplace=inplace, cmap=cmap, val_scale=val_scale, alpha=alpha ) def visualize( self, image_bgr: Image, dp_output_with_bboxes: Tuple[Optional[DensePoseChartPredictorOutput], Optional[Boxes]], ) -> Image: densepose_output, bboxes_xywh = dp_output_with_bboxes if densepose_output is None or bboxes_xywh is None: return image_bgr assert isinstance( densepose_output, DensePoseChartPredictorOutput ), "DensePoseChartPredictorOutput expected, {} encountered".format(type(densepose_output)) S = densepose_output.coarse_segm I = densepose_output.fine_segm # noqa U = densepose_output.u V = densepose_output.v N = S.size(0) assert N == I.size( 0 ), "densepose outputs S {} and I {}" " should have equal first dim size".format( S.size(), I.size() ) assert N == U.size( 0 ), "densepose outputs S {} and U {}" " should have equal first dim size".format( S.size(), U.size() ) assert N == V.size( 0 ), "densepose outputs S {} and V {}" " should have equal first dim size".format( S.size(), V.size() ) assert N == len( bboxes_xywh ), "number of bounding boxes {}" " should be equal to first dim size of outputs {}".format( len(bboxes_xywh), N ) for n in range(N): Sn = S[n].argmax(dim=0) In = I[n].argmax(dim=0) * (Sn > 0).long() segmentation = In.cpu().numpy().astype(np.uint8) mask = np.zeros(segmentation.shape, dtype=np.uint8) mask[segmentation > 0] = 1 bbox_xywh = bboxes_xywh[n] if self.to_visualize == "I": vis = segmentation elif self.to_visualize in "UV": U_or_Vn = {"U": U, "V": V}[self.to_visualize][n].cpu().numpy().astype(np.float32) vis = np.zeros(segmentation.shape, dtype=np.float32) for partId in range(U_or_Vn.shape[0]): vis[segmentation == partId] = ( U_or_Vn[partId][segmentation == partId].clip(0, 1) * 255 ) # pyre-fixme[61]: `vis` may not be initialized here. image_bgr = self.mask_visualizer.visualize(image_bgr, mask, vis, bbox_xywh) return image_bgr class DensePoseOutputsUVisualizer(DensePoseOutputsVisualizer): def __init__(self, inplace=True, cmap=cv2.COLORMAP_PARULA, alpha=0.7, **kwargs): super().__init__(inplace=inplace, cmap=cmap, alpha=alpha, to_visualize="U", **kwargs) class DensePoseOutputsVVisualizer(DensePoseOutputsVisualizer): def __init__(self, inplace=True, cmap=cv2.COLORMAP_PARULA, alpha=0.7, **kwargs): super().__init__(inplace=inplace, cmap=cmap, alpha=alpha, to_visualize="V", **kwargs) class DensePoseOutputsFineSegmentationVisualizer(DensePoseOutputsVisualizer): def __init__(self, inplace=True, cmap=cv2.COLORMAP_PARULA, alpha=0.7, **kwargs): super().__init__(inplace=inplace, cmap=cmap, alpha=alpha, to_visualize="I", **kwargs) ================================================ FILE: detectron2/projects/DensePose/densepose/vis/densepose_outputs_vertex.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import json import numpy as np from functools import lru_cache from typing import Dict, List, Optional, Tuple import cv2 import torch from detectron2.utils.file_io import PathManager from densepose.modeling import build_densepose_embedder from densepose.modeling.cse.utils import get_closest_vertices_mask_from_ES from ..data.utils import get_class_to_mesh_name_mapping from ..structures import DensePoseEmbeddingPredictorOutput from ..structures.mesh import create_mesh from .base import Boxes, Image, MatrixVisualizer from .densepose_results_textures import get_texture_atlas @lru_cache() def get_xyz_vertex_embedding(mesh_name: str, device: torch.device): if mesh_name == "smpl_27554": embed_path = PathManager.get_local_path( "https://dl.fbaipublicfiles.com/densepose/data/cse/mds_d=256.npy" ) embed_map, _ = np.load(embed_path, allow_pickle=True) embed_map = torch.tensor(embed_map).float()[:, 0] embed_map -= embed_map.min() embed_map /= embed_map.max() else: mesh = create_mesh(mesh_name, device) embed_map = mesh.vertices.sum(dim=1) embed_map -= embed_map.min() embed_map /= embed_map.max() embed_map = embed_map**2 return embed_map class DensePoseOutputsVertexVisualizer(object): def __init__( self, cfg, inplace=True, cmap=cv2.COLORMAP_JET, alpha=0.7, device="cuda", default_class=0, **kwargs, ): self.mask_visualizer = MatrixVisualizer( inplace=inplace, cmap=cmap, val_scale=1.0, alpha=alpha ) self.class_to_mesh_name = get_class_to_mesh_name_mapping(cfg) self.embedder = build_densepose_embedder(cfg) self.device = torch.device(device) self.default_class = default_class self.mesh_vertex_embeddings = { mesh_name: self.embedder(mesh_name).to(self.device) for mesh_name in self.class_to_mesh_name.values() if self.embedder.has_embeddings(mesh_name) } def visualize( self, image_bgr: Image, outputs_boxes_xywh_classes: Tuple[ Optional[DensePoseEmbeddingPredictorOutput], Optional[Boxes], Optional[List[int]] ], ) -> Image: if outputs_boxes_xywh_classes[0] is None: return image_bgr S, E, N, bboxes_xywh, pred_classes = self.extract_and_check_outputs_and_boxes( outputs_boxes_xywh_classes ) for n in range(N): x, y, w, h = bboxes_xywh[n].int().tolist() mesh_name = self.class_to_mesh_name[pred_classes[n]] closest_vertices, mask = get_closest_vertices_mask_from_ES( E[[n]], S[[n]], h, w, self.mesh_vertex_embeddings[mesh_name], self.device, ) embed_map = get_xyz_vertex_embedding(mesh_name, self.device) vis = (embed_map[closest_vertices].clip(0, 1) * 255.0).cpu().numpy() mask_numpy = mask.cpu().numpy().astype(dtype=np.uint8) image_bgr = self.mask_visualizer.visualize(image_bgr, mask_numpy, vis, [x, y, w, h]) return image_bgr def extract_and_check_outputs_and_boxes(self, outputs_boxes_xywh_classes): densepose_output, bboxes_xywh, pred_classes = outputs_boxes_xywh_classes if pred_classes is None: pred_classes = [self.default_class] * len(bboxes_xywh) assert isinstance( densepose_output, DensePoseEmbeddingPredictorOutput ), "DensePoseEmbeddingPredictorOutput expected, {} encountered".format( type(densepose_output) ) S = densepose_output.coarse_segm E = densepose_output.embedding N = S.size(0) assert N == E.size( 0 ), "CSE coarse_segm {} and embeddings {}" " should have equal first dim size".format( S.size(), E.size() ) assert N == len( bboxes_xywh ), "number of bounding boxes {}" " should be equal to first dim size of outputs {}".format( len(bboxes_xywh), N ) assert N == len(pred_classes), ( "number of predicted classes {}" " should be equal to first dim size of outputs {}".format(len(bboxes_xywh), N) ) return S, E, N, bboxes_xywh, pred_classes def get_texture_atlases(json_str: Optional[str]) -> Optional[Dict[str, Optional[np.ndarray]]]: """ json_str is a JSON string representing a mesh_name -> texture_atlas_path dictionary """ if json_str is None: return None paths = json.loads(json_str) return {mesh_name: get_texture_atlas(path) for mesh_name, path in paths.items()} class DensePoseOutputsTextureVisualizer(DensePoseOutputsVertexVisualizer): def __init__( self, cfg, texture_atlases_dict, device="cuda", default_class=0, **kwargs, ): self.embedder = build_densepose_embedder(cfg) self.texture_image_dict = {} self.alpha_dict = {} for mesh_name in texture_atlases_dict.keys(): if texture_atlases_dict[mesh_name].shape[-1] == 4: # Image with alpha channel self.alpha_dict[mesh_name] = texture_atlases_dict[mesh_name][:, :, -1] / 255.0 self.texture_image_dict[mesh_name] = texture_atlases_dict[mesh_name][:, :, :3] else: self.alpha_dict[mesh_name] = texture_atlases_dict[mesh_name].sum(axis=-1) > 0 self.texture_image_dict[mesh_name] = texture_atlases_dict[mesh_name] self.device = torch.device(device) self.class_to_mesh_name = get_class_to_mesh_name_mapping(cfg) self.default_class = default_class self.mesh_vertex_embeddings = { mesh_name: self.embedder(mesh_name).to(self.device) for mesh_name in self.class_to_mesh_name.values() } def visualize( self, image_bgr: Image, outputs_boxes_xywh_classes: Tuple[ Optional[DensePoseEmbeddingPredictorOutput], Optional[Boxes], Optional[List[int]] ], ) -> Image: image_target_bgr = image_bgr.copy() if outputs_boxes_xywh_classes[0] is None: return image_target_bgr S, E, N, bboxes_xywh, pred_classes = self.extract_and_check_outputs_and_boxes( outputs_boxes_xywh_classes ) meshes = { p: create_mesh(self.class_to_mesh_name[p], self.device) for p in np.unique(pred_classes) } for n in range(N): x, y, w, h = bboxes_xywh[n].int().cpu().numpy() mesh_name = self.class_to_mesh_name[pred_classes[n]] closest_vertices, mask = get_closest_vertices_mask_from_ES( E[[n]], S[[n]], h, w, self.mesh_vertex_embeddings[mesh_name], self.device, ) uv_array = meshes[pred_classes[n]].texcoords[closest_vertices].permute((2, 0, 1)) uv_array = uv_array.cpu().numpy().clip(0, 1) textured_image = self.generate_image_with_texture( image_target_bgr[y : y + h, x : x + w], uv_array, mask.cpu().numpy(), self.class_to_mesh_name[pred_classes[n]], ) if textured_image is None: continue image_target_bgr[y : y + h, x : x + w] = textured_image return image_target_bgr def generate_image_with_texture(self, bbox_image_bgr, uv_array, mask, mesh_name): alpha = self.alpha_dict.get(mesh_name) texture_image = self.texture_image_dict.get(mesh_name) if alpha is None or texture_image is None: return None U, V = uv_array x_index = (U * texture_image.shape[1]).astype(int) y_index = (V * texture_image.shape[0]).astype(int) local_texture = texture_image[y_index, x_index][mask] local_alpha = np.expand_dims(alpha[y_index, x_index][mask], -1) output_image = bbox_image_bgr.copy() output_image[mask] = output_image[mask] * (1 - local_alpha) + local_texture * local_alpha return output_image.astype(np.uint8) ================================================ FILE: detectron2/projects/DensePose/densepose/vis/densepose_results.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np from typing import List, Optional, Tuple import cv2 import torch from densepose.structures import DensePoseDataRelative from ..structures import DensePoseChartResult from .base import Boxes, Image, MatrixVisualizer class DensePoseResultsVisualizer(object): def visualize( self, image_bgr: Image, results_and_boxes_xywh: Tuple[Optional[List[DensePoseChartResult]], Optional[Boxes]], ) -> Image: densepose_result, boxes_xywh = results_and_boxes_xywh if densepose_result is None or boxes_xywh is None: return image_bgr boxes_xywh = boxes_xywh.cpu().numpy() context = self.create_visualization_context(image_bgr) for i, result in enumerate(densepose_result): iuv_array = torch.cat( (result.labels[None].type(torch.float32), result.uv * 255.0) ).type(torch.uint8) self.visualize_iuv_arr(context, iuv_array.cpu().numpy(), boxes_xywh[i]) image_bgr = self.context_to_image_bgr(context) return image_bgr def create_visualization_context(self, image_bgr: Image): return image_bgr def visualize_iuv_arr(self, context, iuv_arr: np.ndarray, bbox_xywh) -> None: pass def context_to_image_bgr(self, context): return context def get_image_bgr_from_context(self, context): return context class DensePoseMaskedColormapResultsVisualizer(DensePoseResultsVisualizer): def __init__( self, data_extractor, segm_extractor, inplace=True, cmap=cv2.COLORMAP_PARULA, alpha=0.7, val_scale=1.0, **kwargs, ): self.mask_visualizer = MatrixVisualizer( inplace=inplace, cmap=cmap, val_scale=val_scale, alpha=alpha ) self.data_extractor = data_extractor self.segm_extractor = segm_extractor def context_to_image_bgr(self, context): return context def visualize_iuv_arr(self, context, iuv_arr: np.ndarray, bbox_xywh) -> None: image_bgr = self.get_image_bgr_from_context(context) matrix = self.data_extractor(iuv_arr) segm = self.segm_extractor(iuv_arr) mask = np.zeros(matrix.shape, dtype=np.uint8) mask[segm > 0] = 1 image_bgr = self.mask_visualizer.visualize(image_bgr, mask, matrix, bbox_xywh) def _extract_i_from_iuvarr(iuv_arr): return iuv_arr[0, :, :] def _extract_u_from_iuvarr(iuv_arr): return iuv_arr[1, :, :] def _extract_v_from_iuvarr(iuv_arr): return iuv_arr[2, :, :] class DensePoseResultsMplContourVisualizer(DensePoseResultsVisualizer): def __init__(self, levels=10, **kwargs): self.levels = levels self.plot_args = kwargs def create_visualization_context(self, image_bgr: Image): import matplotlib.pyplot as plt from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas context = {} context["image_bgr"] = image_bgr dpi = 100 height_inches = float(image_bgr.shape[0]) / dpi width_inches = float(image_bgr.shape[1]) / dpi fig = plt.figure(figsize=(width_inches, height_inches), dpi=dpi) plt.axes([0, 0, 1, 1]) plt.axis("off") context["fig"] = fig canvas = FigureCanvas(fig) context["canvas"] = canvas extent = (0, image_bgr.shape[1], image_bgr.shape[0], 0) plt.imshow(image_bgr[:, :, ::-1], extent=extent) return context def context_to_image_bgr(self, context): fig = context["fig"] w, h = map(int, fig.get_size_inches() * fig.get_dpi()) canvas = context["canvas"] canvas.draw() image_1d = np.fromstring(canvas.tostring_rgb(), dtype="uint8") image_rgb = image_1d.reshape(h, w, 3) image_bgr = image_rgb[:, :, ::-1].copy() return image_bgr def visualize_iuv_arr(self, context, iuv_arr: np.ndarray, bbox_xywh: Boxes) -> None: import matplotlib.pyplot as plt u = _extract_u_from_iuvarr(iuv_arr).astype(float) / 255.0 v = _extract_v_from_iuvarr(iuv_arr).astype(float) / 255.0 extent = ( bbox_xywh[0], bbox_xywh[0] + bbox_xywh[2], bbox_xywh[1], bbox_xywh[1] + bbox_xywh[3], ) plt.contour(u, self.levels, extent=extent, **self.plot_args) plt.contour(v, self.levels, extent=extent, **self.plot_args) class DensePoseResultsCustomContourVisualizer(DensePoseResultsVisualizer): """ Contour visualization using marching squares """ def __init__(self, levels=10, **kwargs): # TODO: colormap is hardcoded cmap = cv2.COLORMAP_PARULA if isinstance(levels, int): self.levels = np.linspace(0, 1, levels) else: self.levels = levels if "linewidths" in kwargs: self.linewidths = kwargs["linewidths"] else: self.linewidths = [1] * len(self.levels) self.plot_args = kwargs img_colors_bgr = cv2.applyColorMap((self.levels * 255).astype(np.uint8), cmap) self.level_colors_bgr = [ [int(v) for v in img_color_bgr.ravel()] for img_color_bgr in img_colors_bgr ] def visualize_iuv_arr(self, context, iuv_arr: np.ndarray, bbox_xywh: Boxes) -> None: image_bgr = self.get_image_bgr_from_context(context) segm = _extract_i_from_iuvarr(iuv_arr) u = _extract_u_from_iuvarr(iuv_arr).astype(float) / 255.0 v = _extract_v_from_iuvarr(iuv_arr).astype(float) / 255.0 self._contours(image_bgr, u, segm, bbox_xywh) self._contours(image_bgr, v, segm, bbox_xywh) def _contours(self, image_bgr, arr, segm, bbox_xywh): for part_idx in range(1, DensePoseDataRelative.N_PART_LABELS + 1): mask = segm == part_idx if not np.any(mask): continue arr_min = np.amin(arr[mask]) arr_max = np.amax(arr[mask]) I, J = np.nonzero(mask) i0 = np.amin(I) i1 = np.amax(I) + 1 j0 = np.amin(J) j1 = np.amax(J) + 1 if (j1 == j0 + 1) or (i1 == i0 + 1): continue Nw = arr.shape[1] - 1 Nh = arr.shape[0] - 1 for level_idx, level in enumerate(self.levels): if (level < arr_min) or (level > arr_max): continue vp = arr[i0:i1, j0:j1] >= level bin_codes = vp[:-1, :-1] + vp[1:, :-1] * 2 + vp[1:, 1:] * 4 + vp[:-1, 1:] * 8 mp = mask[i0:i1, j0:j1] bin_mask_codes = mp[:-1, :-1] + mp[1:, :-1] * 2 + mp[1:, 1:] * 4 + mp[:-1, 1:] * 8 it = np.nditer(bin_codes, flags=["multi_index"]) color_bgr = self.level_colors_bgr[level_idx] linewidth = self.linewidths[level_idx] while not it.finished: if (it[0] != 0) and (it[0] != 15): i, j = it.multi_index if bin_mask_codes[i, j] != 0: self._draw_line( image_bgr, arr, mask, level, color_bgr, linewidth, it[0], it.multi_index, bbox_xywh, Nw, Nh, (i0, j0), ) it.iternext() def _draw_line( self, image_bgr, arr, mask, v, color_bgr, linewidth, bin_code, multi_idx, bbox_xywh, Nw, Nh, offset, ): lines = self._bin_code_2_lines(arr, v, bin_code, multi_idx, Nw, Nh, offset) x0, y0, w, h = bbox_xywh x1 = x0 + w y1 = y0 + h for line in lines: x0r, y0r = line[0] x1r, y1r = line[1] pt0 = (int(x0 + x0r * (x1 - x0)), int(y0 + y0r * (y1 - y0))) pt1 = (int(x0 + x1r * (x1 - x0)), int(y0 + y1r * (y1 - y0))) cv2.line(image_bgr, pt0, pt1, color_bgr, linewidth) def _bin_code_2_lines(self, arr, v, bin_code, multi_idx, Nw, Nh, offset): i0, j0 = offset i, j = multi_idx i += i0 j += j0 v0, v1, v2, v3 = arr[i, j], arr[i + 1, j], arr[i + 1, j + 1], arr[i, j + 1] x0i = float(j) / Nw y0j = float(i) / Nh He = 1.0 / Nh We = 1.0 / Nw if (bin_code == 1) or (bin_code == 14): a = (v - v0) / (v1 - v0) b = (v - v0) / (v3 - v0) pt1 = (x0i, y0j + a * He) pt2 = (x0i + b * We, y0j) return [(pt1, pt2)] elif (bin_code == 2) or (bin_code == 13): a = (v - v0) / (v1 - v0) b = (v - v1) / (v2 - v1) pt1 = (x0i, y0j + a * He) pt2 = (x0i + b * We, y0j + He) return [(pt1, pt2)] elif (bin_code == 3) or (bin_code == 12): a = (v - v0) / (v3 - v0) b = (v - v1) / (v2 - v1) pt1 = (x0i + a * We, y0j) pt2 = (x0i + b * We, y0j + He) return [(pt1, pt2)] elif (bin_code == 4) or (bin_code == 11): a = (v - v1) / (v2 - v1) b = (v - v3) / (v2 - v3) pt1 = (x0i + a * We, y0j + He) pt2 = (x0i + We, y0j + b * He) return [(pt1, pt2)] elif (bin_code == 6) or (bin_code == 9): a = (v - v0) / (v1 - v0) b = (v - v3) / (v2 - v3) pt1 = (x0i, y0j + a * He) pt2 = (x0i + We, y0j + b * He) return [(pt1, pt2)] elif (bin_code == 7) or (bin_code == 8): a = (v - v0) / (v3 - v0) b = (v - v3) / (v2 - v3) pt1 = (x0i + a * We, y0j) pt2 = (x0i + We, y0j + b * He) return [(pt1, pt2)] elif bin_code == 5: a1 = (v - v0) / (v1 - v0) b1 = (v - v1) / (v2 - v1) pt11 = (x0i, y0j + a1 * He) pt12 = (x0i + b1 * We, y0j + He) a2 = (v - v0) / (v3 - v0) b2 = (v - v3) / (v2 - v3) pt21 = (x0i + a2 * We, y0j) pt22 = (x0i + We, y0j + b2 * He) return [(pt11, pt12), (pt21, pt22)] elif bin_code == 10: a1 = (v - v0) / (v3 - v0) b1 = (v - v0) / (v1 - v0) pt11 = (x0i + a1 * We, y0j) pt12 = (x0i, y0j + b1 * He) a2 = (v - v1) / (v2 - v1) b2 = (v - v3) / (v2 - v3) pt21 = (x0i + a2 * We, y0j + He) pt22 = (x0i + We, y0j + b2 * He) return [(pt11, pt12), (pt21, pt22)] return [] try: import matplotlib matplotlib.use("Agg") DensePoseResultsContourVisualizer = DensePoseResultsMplContourVisualizer except ModuleNotFoundError: logger = logging.getLogger(__name__) logger.warning("Could not import matplotlib, using custom contour visualizer") DensePoseResultsContourVisualizer = DensePoseResultsCustomContourVisualizer class DensePoseResultsFineSegmentationVisualizer(DensePoseMaskedColormapResultsVisualizer): def __init__(self, inplace=True, cmap=cv2.COLORMAP_PARULA, alpha=0.7, **kwargs): super(DensePoseResultsFineSegmentationVisualizer, self).__init__( _extract_i_from_iuvarr, _extract_i_from_iuvarr, inplace, cmap, alpha, val_scale=255.0 / DensePoseDataRelative.N_PART_LABELS, **kwargs, ) class DensePoseResultsUVisualizer(DensePoseMaskedColormapResultsVisualizer): def __init__(self, inplace=True, cmap=cv2.COLORMAP_PARULA, alpha=0.7, **kwargs): super(DensePoseResultsUVisualizer, self).__init__( _extract_u_from_iuvarr, _extract_i_from_iuvarr, inplace, cmap, alpha, val_scale=1.0, **kwargs, ) class DensePoseResultsVVisualizer(DensePoseMaskedColormapResultsVisualizer): def __init__(self, inplace=True, cmap=cv2.COLORMAP_PARULA, alpha=0.7, **kwargs): super(DensePoseResultsVVisualizer, self).__init__( _extract_v_from_iuvarr, _extract_i_from_iuvarr, inplace, cmap, alpha, val_scale=1.0, **kwargs, ) ================================================ FILE: detectron2/projects/DensePose/densepose/vis/densepose_results_textures.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from typing import List, Optional, Tuple import torch from detectron2.data.detection_utils import read_image from ..structures import DensePoseChartResult from .base import Boxes, Image from .densepose_results import DensePoseResultsVisualizer def get_texture_atlas(path: Optional[str]) -> Optional[np.ndarray]: if path is None: return None # Reading images like that downsamples 16-bit images to 8-bit # If 16-bit images are needed, we can replace that by cv2.imread with the # cv2.IMREAD_UNCHANGED flag (with cv2 we also need it to keep alpha channels) # The rest of the pipeline would need to be adapted to 16-bit images too bgr_image = read_image(path) rgb_image = np.copy(bgr_image) # Convert BGR -> RGB rgb_image[:, :, :3] = rgb_image[:, :, 2::-1] # Works with alpha channel return rgb_image class DensePoseResultsVisualizerWithTexture(DensePoseResultsVisualizer): """ texture_atlas: An image, size 6N * 4N, with N * N squares for each of the 24 body parts. It must follow the grid found at https://github.com/facebookresearch/DensePose/blob/master/DensePoseData/demo_data/texture_atlas_200.png # noqa For each body part, U is proportional to the x coordinate, and (1 - V) to y """ def __init__(self, texture_atlas, **kwargs): self.texture_atlas = texture_atlas self.body_part_size = texture_atlas.shape[0] // 6 assert self.body_part_size == texture_atlas.shape[1] // 4 def visualize( self, image_bgr: Image, results_and_boxes_xywh: Tuple[Optional[List[DensePoseChartResult]], Optional[Boxes]], ) -> Image: densepose_result, boxes_xywh = results_and_boxes_xywh if densepose_result is None or boxes_xywh is None: return image_bgr boxes_xywh = boxes_xywh.int().cpu().numpy() texture_image, alpha = self.get_texture() for i, result in enumerate(densepose_result): iuv_array = torch.cat((result.labels[None], result.uv.clamp(0, 1))) x, y, w, h = boxes_xywh[i] bbox_image = image_bgr[y : y + h, x : x + w] image_bgr[y : y + h, x : x + w] = self.generate_image_with_texture( texture_image, alpha, bbox_image, iuv_array.cpu().numpy() ) return image_bgr def get_texture(self): N = self.body_part_size texture_image = np.zeros([24, N, N, self.texture_atlas.shape[-1]]) for i in range(4): for j in range(6): texture_image[(6 * i + j), :, :, :] = self.texture_atlas[ N * j : N * (j + 1), N * i : N * (i + 1), : ] if texture_image.shape[-1] == 4: # Image with alpha channel alpha = texture_image[:, :, :, -1] / 255.0 texture_image = texture_image[:, :, :, :3] else: alpha = texture_image.sum(axis=-1) > 0 return texture_image, alpha def generate_image_with_texture(self, texture_image, alpha, bbox_image_bgr, iuv_array): I, U, V = iuv_array generated_image_bgr = bbox_image_bgr.copy() for PartInd in range(1, 25): x, y = np.where(I == PartInd) x_index = (U[x, y] * (self.body_part_size - 1)).astype(int) y_index = ((1 - V[x, y]) * (self.body_part_size - 1)).astype(int) part_alpha = np.expand_dims(alpha[PartInd - 1, y_index, x_index], -1) generated_image_bgr[I == PartInd] = ( generated_image_bgr[I == PartInd] * (1 - part_alpha) + texture_image[PartInd - 1, y_index, x_index] * part_alpha ) return generated_image_bgr.astype(np.uint8) ================================================ FILE: detectron2/projects/DensePose/densepose/vis/extractor.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging from typing import List, Optional, Sequence, Tuple import torch from detectron2.layers.nms import batched_nms from detectron2.structures.instances import Instances from densepose.converters import ToChartResultConverterWithConfidences from densepose.structures import ( DensePoseChartResultWithConfidences, DensePoseEmbeddingPredictorOutput, ) from densepose.vis.bounding_box import BoundingBoxVisualizer, ScoredBoundingBoxVisualizer from densepose.vis.densepose_outputs_vertex import DensePoseOutputsVertexVisualizer from densepose.vis.densepose_results import DensePoseResultsVisualizer from .base import CompoundVisualizer Scores = Sequence[float] DensePoseChartResultsWithConfidences = List[DensePoseChartResultWithConfidences] def extract_scores_from_instances(instances: Instances, select=None): if instances.has("scores"): return instances.scores if select is None else instances.scores[select] return None def extract_boxes_xywh_from_instances(instances: Instances, select=None): if instances.has("pred_boxes"): boxes_xywh = instances.pred_boxes.tensor.clone() boxes_xywh[:, 2] -= boxes_xywh[:, 0] boxes_xywh[:, 3] -= boxes_xywh[:, 1] return boxes_xywh if select is None else boxes_xywh[select] return None def create_extractor(visualizer: object): """ Create an extractor for the provided visualizer """ if isinstance(visualizer, CompoundVisualizer): extractors = [create_extractor(v) for v in visualizer.visualizers] return CompoundExtractor(extractors) elif isinstance(visualizer, DensePoseResultsVisualizer): return DensePoseResultExtractor() elif isinstance(visualizer, ScoredBoundingBoxVisualizer): return CompoundExtractor([extract_boxes_xywh_from_instances, extract_scores_from_instances]) elif isinstance(visualizer, BoundingBoxVisualizer): return extract_boxes_xywh_from_instances elif isinstance(visualizer, DensePoseOutputsVertexVisualizer): return DensePoseOutputsExtractor() else: logger = logging.getLogger(__name__) logger.error(f"Could not create extractor for {visualizer}") return None class BoundingBoxExtractor(object): """ Extracts bounding boxes from instances """ def __call__(self, instances: Instances): boxes_xywh = extract_boxes_xywh_from_instances(instances) return boxes_xywh class ScoredBoundingBoxExtractor(object): """ Extracts bounding boxes from instances """ def __call__(self, instances: Instances, select=None): scores = extract_scores_from_instances(instances) boxes_xywh = extract_boxes_xywh_from_instances(instances) if (scores is None) or (boxes_xywh is None): return (boxes_xywh, scores) if select is not None: scores = scores[select] boxes_xywh = boxes_xywh[select] return (boxes_xywh, scores) class DensePoseResultExtractor(object): """ Extracts DensePose chart result with confidences from instances """ def __call__( self, instances: Instances, select=None ) -> Tuple[Optional[DensePoseChartResultsWithConfidences], Optional[torch.Tensor]]: if instances.has("pred_densepose") and instances.has("pred_boxes"): dpout = instances.pred_densepose boxes_xyxy = instances.pred_boxes boxes_xywh = extract_boxes_xywh_from_instances(instances) if select is not None: dpout = dpout[select] boxes_xyxy = boxes_xyxy[select] converter = ToChartResultConverterWithConfidences() results = [converter.convert(dpout[i], boxes_xyxy[[i]]) for i in range(len(dpout))] return results, boxes_xywh else: return None, None class DensePoseOutputsExtractor(object): """ Extracts DensePose result from instances """ def __call__( self, instances: Instances, select=None, ) -> Tuple[ Optional[DensePoseEmbeddingPredictorOutput], Optional[torch.Tensor], Optional[List[int]] ]: if not (instances.has("pred_densepose") and instances.has("pred_boxes")): return None, None, None dpout = instances.pred_densepose boxes_xyxy = instances.pred_boxes boxes_xywh = extract_boxes_xywh_from_instances(instances) if instances.has("pred_classes"): classes = instances.pred_classes.tolist() else: classes = None if select is not None: dpout = dpout[select] boxes_xyxy = boxes_xyxy[select] if classes is not None: classes = classes[select] return dpout, boxes_xywh, classes class CompoundExtractor(object): """ Extracts data for CompoundVisualizer """ def __init__(self, extractors): self.extractors = extractors def __call__(self, instances: Instances, select=None): datas = [] for extractor in self.extractors: data = extractor(instances, select) datas.append(data) return datas class NmsFilteredExtractor(object): """ Extracts data in the format accepted by NmsFilteredVisualizer """ def __init__(self, extractor, iou_threshold): self.extractor = extractor self.iou_threshold = iou_threshold def __call__(self, instances: Instances, select=None): scores = extract_scores_from_instances(instances) boxes_xywh = extract_boxes_xywh_from_instances(instances) if boxes_xywh is None: return None select_local_idx = batched_nms( boxes_xywh, scores, torch.zeros(len(scores), dtype=torch.int32), iou_threshold=self.iou_threshold, ).squeeze() select_local = torch.zeros(len(boxes_xywh), dtype=torch.bool, device=boxes_xywh.device) select_local[select_local_idx] = True select = select_local if select is None else (select & select_local) return self.extractor(instances, select=select) class ScoreThresholdedExtractor(object): """ Extracts data in the format accepted by ScoreThresholdedVisualizer """ def __init__(self, extractor, min_score): self.extractor = extractor self.min_score = min_score def __call__(self, instances: Instances, select=None): scores = extract_scores_from_instances(instances) if scores is None: return None select_local = scores > self.min_score select = select_local if select is None else (select & select_local) data = self.extractor(instances, select=select) return data ================================================ FILE: detectron2/projects/DensePose/dev/README.md ================================================ ## Some scripts for developers to use, include: - `run_instant_tests.sh`: run training for a few iterations. - `run_inference_tests.sh`: run inference on a small dataset. - `../../dev/linter.sh`: lint the codebase before commit - `../../dev/parse_results.sh`: parse results from log file. ================================================ FILE: detectron2/projects/DensePose/dev/run_inference_tests.sh ================================================ #!/bin/bash -e # Copyright (c) Facebook, Inc. and its affiliates. BIN="python train_net.py" OUTPUT="inference_test_output" NUM_GPUS=2 IMS_PER_GPU=2 IMS_PER_BATCH=$(( NUM_GPUS * IMS_PER_GPU )) CFG_LIST=( "${@:1}" ) if [ ${#CFG_LIST[@]} -eq 0 ]; then CFG_LIST=( ./configs/quick_schedules/*inference_acc_test.yaml ) fi echo "========================================================================" echo "Configs to run:" echo "${CFG_LIST[@]}" echo "========================================================================" for cfg in "${CFG_LIST[@]}"; do echo "========================================================================" echo "Running $cfg ..." echo "========================================================================" $BIN \ --eval-only \ --num-gpus $NUM_GPUS \ --config-file "$cfg" \ OUTPUT_DIR "$OUTPUT" \ SOLVER.IMS_PER_BATCH $IMS_PER_BATCH rm -rf $OUTPUT done ================================================ FILE: detectron2/projects/DensePose/dev/run_instant_tests.sh ================================================ #!/bin/bash -e # Copyright (c) Facebook, Inc. and its affiliates. BIN="python train_net.py" OUTPUT="instant_test_output" NUM_GPUS=2 SOLVER_IMS_PER_BATCH=$((NUM_GPUS * 2)) CFG_LIST=( "${@:1}" ) if [ ${#CFG_LIST[@]} -eq 0 ]; then CFG_LIST=( ./configs/quick_schedules/*instant_test.yaml ) fi echo "========================================================================" echo "Configs to run:" echo "${CFG_LIST[@]}" echo "========================================================================" for cfg in "${CFG_LIST[@]}"; do echo "========================================================================" echo "Running $cfg ..." echo "========================================================================" $BIN --num-gpus $NUM_GPUS --config-file "$cfg" \ SOLVER.IMS_PER_BATCH $SOLVER_IMS_PER_BATCH \ OUTPUT_DIR "$OUTPUT" rm -rf "$OUTPUT" done ================================================ FILE: detectron2/projects/DensePose/doc/BOOTSTRAPPING_PIPELINE.md ================================================ # Bootstrapping Pipeline Bootstrapping pipeline for DensePose was proposed in [Sanakoyeu et al., 2020](https://arxiv.org/pdf/2003.00080.pdf) to extend DensePose from humans to proximal animal classes (chimpanzees). Currently, the pipeline is only implemented for [chart-based models](DENSEPOSE_IUV.md). Bootstrapping proceeds in two steps. ## Master Model Training Master model is trained on data from source domain (humans) and supporting domain (animals). Instances from the source domain contain full DensePose annotations (`S`, `I`, `U` and `V`) and instances from the supporting domain have segmentation annotations only. To ensure segmentation quality in the target domain, only a subset of supporting domain classes is included into the training. This is achieved through category filters, e.g. (see [configs/evolution/Base-RCNN-FPN-Atop10P_CA.yaml](../configs/evolution/Base-RCNN-FPN-Atop10P_CA.yaml)): ``` WHITELISTED_CATEGORIES: "base_coco_2017_train": - 1 # person - 16 # bird - 17 # cat - 18 # dog - 19 # horse - 20 # sheep - 21 # cow - 22 # elephant - 23 # bear - 24 # zebra - 25 # girafe ``` The acronym `Atop10P` in config file names indicates that categories are filtered to only contain top 10 animals and person. The training is performed in a *class-agnostic* manner: all instances are mapped into the same class (person), e.g. (see [configs/evolution/Base-RCNN-FPN-Atop10P_CA.yaml](../configs/evolution/Base-RCNN-FPN-Atop10P_CA.yaml)): ``` CATEGORY_MAPS: "base_coco_2017_train": "16": 1 # bird -> person "17": 1 # cat -> person "18": 1 # dog -> person "19": 1 # horse -> person "20": 1 # sheep -> person "21": 1 # cow -> person "22": 1 # elephant -> person "23": 1 # bear -> person "24": 1 # zebra -> person "25": 1 # girafe -> person ``` The acronym `CA` in config file names indicates that the training is class-agnostic. ## Student Model Training Student model is trained on data from source domain (humans), supporting domain (animals) and target domain (chimpanzees). Annotations in source and supporting domains are similar to the ones used for the master model training. Annotations in target domain are obtained by applying the master model to images that contain instances from the target category and sampling sparse annotations from dense results. This process is called *bootstrapping*. Below we give details on how the bootstrapping pipeline is implemented. ### Data Loaders The central components that enable bootstrapping are [`InferenceBasedLoader`](../densepose/data/inference_based_loader.py) and [`CombinedDataLoader`](../densepose/data/combined_loader.py). `InferenceBasedLoader` takes images from a data loader, applies a model to the images, filters the model outputs based on the selected criteria and samples the filtered outputs to produce annotations. `CombinedDataLoader` combines data obtained from the loaders based on specified ratios. The standard data loader has the default ratio of 1.0, ratios for bootstrap datasets are specified in the configuration file. The higher the ratio the higher the probability to include samples from the particular data loader into a batch. Here is an example of the bootstrapping configuration taken from [`configs/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_uniform.yaml`](../configs/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_uniform.yaml): ``` BOOTSTRAP_DATASETS: - DATASET: "chimpnsee" RATIO: 1.0 IMAGE_LOADER: TYPE: "video_keyframe" SELECT: STRATEGY: "random_k" NUM_IMAGES: 4 TRANSFORM: TYPE: "resize" MIN_SIZE: 800 MAX_SIZE: 1333 BATCH_SIZE: 8 NUM_WORKERS: 1 INFERENCE: INPUT_BATCH_SIZE: 1 OUTPUT_BATCH_SIZE: 1 DATA_SAMPLER: # supported types: # densepose_uniform # densepose_UV_confidence # densepose_fine_segm_confidence # densepose_coarse_segm_confidence TYPE: "densepose_uniform" COUNT_PER_CLASS: 8 FILTER: TYPE: "detection_score" MIN_VALUE: 0.8 BOOTSTRAP_MODEL: WEIGHTS: https://dl.fbaipublicfiles.com/densepose/evolution/densepose_R_50_FPN_DL_WC1M_3x_Atop10P_CA/217578784/model_final_9fe1cc.pkl ``` The above example has one bootstrap dataset (`chimpnsee`). This dataset is registered as a [VIDEO_LIST](../densepose/data/datasets/chimpnsee.py) dataset, which means that it consists of a number of videos specified in a text file. For videos there can be different strategies to sample individual images. Here we use `video_keyframe` strategy which considers only keyframes; this ensures temporal offset between sampled images and faster seek operations. We select at most 4 random keyframes in each video: ``` SELECT: STRATEGY: "random_k" NUM_IMAGES: 4 ``` The frames are then resized ``` TRANSFORM: TYPE: "resize" MIN_SIZE: 800 MAX_SIZE: 1333 ``` and batched using the standard [PyTorch DataLoader](https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader): ``` BATCH_SIZE: 8 NUM_WORKERS: 1 ``` `InferenceBasedLoader` decomposes those batches into batches of size `INPUT_BATCH_SIZE` and applies the master model specified by `BOOTSTRAP_MODEL`. Models outputs are filtered by detection score: ``` FILTER: TYPE: "detection_score" MIN_VALUE: 0.8 ``` and sampled using the specified sampling strategy: ``` DATA_SAMPLER: # supported types: # densepose_uniform # densepose_UV_confidence # densepose_fine_segm_confidence # densepose_coarse_segm_confidence TYPE: "densepose_uniform" COUNT_PER_CLASS: 8 ``` The current implementation supports [uniform sampling](../densepose/data/samplers/densepose_uniform.py) and [confidence-based sampling](../densepose/data/samplers/densepose_confidence_based.py) to obtain sparse annotations from dense results. For confidence-based sampling one needs to use the master model which produces confidence estimates. The `WC1M` master model used in the example above produces all three types of confidence estimates. Finally, sampled data is grouped into batches of size `OUTPUT_BATCH_SIZE`: ``` INFERENCE: INPUT_BATCH_SIZE: 1 OUTPUT_BATCH_SIZE: 1 ``` The proportion of data from annotated datasets and bootstrapped dataset can be tracked in the logs, e.g.: ``` [... densepose.engine.trainer]: batch/ 1.8, batch/base_coco_2017_train 6.4, batch/densepose_coco_2014_train 3.85 ``` which means that over the last 20 iterations, on average for 1.8 bootstrapped data samples there were 6.4 samples from `base_coco_2017_train` and 3.85 samples from `densepose_coco_2014_train`. ================================================ FILE: detectron2/projects/DensePose/doc/DENSEPOSE_CSE.md ================================================ # Continuous Surface Embeddings for Dense Pose Estimation for Humans and Animals ## Overview
The pipeline uses [Faster R-CNN](https://arxiv.org/abs/1506.01497) with [Feature Pyramid Network](https://arxiv.org/abs/1612.03144) meta architecture outlined in Figure 1. For each detected object, the model predicts its coarse segmentation `S` (2 channels: foreground / background) and the embedding `E` (16 channels). At the same time, the embedder produces vertex embeddings `Ê` for the corresponding mesh. Universal positional embeddings `E` and vertex embeddings `Ê` are matched to derive for each pixel its continuous surface embedding.

Figure 1. DensePose continuous surface embeddings architecture based on Faster R-CNN with Feature Pyramid Network (FPN).

### Datasets For more details on datasets used for training and validation of continuous surface embeddings models, please refer to the [DensePose Datasets](DENSEPOSE_DATASETS.md) page. ## Model Zoo and Baselines ### Human CSE Models Continuous surface embeddings models for humans trained using the protocols from [Neverova et al, 2020](https://arxiv.org/abs/2011.12438). Models trained with hard assignment loss ℒ:
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
segm
AP
dp. AP
GPS
dp. AP
GPSm
model id download
R_50_FPN_s1x s1x 0.349 0.060 6.3 61.1 67.1 64.4 65.7 251155172 model | metrics
R_101_FPN_s1x s1x 0.461 0.071 7.4 62.3 67.2 64.7 65.8 251155500 model | metrics
R_50_FPN_DL_s1x s1x 0.399 0.061 7.0 60.8 67.8 65.5 66.4 251156349 model | metrics
R_101_FPN_DL_s1x s1x 0.504 0.074 8.3 61.5 68.0 65.6 66.6 251156606 model | metrics
Models trained with soft assignment loss ℒσ:
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
segm
AP
dp. AP
GPS
dp. AP
GPSm
model id download
R_50_FPN_soft_s1x s1x 0.357 0.057 9.7 61.3 66.9 64.3 65.4 250533982 model | metrics
R_101_FPN_soft_s1x s1x 0.464 0.071 10.5 62.1 67.3 64.5 66.0 250712522 model | metrics
R_50_FPN_DL_soft_s1x s1x 0.427 0.062 11.3 60.8 68.0 66.1 66.7 250713703 model | metrics
R_101_FPN_DL_soft_s1x s1x 0.483 0.071 12.2 61.5 68.2 66.2 67.1 250713061 model | metrics
### Animal CSE Models Models obtained by finetuning human CSE models on animals data from `ds1_train` (see the [DensePose LVIS](DENSEPOSE_DATASETS.md#continuous-surface-embeddings-annotations-3) section for more details on the datasets) with soft assignment loss ℒσ:
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
segm
AP
dp. AP
GPS
dp. AP
GPSm
model id download
R_50_FPN_soft_chimps_finetune_4k 4K 0.569 0.051 4.7 62.0 59.0 32.2 39.6 253146869 model | metrics
R_50_FPN_soft_animals_finetune_4k 4K 0.381 0.061 7.3 44.9 55.5 21.3 28.8 253145793 model | metrics
R_50_FPN_soft_animals_CA_finetune_4k 4K 0.412 0.059 7.1 53.4 59.5 25.4 33.4 253498611 model | metrics
Acronyms: `CA`: class agnostic training, where all annotated instances are mapped into a single category Models obtained by finetuning human CSE models on animals data from `ds2_train` dataset with soft assignment loss ℒσ and, for some schedules, cycle losses. Please refer to [DensePose LVIS](DENSEPOSE_DATASETS.md#continuous-surface-embeddings-annotations-3) section for details on the dataset and to [Neverova et al, 2021]() for details on cycle losses.
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
segm
AP
dp. AP
GPS
dp. AP
GPSm
GErr GPS model id download
R_50_FPN_soft_animals_I0_finetune_16k 16k 0.386 0.058 8.4 54.2 67.0 29.0 38.6 13.2 85.4 270727112 model | metrics
R_50_FPN_soft_animals_I0_finetune_m2m_16k 16k 0.508 0.056 12.2 54.1 67.3 28.6 38.4 12.5 87.6 270982215 model | metrics
R_50_FPN_soft_animals_I0_finetune_i2m_16k 16k 0.483 0.056 9.7 54.0 66.6 28.9 38.3 11.0 88.9 270727461 model | metrics
## References If you use DensePose methods based on continuous surface embeddings, please take the references from the following BibTeX entries: Continuous surface embeddings: ``` @InProceedings{Neverova2020ContinuousSurfaceEmbeddings, title = {Continuous Surface Embeddings}, author = {Neverova, Natalia and Novotny, David and Khalidov, Vasil and Szafraniec, Marc and Labatut, Patrick and Vedaldi, Andrea}, journal = {Advances in Neural Information Processing Systems}, year = {2020}, } ``` Cycle Losses: ``` @InProceedings{Neverova2021UniversalCanonicalMaps, title = {Discovering Relationships between Object Categories via Universal Canonical Maps}, author = {Neverova, Natalia and Sanakoyeu, Artsiom and Novotny, David and Labatut, Patrick and Vedaldi, Andrea}, journal = {The IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}, year = {2021}, } ``` ================================================ FILE: detectron2/projects/DensePose/doc/DENSEPOSE_DATASETS.md ================================================ # DensePose Datasets We summarize the datasets used in various DensePose training schedules and describe different available annotation types. ## Table of Contents [General Information](#general-information) [DensePose COCO](#densepose-coco) [DensePose PoseTrack](#densepose-posetrack) [DensePose Chimps](#densepose-chimps) [DensePose LVIS](#densepose-lvis) ## General Information DensePose annotations are typically stored in JSON files. Their structure follows the [COCO Data Format](https://cocodataset.org/#format-data), the basic data structure is outlined below: ``` { "info": info, "images": [image], "annotations": [annotation], "licenses": [license], } info{ "year": int, "version": str, "description": str, "contributor": str, "url": str, "date_created": datetime, } image{ "id": int, "width": int, "height": int, "file_name": str, "license": int, "flickr_url": str, "coco_url": str, "date_captured": datetime, } license{ "id": int, "name": str, "url": str, } ``` DensePose annotations can be of two types: *chart-based annotations* or *continuous surface embeddings annotations*. We give more details on each of the two annotation types below. ### Chart-based Annotations These annotations assume a single 3D model which corresponds to all the instances in a given dataset. 3D model is assumed to be split into *charts*. Each chart has its own 2D parametrization through inner coordinates `U` and `V`, typically taking values in `[0, 1]`. Chart-based annotations consist of *point-based annotations* and *segmentation annotations*. Point-based annotations specify, for a given image point, which model part it belongs to and what are its coordinates in the corresponding chart. Segmentation annotations specify regions in an image that are occupied by a given part. In some cases, charts associated with point annotations are more detailed than the ones associated with segmentation annotations. In this case we distinguish *fine segmentation* (associated with points) and *coarse segmentation* (associated with masks). **Point-based annotations**: `dp_x` and `dp_y`: image coordinates of the annotated points along the horizontal and vertical axes respectively. The coordinates are defined with respect to the top-left corner of the annotated bounding box and are normalized assuming the bounding box size to be `256x256`; `dp_I`: for each point specifies the index of the fine segmentation chart it belongs to; `dp_U` and `dp_V`: point coordinates on the corresponding chart. Each fine segmentation part has its own parametrization in terms of chart coordinates. **Segmentation annotations**: `dp_masks`: RLE encoded dense masks (`dict` containing keys `counts` and `size`). The masks are typically of size `256x256`, they define segmentation within the bounding box. ### Continuous Surface Embeddings Annotations Continuous surface embeddings annotations also consist of *point-based annotations* and *segmentation annotations*. Point-based annotations establish correspondence between image points and 3D model vertices. Segmentation annotations specify foreground regions for a given instane. **Point-based annotations**: `dp_x` and `dp_y` specify image point coordinates the same way as for chart-based annotations; `dp_vertex` gives indices of 3D model vertices, which the annotated image points correspond to; `ref_model` specifies 3D model name. **Segmentation annotations**: Segmentations can either be given by `dp_masks` field or by `segmentation` field. `dp_masks`: RLE encoded dense masks (`dict` containing keys `counts` and `size`). The masks are typically of size `256x256`, they define segmentation within the bounding box. `segmentation`: polygon-based masks stored as a 2D list `[[x1 y1 x2 y2...],[x1 y1 ...],...]` of polygon vertex coordinates in a given image. ## DensePose COCO

Figure 1. Annotation examples from the DensePose COCO dataset.

DensePose COCO dataset contains about 50K annotated persons on images from the [COCO dataset](https://cocodataset.org/#home) The images are available for download from the [COCO Dataset download page](https://cocodataset.org/#download): [train2014](http://images.cocodataset.org/zips/train2014.zip), [val2014](http://images.cocodataset.org/zips/val2014.zip). The details on available annotations and their download links are given below. ### Chart-based Annotations Chart-based DensePose COCO annotations are available for the instances of category `person` and correspond to the model shown in Figure 2. They include `dp_x`, `dp_y`, `dp_I`, `dp_U` and `dp_V` fields for annotated points (~100 points per annotated instance) and `dp_masks` field, which encodes coarse segmentation into 14 parts in the following order: `Torso`, `Right Hand`, `Left Hand`, `Left Foot`, `Right Foot`, `Upper Leg Right`, `Upper Leg Left`, `Lower Leg Right`, `Lower Leg Left`, `Upper Arm Left`, `Upper Arm Right`, `Lower Arm Left`, `Lower Arm Right`, `Head`.

Figure 2. Human body charts (fine segmentation) and the associated 14 body parts depicted with rounded rectangles (coarse segmentation).

The dataset splits used in the training schedules are `train2014`, `valminusminival2014` and `minival2014`. `train2014` and `valminusminival2014` are used for training, and `minival2014` is used for validation. The table with annotation download links, which summarizes the number of annotated instances and images for each of the dataset splits is given below:
Name # inst # images file size download
densepose_train2014 39210 26437 526M densepose_train2014.json
densepose_valminusminival2014 7297 5984 105M densepose_valminusminival2014.json
densepose_minival2014 2243 1508 31M densepose_minival2014.json
### Continuous Surface Embeddings Annotations DensePose COCO continuous surface embeddings annotations are available for the instances of category `person`. The annotations correspond to the 3D model shown in Figure 2, and include `dp_x`, `dp_y` and `dp_vertex` and `ref_model` fields. All chart-based annotations were also kept for convenience. As with chart-based annotations, the dataset splits used in the training schedules are `train2014`, `valminusminival2014` and `minival2014`. `train2014` and `valminusminival2014` are used for training, and `minival2014` is used for validation. The table with annotation download links, which summarizes the number of annotated instances and images for each of the dataset splits is given below:
Name # inst # images file size download
densepose_train2014_cse 39210 26437 554M densepose_train2014_cse.json
densepose_valminusminival2014_cse 7297 5984 110M densepose_valminusminival2014_cse.json
densepose_minival2014_cse 2243 1508 32M densepose_minival2014_cse.json
## DensePose PoseTrack

Figure 3. Annotation examples from the PoseTrack dataset.

DensePose PoseTrack dataset contains annotated image sequences. To download the images for this dataset, please follow the instructions from the [PoseTrack Download Page](https://posetrack.net/users/download.php). ### Chart-based Annotations Chart-based DensePose PoseTrack annotations are available for the instances with category `person` and correspond to the model shown in Figure 2. They include `dp_x`, `dp_y`, `dp_I`, `dp_U` and `dp_V` fields for annotated points (~100 points per annotated instance) and `dp_masks` field, which encodes coarse segmentation into the same 14 parts as in DensePose COCO. The dataset splits used in the training schedules are `posetrack_train2017` (train set) and `posetrack_val2017` (validation set). The table with annotation download links, which summarizes the number of annotated instances, instance tracks and images for the dataset splits is given below:
Name # inst # images # tracks file size download
densepose_posetrack_train2017 8274 1680 36 118M densepose_posetrack_train2017.json
densepose_posetrack_val2017 4753 782 46 59M densepose_posetrack_val2017.json
## DensePose Chimps

Figure 4. Example images from the DensePose Chimps dataset.

DensePose Chimps dataset contains annotated images of chimpanzees. To download the images for this dataset, please use the URL specified in `image_url` field in the annotations. ### Chart-based Annotations Chart-based DensePose Chimps annotations correspond to the human model shown in Figure 2, the instances are thus annotated to belong to the `person` category. They include `dp_x`, `dp_y`, `dp_I`, `dp_U` and `dp_V` fields for annotated points (~3 points per annotated instance) and `dp_masks` field, which encodes foreground mask in RLE format. Chart-base DensePose Chimps annotations are used for validation only. The table with annotation download link, which summarizes the number of annotated instances and images is given below:
Name # inst # images file size download
densepose_chimps 930 654 6M densepose_chimps_full_v2.json
### Continuous Surface Embeddings Annotations Continuous surface embeddings annotations for DensePose Chimps include `dp_x`, `dp_y` and `dp_vertex` point-based annotations (~3 points per annotated instance), `dp_masks` field with the same contents as for chart-based annotations and `ref_model` field which refers to a chimpanzee 3D model `chimp_5029`. The dataset is split into training and validation subsets. The table with annotation download links, which summarizes the number of annotated instances and images for each of the dataset splits is given below: The table below outlines the dataset splits:
Name # inst # images file size download
densepose_chimps_cse_train 500 350 3M densepose_chimps_cse_train.json
densepose_chimps_cse_val 430 304 3M densepose_chimps_cse_val.json
## DensePose LVIS

Figure 5. Example images from the DensePose LVIS dataset.

DensePose LVIS dataset contains segmentation and DensePose annotations for animals on images from the [LVIS dataset](https://www.lvisdataset.org/dataset). The images are available for download through the links: [train2017](http://images.cocodataset.org/zips/train2017.zip), [val2017](http://images.cocodataset.org/zips/val2017.zip). ### Continuous Surface Embeddings Annotations Continuous surface embeddings (CSE) annotations for DensePose LVIS include `dp_x`, `dp_y` and `dp_vertex` point-based annotations (~3 points per annotated instance) and a `ref_model` field which refers to a 3D model that corresponds to the instance. Instances from 9 animal categories were annotated with CSE DensePose data: bear, cow, cat, dog, elephant, giraffe, horse, sheep and zebra. Foreground masks are available from instance segmentation annotations (`segmentation` field) in polygon format, they are stored as a 2D list `[[x1 y1 x2 y2...],[x1 y1 ...],...]`. We used two datasets, each constising of one training (`train`) and validation (`val`) subsets: the first one (`ds1`) was used in [Neverova et al, 2020](https://arxiv.org/abs/2011.12438). The second one (`ds2`), was used in [Neverova et al, 2021](). The summary of the available datasets is given below:
All Data Selected Animals
(9 categories)
File
Name # cat # img # segm # img # segm # dp size download
ds1_train 556 4141 23985 4141 9472 5184 46M densepose_lvis_v1_ds1_train_v1.json
ds1_val 251 571 3281 571 1537 1036 5M densepose_lvis_v1_ds1_val_v1.json
ds2_train 1203 99388 1270141 13746 46964 18932 1051M densepose_lvis_v1_ds2_train_v1.json
ds2_val 9 2690 9155 2690 9155 3604 24M densepose_lvis_v1_ds2_val_v1.json
Legend: `#cat` - number of categories in the dataset for which annotations are available; `#img` - number of images with annotations in the dataset; `#segm` - number of segmentation annotations; `#dp` - number of DensePose annotations. Important Notes: 1. The reference models used for `ds1_train` and `ds1_val` are `bear_4936`, `cow_5002`, `cat_5001`, `dog_5002`, `elephant_5002`, `giraffe_5002`, `horse_5004`, `sheep_5004` and `zebra_5002`. The reference models used for `ds2_train` and `ds2_val` are `bear_4936`, `cow_5002`, `cat_7466`, `dog_7466`, `elephant_5002`, `giraffe_5002`, `horse_5004`, `sheep_5004` and `zebra_5002`. So reference models for categories `cat` aind `dog` are different for `ds1` and `ds2`. 2. Some annotations from `ds1_train` are reused in `ds2_train` (4538 DensePose annotations and 21275 segmentation annotations). The ones for cat and dog categories were remapped from `cat_5001` and `dog_5002` reference models used in `ds1` to `cat_7466` and `dog_7466` used in `ds2`. 3. All annotations from `ds1_val` are included into `ds2_val` after the remapping procedure mentioned in note 2. 4. Some annotations from `ds1_train` are part of `ds2_val` (646 DensePose annotations and 1225 segmentation annotations). Thus one should not train on `ds1_train` if evaluating on `ds2_val`. ================================================ FILE: detectron2/projects/DensePose/doc/DENSEPOSE_IUV.md ================================================ # Chart-based Dense Pose Estimation for Humans and Animals ## Overview The goal of chart-based DensePose methods is to establish dense correspondences between image pixels and 3D object mesh by splitting the latter into charts and estimating for each pixel the corresponding chart index `I` and local chart coordinates `(U, V)`.
The charts used for human DensePose estimation are shown in Figure 1. The human body is split into 24 parts, each part is parametrized by `U` and `V` coordinates, each taking values in `[0, 1]`.

Figure 1. Partitioning and parametrization of human body surface.

The pipeline uses [Faster R-CNN](https://arxiv.org/abs/1506.01497) with [Feature Pyramid Network](https://arxiv.org/abs/1612.03144) meta architecture outlined in Figure 2. For each detected object, the model predicts its coarse segmentation `S` (2 or 15 channels: foreground / background or background + 14 predefined body parts), fine segmentation `I` (25 channels: background + 24 predefined body parts) and local chart coordinates `U` and `V`.

Figure 2. DensePose chart-based architecture based on Faster R-CNN with Feature Pyramid Network (FPN).

### Bootstrapping Chart-Based Models [Sanakoyeu et al., 2020](https://arxiv.org/pdf/2003.00080.pdf) introduced a pipeline to transfer DensePose models trained on humans to proximal animal classes (chimpanzees), which is summarized in Figure 3. The training proceeds in two stages: First, a *master* model is trained on data from source domain (humans with full DensePose annotation `S`, `I`, `U` and `V`) and supporting domain (animals with segmentation annotation only). Only selected animal classes are chosen from the supporting domain through *category filters* to guarantee the quality of target domain results. The training is done in *class-agnostic manner*: all selected categories are mapped to a single category (human). Second, a *student* model is trained on data from source and supporting domains, as well as data from target domain obtained by applying the master model, selecting high-confidence detections and sampling the results.

Figure 3. Domain adaptation: master model is trained on data from source and supporting domains to produce predictions in target domain; student model combines data from source and supporting domains, as well as sampled predictions from the master model on target domain to improve target domain predictions quality.

Examples of pretrained master and student models are available in the [Model Zoo](#ModelZooBootstrap). For more details on the bootstrapping pipeline, please see [Bootstrapping Pipeline](BOOTSTRAPPING_PIPELINE.md). ### Datasets For more details on datasets used for chart-based model training and validation, please refer to the [DensePose Datasets](DENSEPOSE_DATASETS.md) page. ## Model Zoo and Baselines ### Legacy Models Baselines trained using schedules from [Güler et al, 2018](https://arxiv.org/pdf/1802.00434.pdf)
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
segm
AP
dp. AP
GPS
dp. AP
GPSm
model id download
R_50_FPN_s1x_legacy s1x 0.307 0.051 3.2 58.1 58.2 52.1 54.9 164832157 model | metrics
R_101_FPN_s1x_legacy s1x 0.390 0.063 4.3 59.5 59.3 53.2 56.0 164832182 model | metrics
### Improved Baselines, Original Fully Convolutional Head These models use an improved training schedule and Panoptic FPN head from [Kirillov et al, 2019](https://arxiv.org/abs/1901.02446).
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
segm
AP
dp. AP
GPS
dp. AP
GPSm
model id download
R_50_FPN_s1x s1x 0.359 0.066 4.5 61.2 67.2 63.7 65.3 165712039 model | metrics
R_101_FPN_s1x s1x 0.428 0.079 5.8 62.3 67.8 64.5 66.2 165712084 model | metrics
### Improved Baselines, DeepLabV3 Head These models use an improved training schedule, Panoptic FPN head from [Kirillov et al, 2019](https://arxiv.org/abs/1901.02446) and DeepLabV3 head from [Chen et al, 2017](https://arxiv.org/abs/1706.05587).
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
segm
AP
dp. AP
GPS
dp. AP
GPSm
model id download
R_50_FPN_DL_s1x s1x 0.392 0.070 6.7 61.1 68.3 65.6 66.7 165712097 model | metrics
R_101_FPN_DL_s1x s1x 0.478 0.083 7.0 62.3 68.7 66.3 67.6 165712116 model | metrics
###
Baselines with Confidence Estimation These models perform additional estimation of confidence in regressed UV coodrinates, along the lines of [Neverova et al., 2019](https://papers.nips.cc/paper/8378-correlated-uncertainty-for-learning-dense-correspondences-from-noisy-labels).
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
segm
AP
dp. AP
GPS
dp. AP
GPSm
model id download
R_50_FPN_WC1_s1x s1x 0.353 0.064 4.6 60.5 67.0 64.2 65.4 173862049 model | metrics
R_50_FPN_WC2_s1x s1x 0.364 0.066 4.8 60.7 66.9 64.2 65.7 173861455 model | metrics
R_50_FPN_DL_WC1_s1x s1x 0.397 0.068 6.7 61.1 68.1 65.8 67.0 173067973 model | metrics
R_50_FPN_DL_WC2_s1x s1x 0.410 0.070 6.8 60.8 67.9 65.6 66.7 173859335 model | metrics
R_101_FPN_WC1_s1x s1x 0.435 0.076 5.7 62.5 67.6 64.9 66.3 171402969 model | metrics
R_101_FPN_WC2_s1x s1x 0.450 0.078 5.7 62.3 67.6 64.8 66.4 173860702 model | metrics
R_101_FPN_DL_WC1_s1x s1x 0.479 0.081 7.9 62.0 68.4 66.2 67.2 173858525 model | metrics
R_101_FPN_DL_WC2_s1x s1x 0.491 0.082 7.6 61.7 68.3 65.9 67.2 173294801 model | metrics
Acronyms: `WC1`: with confidence estimation model type 1 for `U` and `V` `WC2`: with confidence estimation model type 2 for `U` and `V` ###
Baselines with Mask Confidence Estimation Models that perform estimation of confidence in regressed UV coodrinates as well as confidences associated with coarse and fine segmentation, see [Sanakoyeu et al., 2020](https://arxiv.org/pdf/2003.00080.pdf) for details.
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
segm
AP
dp. AP
GPS
dp. AP
GPSm
model id download
R_50_FPN_WC1M_s1x s1x 0.381 0.066 4.8 60.6 66.7 64.0 65.4 217144516 model | metrics
R_50_FPN_WC2M_s1x s1x 0.342 0.068 5.0 60.7 66.9 64.2 65.5 216245640 model | metrics
R_50_FPN_DL_WC1M_s1x s1x 0.371 0.068 6.0 60.7 68.0 65.2 66.7 216245703 model | metrics
R_50_FPN_DL_WC2M_s1x s1x 0.385 0.071 6.1 60.8 68.1 65.0 66.4 216245758 model | metrics
R_101_FPN_WC1M_s1x s1x 0.423 0.079 5.9 62.0 67.3 64.8 66.0 216453687 model | metrics
R_101_FPN_WC2M_s1x s1x 0.436 0.080 5.9 62.5 67.4 64.5 66.0 216245682 model | metrics
R_101_FPN_DL_WC1M_s1x s1x 0.453 0.079 6.8 62.0 68.1 66.4 67.1 216245771 model | metrics
R_101_FPN_DL_WC2M_s1x s1x 0.464 0.080 6.9 61.9 68.2 66.1 67.1 216245790 model | metrics
Acronyms: `WC1M`: with confidence estimation model type 1 for `U` and `V` and mask confidence estimation `WC2M`: with confidence estimation model type 2 for `U` and `V` and mask confidence estimation ###
Bootstrapping Baselines Master and student models trained using the bootstrapping pipeline with chimpanzee as the target category, see [Sanakoyeu et al., 2020](https://arxiv.org/pdf/2003.00080.pdf) and [Bootstrapping Pipeline](BOOTSTRAPPING_PIPELINE.md) for details. Evaluation is performed on [DensePose Chimps](DENSEPOSE_DATASETS.md#densepose-chimps) dataset.
Name lr
sched
train
time
(s/iter)
inference
time
(s/im)
train
mem
(GB)
box
AP
segm
AP
dp. APex
GPS
dp. AP
GPS
dp. AP
GPSm
model id download
R_50_FPN_DL_WC1M_3x_Atop10P_CA 3x 0.522 0.073 9.7 61.3 59.1 36.2 20.0 30.2 217578784 model | metrics
R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_uniform 3x 1.939 0.072 10.1 60.9 58.5 37.2 21.5 31.0 256453729 model | metrics
R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_uv 3x 1.985 0.072 9.6 61.4 58.9 38.3 22.2 32.1 256452095 model | metrics
R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_finesegm 3x 2.047 0.072 10.3 60.9 58.5 36.7 20.7 30.7 256452819 model | metrics
R_50_FPN_DL_WC1M_3x_Atop10P_CA_B_coarsesegm 3x 1.830 0.070 9.6 61.3 59.2 37.9 21.5 31.6 256455697 model | metrics
Acronyms: `WC1M`: with confidence estimation model type 1 for `U` and `V` and mask confidence estimation `Atop10P`: humans and animals from the 10 best suitable categories are used for training `CA`: class agnostic training, where all annotated instances are mapped into a single category `B_<...>`: schedule with bootstrapping with the specified results sampling strategy Note: The relaxed `dp. APex GPS` metric was used in [Sanakoyeu et al., 2020](https://arxiv.org/pdf/2003.00080.pdf) to evaluate DensePose results. This metric considers matches at thresholds 0.2, 0.3 and 0.4 additionally to the standard ones used in the evaluation protocol. The minimum threshold is controlled by `DENSEPOSE_EVALUATION.MIN_IOU_THRESHOLD` config option. ### License All models available for download are licensed under the [Creative Commons Attribution-ShareAlike 3.0 license](https://creativecommons.org/licenses/by-sa/3.0/) ## References If you use chart-based DensePose methods, please take the references from the following BibTeX entries: DensePose bootstrapping pipeline: ``` @InProceedings{Sanakoyeu2020TransferringDensePose, title = {Transferring Dense Pose to Proximal Animal Classes}, author = {Artsiom Sanakoyeu and Vasil Khalidov and Maureen S. McCarthy and Andrea Vedaldi and Natalia Neverova}, journal = {The IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}, year = {2020}, } ``` DensePose with confidence estimation: ``` @InProceedings{Neverova2019DensePoseConfidences, title = {Correlated Uncertainty for Learning Dense Correspondences from Noisy Labels}, author = {Neverova, Natalia and Novotny, David and Vedaldi, Andrea}, journal = {Advances in Neural Information Processing Systems}, year = {2019}, } ``` Original DensePose: ``` @InProceedings{Guler2018DensePose, title={DensePose: Dense Human Pose Estimation In The Wild}, author={R\{i}za Alp G\"uler, Natalia Neverova, Iasonas Kokkinos}, journal={The IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}, year={2018} } ``` ================================================ FILE: detectron2/projects/DensePose/doc/GETTING_STARTED.md ================================================ # Getting Started with DensePose ## Inference with Pre-trained Models 1. Pick a model and its config file from [Model Zoo(IUV)](DENSEPOSE_IUV.md#ModelZoo), [Model Zoo(CSE)](DENSEPOSE_CSE.md#ModelZoo), for example [densepose_rcnn_R_50_FPN_s1x.yaml](../configs/densepose_rcnn_R_50_FPN_s1x.yaml) 2. Run the [Apply Net](TOOL_APPLY_NET.md) tool to visualize the results or save the to disk. For example, to use contour visualization for DensePose, one can run: ```bash python apply_net.py show configs/densepose_rcnn_R_50_FPN_s1x.yaml densepose_rcnn_R_50_FPN_s1x.pkl image.jpg dp_contour,bbox --output image_densepose_contour.png ``` Please see [Apply Net](TOOL_APPLY_NET.md) for more details on the tool. ## Training First, prepare the [dataset](http://densepose.org/#dataset) into the following structure under the directory you'll run training scripts:
datasets/coco/
  annotations/
    densepose_{train,minival,valminusminival}2014.json
    densepose_minival2014_100.json   (optional, for testing only)
  {train,val}2014/
    # image files that are mentioned in the corresponding json
To train a model one can use the [train_net.py](../train_net.py) script. This script was used to train all DensePose models in [Model Zoo(IUV)](DENSEPOSE_IUV.md#ModelZoo), [Model Zoo(CSE)](DENSEPOSE_CSE.md#ModelZoo). For example, to launch end-to-end DensePose-RCNN training with ResNet-50 FPN backbone on 8 GPUs following the s1x schedule, one can run ```bash python train_net.py --config-file configs/densepose_rcnn_R_50_FPN_s1x.yaml --num-gpus 8 ``` The configs are made for 8-GPU training. To train on 1 GPU, one can apply the [linear learning rate scaling rule](https://arxiv.org/abs/1706.02677): ```bash python train_net.py --config-file configs/densepose_rcnn_R_50_FPN_s1x.yaml \ SOLVER.IMS_PER_BATCH 2 SOLVER.BASE_LR 0.0025 ``` ## Evaluation Model testing can be done in the same way as training, except for an additional flag `--eval-only` and model location specification through `MODEL.WEIGHTS model.pth` in the command line ```bash python train_net.py --config-file configs/densepose_rcnn_R_50_FPN_s1x.yaml \ --eval-only MODEL.WEIGHTS model.pth ``` ## Tools We provide tools which allow one to: - easily view DensePose annotated data in a dataset; - perform DensePose inference on a set of images; - visualize DensePose model results; `query_db` is a tool to print or visualize DensePose data in a dataset. Please refer to [Query DB](TOOL_QUERY_DB.md) for more details on this tool `apply_net` is a tool to print or visualize DensePose results. Please refer to [Apply Net](TOOL_APPLY_NET.md) for more details on this tool ## Installation as a package DensePose can also be installed as a Python package for integration with other software. The following dependencies are needed: - Python >= 3.7 - [PyTorch](https://pytorch.org/get-started/locally/#start-locally) >= 1.7 (to match [detectron2 requirements](https://detectron2.readthedocs.io/en/latest/tutorials/install.html#requirements)) - [torchvision](https://pytorch.org/vision/stable/) version [compatible with your version of PyTorch](https://github.com/pytorch/vision#installation) DensePose can then be installed from this repository with: ``` pip install git+https://github.com/facebookresearch/detectron2@main#subdirectory=projects/DensePose ``` After installation, the package will be importable as `densepose`. ================================================ FILE: detectron2/projects/DensePose/doc/RELEASE_2020_04.md ================================================ # DensePose Confidence Estimation and Model Zoo Improvements * [DensePose models with confidence estimation](doc/DENSEPOSE_IUV.md#ModelZooConfidence) * [Panoptic FPN and DeepLabV3 head implementation](doc/DENSEPOSE_IUV.md#ModelZooDeepLabV3) * Test time augmentations for DensePose * New evaluation metric (GPSm) that yields more reliable scores ================================================ FILE: detectron2/projects/DensePose/doc/RELEASE_2021_03.md ================================================ # DensePose CSE and DensePose Evolution * [DensePose Evolution pipeline](DENSEPOSE_IUV.md#ModelZooBootstrap), a framework to bootstrap DensePose on unlabeled data * [`InferenceBasedLoader`](../densepose/data/inference_based_loader.py) with data samplers to use inference results from one model to train another model (bootstrap); * [`VideoKeyframeDataset`](../densepose/data/video/video_keyframe_dataset.py) to efficiently load images from video keyframes; * Category maps and filters to combine annotations from different categories and train in a class-agnostic manner; * [Pretrained models](DENSEPOSE_IUV.md#ModelZooBootstrap) for DensePose estimation on chimpanzees; * DensePose head training from partial data (segmentation only); * [DensePose models with mask confidence estimation](DENSEPOSE_IUV.md#ModelZooMaskConfidence); * [DensePose Chimps]() dataset for IUV evaluation * [DensePose Continuous Surface Embeddings](DENSEPOSE_CSE.md), a framework to extend DensePose to various categories using 3D models * [Hard embedding](../densepose/modeling/losses/embed.py) and [soft embedding](../densepose/modeling/losses/soft_embed.py) losses to train universal positional embeddings; * [Embedder](../(densepose/modeling/cse/embedder.py) to handle mesh vertex embeddings; * [Storage](../densepose/evaluation/tensor_storage.py) for evaluation with high volumes of data; * [Pretrained models](DENSEPOSE_CSE.md#ModelZoo) for DensePose CSE estimation on humans and animals; * [DensePose Chimps](DENSEPOSE_DATASETS.md#densepose-chimps) and [DensePose LVIS](DENSEPOSE_DATASETS.md#densepose-lvis) datasets for CSE finetuning and evaluation; * [Vertex and texture mapping visualizers](../densepose/vis/densepose_outputs_vertex.py); * Refactoring of all major components: losses, predictors, model outputs, model results, visualizers; * Dedicated structures for [chart outputs](../densepose/structures/chart.py), [chart outputs with confidences](../densepose/structures/chart_confidence.py), [chart results](../densepose/structures/chart_result.py), [CSE outputs](../densepose/structures/cse.py); * Dedicated predictors for [chart-based estimation](../densepose/modeling/predictors/chart.py), [confidence estimation](../densepose/modeling/predictors/chart_confidence.py) and [CSE estimation](../densepose/modeling/predictors/cse.py); * Generic handling of various [conversions](../densepose/converters) (e.g. from outputs to results); * Better organization of various [losses](../densepose/modeling/losses); * Segregation of loss data accumulators for [IUV setting](../densepose/modeling/losses/utils.py) and [CSE setting](../densepose/modeling/losses/embed_utils.py); * Splitting visualizers into separate modules; * [HRNet](../densepose/modeling/hrnet.py) and [HRFPN](../densepose/modeling/hrfpn.py) backbones; * [PoseTrack](DENSEPOSE_DATASETS.md#densepose-posetrack) dataset; * [IUV texture visualizer](../densepose/vis/densepose_results_textures.py) ================================================ FILE: detectron2/projects/DensePose/doc/RELEASE_2021_06.md ================================================ # DensePose CSE with Cycle Losses This release follows the paper [Neverova et al, 2021]() and adds CSE datasets with more annotations, better CSE animal models to the model zoo, losses to ensure cycle consistency for models and mesh alignment evaluator. In particular: * [Pixel to shape](../densepose/modeling/losses/cycle_pix2shape.py) and [shape to shape](../densepose/modeling/losses/cycle_shape2shape.py) cycle consistency losses; * Mesh alignment [evaluator](../densepose/evaluation/mesh_alignment_evaluator.py); * Existing CSE datasets renamed to [ds1_train](https://dl.fbaipublicfiles.com/densepose/annotations/lvis/densepose_lvis_v1_ds1_train_v1.json) and [ds1_val](https://dl.fbaipublicfiles.com/densepose/annotations/lvis/densepose_lvis_v1_ds1_val_v1.json); * New CSE datasets [ds2_train](https://dl.fbaipublicfiles.com/densepose/annotations/lvis/densepose_lvis_v1_ds2_train_v1.json) and [ds2_val](https://dl.fbaipublicfiles.com/densepose/annotations/lvis/densepose_lvis_v1_ds2_val_v1.json) added; * Better CSE animal models trained with the 16k schedule added to the [model zoo](DENSEPOSE_CSE.md#animal-cse-models). ================================================ FILE: detectron2/projects/DensePose/doc/TOOL_APPLY_NET.md ================================================ # Apply Net `apply_net` is a tool to print or visualize DensePose results on a set of images. It has two modes: `dump` to save DensePose model results to a pickle file and `show` to visualize them on images. The `image.jpg` file that is used as an example in this doc can be found [here](http://images.cocodataset.org/train2017/000000117508.jpg) ## Dump Mode The general command form is: ```bash python apply_net.py dump [-h] [-v] [--output ] ``` There are three mandatory arguments: - ``, configuration file for a given model; - ``, model file with trained parameters - ``, input image file name, pattern or folder One can additionally provide `--output` argument to define the output file name, which defaults to `output.pkl`. Examples: 1. Dump results of the [R_50_FPN_s1x](https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl) DensePose model for images in a folder `images` to file `dump.pkl`: ```bash python apply_net.py dump configs/densepose_rcnn_R_50_FPN_s1x.yaml \ https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl \ images --output dump.pkl -v ``` 2. Dump results of the [R_50_FPN_s1x](https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl) DensePose model for images with file name matching a pattern `image*.jpg` to file `results.pkl`: ```bash python apply_net.py dump configs/densepose_rcnn_R_50_FPN_s1x.yaml \ https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl \ "image*.jpg" --output results.pkl -v ``` If you want to load the pickle file generated by the above command: ``` # make sure DensePose is in your PYTHONPATH, or use the following line to add it: sys.path.append("/your_detectron2_path/detectron2_repo/projects/DensePose/") f = open('/your_result_path/results.pkl', 'rb') data = pickle.load(f) ``` The file `results.pkl` contains the list of results per image, for each image the result is a dictionary. **If you use a [IUV model](DENSEPOSE_IUV.md#-model-zoo-and-baselines)**, the dumped data will have the following format: ``` data: [{'file_name': '/your_path/image1.jpg', 'scores': tensor([0.9884]), 'pred_boxes_XYXY': tensor([[ 69.6114, 0.0000, 706.9797, 706.0000]]), 'pred_densepose': [DensePoseChartResultWithConfidences(labels=tensor(...), uv=tensor(...), sigma_1=None, sigma_2=None, kappa_u=None, kappa_v=None, fine_segm_confidence=None, coarse_segm_confidence=None), DensePoseChartResultWithConfidences, ...] } {'file_name': '/your_path/image2.jpg', 'scores': tensor([0.9999, 0.5373, 0.3991]), 'pred_boxes_XYXY': tensor([[ 59.5734, 7.7535, 579.9311, 932.3619], [612.9418, 686.1254, 612.9999, 704.6053], [164.5081, 407.4034, 598.3944, 920.4266]]), 'pred_densepose': [DensePoseChartResultWithConfidences(labels=tensor(...), uv=tensor(...), sigma_1=None, sigma_2=None, kappa_u=None, kappa_v=None, fine_segm_confidence=None, coarse_segm_confidence=None), DensePoseChartResultWithConfidences, ...] }] ``` `DensePoseChartResultWithConfidences` contains the following fields: - `labels` - a tensor of size `[H, W]` of type `torch.long` which contains fine segmentation labels (previously called `I`) - `uv` - a tensor of size `[2, H, W]` of type `torch.float` which contains `U` and `V` coordinates - various optional confidence-related fields (`sigma_1`, `sigma_2`, `kappa_u`, `kappa_v`, `fine_segm_confidence`, `coarse_segm_confidence`) **If you use a [CSE model](DENSEPOSE_CSE.md#-model-zoo-and-baselines)**, the dumped data will have the following format: ``` data: [{'file_name': '/your_path/image1.jpg', 'scores': tensor([0.9984, 0.9961]), 'pred_boxes_XYXY': tensor([[480.0093, 461.0796, 698.3614, 696.1011], [78.1589, 168.6614, 307.1287, 653.8522]]), 'pred_densepose': DensePoseEmbeddingPredictorOutput(embedding=tensor(...), coarse_segm=tensor(...))} {'file_name': '/your_path/image2.jpg', 'scores': tensor([0.9189, 0.9491]), 'pred_boxes_XYXY': tensor([[734.9685, 534.2003, 287.3923, 254.8859], [434.2853, 765.1219, 132.1029, 867.9283]]), 'pred_densepose': DensePoseEmbeddingPredictorOutput(embedding=tensor(...), coarse_segm=tensor(...))}] ``` `DensePoseEmbeddingPredictorOutput` contains the following fields: - `embedding` - a tensor of size `[N, D, sz, sz]` of type `torch.float`, which contains embeddings of size `D` of the `N` detections in the image - `coarse_segm` - a tensor of size `[N, 2, sz, sz]` of type `torch.float` which contains segmentation scores of the `N` detections in the image; e.g. a mask can be obtained by `coarse_segm.argmax(dim=1)` `sz` is a fixed size for the tensors; you can resize them to the size of the bounding box, if needed We can use the following code, to parse the outputs of the first detected instance on the first image (IUV model). ``` img_id, instance_id = 0, 0 # Look at the first image and the first detected instance bbox_xyxy = data[img_id]['pred_boxes_XYXY'][instance_id] result = data[img_id]['pred_densepose'][instance_id] uv = result.uv ``` The array `bbox_xyxy` contains (x0, y0, x1, y1) of the bounding box. ## Visualization Mode The general command form is: ```bash python apply_net.py show [-h] [-v] [--min_score ] [--nms_thresh ] [--output ] ``` There are four mandatory arguments: - ``, configuration file for a given model; - ``, model file with trained parameters - ``, input image file name, pattern or folder - ``, visualizations specifier; currently available visualizations are: * `bbox` - bounding boxes of detected persons; * `dp_segm` - segmentation masks for detected persons; * `dp_u` - each body part is colored according to the estimated values of the U coordinate in part parameterization; * `dp_v` - each body part is colored according to the estimated values of the V coordinate in part parameterization; * `dp_contour` - plots contours with color-coded U and V coordinates; * `dp_iuv_texture` - transfers the texture from a given texture image file to detected instances, in IUV mode; * `dp_vertex` - plots the rainbow visualization of the closest vertices prediction for a given mesh, in CSE mode; * `dp_cse_texture` - transfers the texture from a given list of texture image files (one from each human or animal mesh) to detected instances, in CSE mode One can additionally provide the following optional arguments: - `--min_score` to only show detections with sufficient scores that are not lower than provided value - `--nms_thresh` to additionally apply non-maximum suppression to detections at a given threshold - `--output` to define visualization file name template, which defaults to `output.png`. To distinguish output file names for different images, the tool appends 1-based entry index, e.g. output.0001.png, output.0002.png, etc... - `--texture_atlas` to define the texture atlas image for IUV texture transfer - `--texture_atlases_map` to define the texture atlas images map (a dictionary `{mesh name: texture atlas image}`) for CSE texture transfer The following examples show how to output results of a DensePose model with ResNet-50 FPN backbone using different visualizations for image `image.jpg`: 1. Show bounding box and segmentation: ```bash python apply_net.py show configs/densepose_rcnn_R_50_FPN_s1x.yaml \ https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl \ image.jpg bbox,dp_segm -v ``` ![Bounding Box + Segmentation Visualization](https://dl.fbaipublicfiles.com/densepose/web/apply_net/res_bbox_dp_segm.jpg) 2. Show bounding box and estimated U coordinates for body parts: ```bash python apply_net.py show configs/densepose_rcnn_R_50_FPN_s1x.yaml \ https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl \ image.jpg bbox,dp_u -v ``` ![Bounding Box + U Coordinate Visualization](https://dl.fbaipublicfiles.com/densepose/web/apply_net/res_bbox_dp_u.jpg) 3. Show bounding box and estimated V coordinates for body parts: ```bash python apply_net.py show configs/densepose_rcnn_R_50_FPN_s1x.yaml \ https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl \ image.jpg bbox,dp_v -v ``` ![Bounding Box + V Coordinate Visualization](https://dl.fbaipublicfiles.com/densepose/web/apply_net/res_bbox_dp_v.jpg) 4. Show bounding box and estimated U and V coordinates via contour plots: ```bash python apply_net.py show configs/densepose_rcnn_R_50_FPN_s1x.yaml \ https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl \ image.jpg dp_contour,bbox -v ``` ![Bounding Box + Contour Visualization](https://dl.fbaipublicfiles.com/densepose/web/apply_net/res_bbox_dp_contour.jpg) 5. Show bounding box and texture transfer: ```bash python apply_net.py show configs/densepose_rcnn_R_50_FPN_s1x.yaml \ https://dl.fbaipublicfiles.com/densepose/densepose_rcnn_R_50_FPN_s1x/165712039/model_final_162be9.pkl \ image.jpg dp_iuv_texture,bbox --texture_atlas texture_from_SURREAL.jpg -v ``` ![Bounding Box + IUV Texture Transfer Visualization](https://dl.fbaipublicfiles.com/densepose/web/apply_net/res_bbox_dp_iuv_texture.jpg) 6. Show bounding box and CSE rainbow visualization: ```bash python apply_net.py show configs/cse/densepose_rcnn_R_50_FPN_s1x.yaml \ https://dl.fbaipublicfiles.com/densepose/cse/densepose_rcnn_R_50_FPN_s1x/251155172/model_final_c4ea5f.pkl \ image.jpg dp_vertex,bbox -v ``` ![Bounding Box + CSE Rainbow Visualization](https://dl.fbaipublicfiles.com/densepose/web/apply_net/res_bbox_dp_vertex.jpg) 7. Show bounding box and CSE texture transfer: ```bash python apply_net.py show configs/cse/densepose_rcnn_R_50_FPN_s1x.yaml \ https://dl.fbaipublicfiles.com/densepose/cse/densepose_rcnn_R_50_FPN_s1x/251155172/model_final_c4ea5f.pkl \ image.jpg dp_cse_texture,bbox --texture_atlases_map '{"smpl_27554": "smpl_uvSnapshot_colors.jpg"}' -v ``` ![Bounding Box + CSE Texture Transfer Visualization](https://dl.fbaipublicfiles.com/densepose/web/apply_net/res_bbox_dp_cse_texture.jpg) The texture files can be found in the `doc/images` folder ================================================ FILE: detectron2/projects/DensePose/doc/TOOL_QUERY_DB.md ================================================ # Query Dataset `query_db` is a tool to print or visualize DensePose data from a dataset. It has two modes: `print` and `show` to output dataset entries to standard output or to visualize them on images. ## Print Mode The general command form is: ```bash python query_db.py print [-h] [-v] [--max-entries N] ``` There are two mandatory arguments: - ``, DensePose dataset specification, from which to select the entries (e.g. `densepose_coco_2014_train`). - ``, dataset entry selector which can be a single specification, or a comma-separated list of specifications of the form `field[:type]=value` for exact match with the value or `field[:type]=min-max` for a range of values One can additionally limit the maximum number of entries to output by providing `--max-entries` argument. Examples: 1. Output at most 10 first entries from the `densepose_coco_2014_train` dataset: ```bash python query_db.py print densepose_coco_2014_train \* --max-entries 10 -v ``` 2. Output all entries with `file_name` equal to `COCO_train2014_000000000036.jpg`: ```bash python query_db.py print densepose_coco_2014_train file_name=COCO_train2014_000000000036.jpg -v ``` 3. Output all entries with `image_id` between 36 and 156: ```bash python query_db.py print densepose_coco_2014_train image_id:int=36-156 -v ``` ## Visualization Mode The general command form is: ```bash python query_db.py show [-h] [-v] [--max-entries N] [--output ] ``` There are three mandatory arguments: - ``, DensePose dataset specification, from which to select the entries (e.g. `densepose_coco_2014_train`). - ``, dataset entry selector which can be a single specification, or a comma-separated list of specifications of the form `field[:type]=value` for exact match with the value or `field[:type]=min-max` for a range of values - ``, visualizations specifier; currently available visualizations are: * `bbox` - bounding boxes of annotated persons; * `dp_i` - annotated points colored according to the containing part; * `dp_pts` - annotated points in green color; * `dp_segm` - segmentation masks for annotated persons; * `dp_u` - annotated points colored according to their U coordinate in part parameterization; * `dp_v` - annotated points colored according to their V coordinate in part parameterization; One can additionally provide one of the two optional arguments: - `--max_entries` to limit the maximum number of entries to visualize - `--output` to provide visualization file name template, which defaults to `output.png`. To distinguish file names for different dataset entries, the tool appends 1-based entry index to the output file name, e.g. output.0001.png, output.0002.png, etc. The following examples show how to output different visualizations for image with `id = 322` from `densepose_coco_2014_train` dataset: 1. Show bounding box and segmentation: ```bash python query_db.py show densepose_coco_2014_train image_id:int=322 bbox,dp_segm -v ``` ![Bounding Box + Segmentation Visualization](images/vis_bbox_dp_segm.jpg) 2. Show bounding box and points colored according to the containing part: ```bash python query_db.py show densepose_coco_2014_train image_id:int=322 bbox,dp_i -v ``` ![Bounding Box + Point Label Visualization](images/vis_bbox_dp_i.jpg) 3. Show bounding box and annotated points in green color: ```bash python query_db.py show densepose_coco_2014_train image_id:int=322 bbox,dp_segm -v ``` ![Bounding Box + Point Visualization](images/vis_bbox_dp_pts.jpg) 4. Show bounding box and annotated points colored according to their U coordinate in part parameterization: ```bash python query_db.py show densepose_coco_2014_train image_id:int=322 bbox,dp_u -v ``` ![Bounding Box + Point U Visualization](images/vis_bbox_dp_u.jpg) 5. Show bounding box and annotated points colored according to their V coordinate in part parameterization: ```bash python query_db.py show densepose_coco_2014_train image_id:int=322 bbox,dp_v -v ``` ![Bounding Box + Point V Visualization](images/vis_bbox_dp_v.jpg) ================================================ FILE: detectron2/projects/DensePose/query_db.py ================================================ #!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. import argparse import logging import os import sys from timeit import default_timer as timer from typing import Any, ClassVar, Dict, List import torch from detectron2.data.catalog import DatasetCatalog from detectron2.utils.file_io import PathManager from detectron2.utils.logger import setup_logger from densepose.structures import DensePoseDataRelative from densepose.utils.dbhelper import EntrySelector from densepose.utils.logger import verbosity_to_level from densepose.vis.base import CompoundVisualizer from densepose.vis.bounding_box import BoundingBoxVisualizer from densepose.vis.densepose_data_points import ( DensePoseDataCoarseSegmentationVisualizer, DensePoseDataPointsIVisualizer, DensePoseDataPointsUVisualizer, DensePoseDataPointsVisualizer, DensePoseDataPointsVVisualizer, ) DOC = """Query DB - a tool to print / visualize data from a database """ LOGGER_NAME = "query_db" logger = logging.getLogger(LOGGER_NAME) _ACTION_REGISTRY: Dict[str, "Action"] = {} class Action(object): @classmethod def add_arguments(cls: type, parser: argparse.ArgumentParser): parser.add_argument( "-v", "--verbosity", action="count", help="Verbose mode. Multiple -v options increase the verbosity.", ) def register_action(cls: type): """ Decorator for action classes to automate action registration """ global _ACTION_REGISTRY _ACTION_REGISTRY[cls.COMMAND] = cls return cls class EntrywiseAction(Action): @classmethod def add_arguments(cls: type, parser: argparse.ArgumentParser): super(EntrywiseAction, cls).add_arguments(parser) parser.add_argument( "dataset", metavar="", help="Dataset name (e.g. densepose_coco_2014_train)" ) parser.add_argument( "selector", metavar="", help="Dataset entry selector in the form field1[:type]=value1[," "field2[:type]=value_min-value_max...] which selects all " "entries from the dataset that satisfy the constraints", ) parser.add_argument( "--max-entries", metavar="N", help="Maximum number of entries to process", type=int ) @classmethod def execute(cls: type, args: argparse.Namespace): dataset = setup_dataset(args.dataset) entry_selector = EntrySelector.from_string(args.selector) context = cls.create_context(args) if args.max_entries is not None: for _, entry in zip(range(args.max_entries), dataset): if entry_selector(entry): cls.execute_on_entry(entry, context) else: for entry in dataset: if entry_selector(entry): cls.execute_on_entry(entry, context) @classmethod def create_context(cls: type, args: argparse.Namespace) -> Dict[str, Any]: context = {} return context @register_action class PrintAction(EntrywiseAction): """ Print action that outputs selected entries to stdout """ COMMAND: ClassVar[str] = "print" @classmethod def add_parser(cls: type, subparsers: argparse._SubParsersAction): parser = subparsers.add_parser(cls.COMMAND, help="Output selected entries to stdout. ") cls.add_arguments(parser) parser.set_defaults(func=cls.execute) @classmethod def add_arguments(cls: type, parser: argparse.ArgumentParser): super(PrintAction, cls).add_arguments(parser) @classmethod def execute_on_entry(cls: type, entry: Dict[str, Any], context: Dict[str, Any]): import pprint printer = pprint.PrettyPrinter(indent=2, width=200, compact=True) printer.pprint(entry) @register_action class ShowAction(EntrywiseAction): """ Show action that visualizes selected entries on an image """ COMMAND: ClassVar[str] = "show" VISUALIZERS: ClassVar[Dict[str, object]] = { "dp_segm": DensePoseDataCoarseSegmentationVisualizer(), "dp_i": DensePoseDataPointsIVisualizer(), "dp_u": DensePoseDataPointsUVisualizer(), "dp_v": DensePoseDataPointsVVisualizer(), "dp_pts": DensePoseDataPointsVisualizer(), "bbox": BoundingBoxVisualizer(), } @classmethod def add_parser(cls: type, subparsers: argparse._SubParsersAction): parser = subparsers.add_parser(cls.COMMAND, help="Visualize selected entries") cls.add_arguments(parser) parser.set_defaults(func=cls.execute) @classmethod def add_arguments(cls: type, parser: argparse.ArgumentParser): super(ShowAction, cls).add_arguments(parser) parser.add_argument( "visualizations", metavar="", help="Comma separated list of visualizations, possible values: " "[{}]".format(",".join(sorted(cls.VISUALIZERS.keys()))), ) parser.add_argument( "--output", metavar="", default="output.png", help="File name to save output to", ) @classmethod def execute_on_entry(cls: type, entry: Dict[str, Any], context: Dict[str, Any]): import cv2 import numpy as np image_fpath = PathManager.get_local_path(entry["file_name"]) image = cv2.imread(image_fpath, cv2.IMREAD_GRAYSCALE) image = np.tile(image[:, :, np.newaxis], [1, 1, 3]) datas = cls._extract_data_for_visualizers_from_entry(context["vis_specs"], entry) visualizer = context["visualizer"] image_vis = visualizer.visualize(image, datas) entry_idx = context["entry_idx"] + 1 out_fname = cls._get_out_fname(entry_idx, context["out_fname"]) cv2.imwrite(out_fname, image_vis) logger.info(f"Output saved to {out_fname}") context["entry_idx"] += 1 @classmethod def _get_out_fname(cls: type, entry_idx: int, fname_base: str): base, ext = os.path.splitext(fname_base) return base + ".{0:04d}".format(entry_idx) + ext @classmethod def create_context(cls: type, args: argparse.Namespace) -> Dict[str, Any]: vis_specs = args.visualizations.split(",") visualizers = [] for vis_spec in vis_specs: vis = cls.VISUALIZERS[vis_spec] visualizers.append(vis) context = { "vis_specs": vis_specs, "visualizer": CompoundVisualizer(visualizers), "out_fname": args.output, "entry_idx": 0, } return context @classmethod def _extract_data_for_visualizers_from_entry( cls: type, vis_specs: List[str], entry: Dict[str, Any] ): dp_list = [] bbox_list = [] for annotation in entry["annotations"]: is_valid, _ = DensePoseDataRelative.validate_annotation(annotation) if not is_valid: continue bbox = torch.as_tensor(annotation["bbox"]) bbox_list.append(bbox) dp_data = DensePoseDataRelative(annotation) dp_list.append(dp_data) datas = [] for vis_spec in vis_specs: datas.append(bbox_list if "bbox" == vis_spec else (bbox_list, dp_list)) return datas def setup_dataset(dataset_name): logger.info("Loading dataset {}".format(dataset_name)) start = timer() dataset = DatasetCatalog.get(dataset_name) stop = timer() logger.info("Loaded dataset {} in {:.3f}s".format(dataset_name, stop - start)) return dataset def create_argument_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( description=DOC, formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=120), ) parser.set_defaults(func=lambda _: parser.print_help(sys.stdout)) subparsers = parser.add_subparsers(title="Actions") for _, action in _ACTION_REGISTRY.items(): action.add_parser(subparsers) return parser def main(): parser = create_argument_parser() args = parser.parse_args() verbosity = args.verbosity if hasattr(args, "verbosity") else None global logger logger = setup_logger(name=LOGGER_NAME) logger.setLevel(verbosity_to_level(verbosity)) args.func(args) if __name__ == "__main__": main() ================================================ FILE: detectron2/projects/DensePose/setup.py ================================================ import re from pathlib import Path from setuptools import find_packages, setup try: import torch # noqa: F401 except ImportError as e: raise Exception( """ You must install PyTorch prior to installing DensePose: pip install torch For more information: https://pytorch.org/get-started/locally/ """ ) from e def get_detectron2_current_version(): """Version is not available for import through Python since it is above the top level of the package. Instead, we parse it from the file with a regex.""" # Get version info from detectron2 __init__.py version_source = (Path(__file__).parents[2] / "detectron2" / "__init__.py").read_text() version_number = re.findall(r'__version__ = "([0-9\.]+)"', version_source)[0] return version_number setup( name="detectron2-densepose", author="FAIR", version=get_detectron2_current_version(), url="https://github.com/facebookresearch/detectron2/tree/main/projects/DensePose", packages=find_packages(), python_requires=">=3.7", install_requires=[ "av>=8.0.3", "detectron2@git+https://github.com/facebookresearch/detectron2.git", "opencv-python-headless>=4.5.3.56", "scipy>=1.5.4", ], ) ================================================ FILE: detectron2/projects/DensePose/tests/common.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import os import torch from detectron2.config import get_cfg from detectron2.engine import default_setup from detectron2.modeling import build_model from densepose import add_densepose_config _BASE_CONFIG_DIR = "configs" _EVOLUTION_CONFIG_SUB_DIR = "evolution" _HRNET_CONFIG_SUB_DIR = "HRNet" _QUICK_SCHEDULES_CONFIG_SUB_DIR = "quick_schedules" _BASE_CONFIG_FILE_PREFIX = "Base-" _CONFIG_FILE_EXT = ".yaml" def _get_base_config_dir(): """ Return the base directory for configurations """ return os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", _BASE_CONFIG_DIR) def _get_evolution_config_dir(): """ Return the base directory for evolution configurations """ return os.path.join(_get_base_config_dir(), _EVOLUTION_CONFIG_SUB_DIR) def _get_hrnet_config_dir(): """ Return the base directory for HRNet configurations """ return os.path.join(_get_base_config_dir(), _HRNET_CONFIG_SUB_DIR) def _get_quick_schedules_config_dir(): """ Return the base directory for quick schedules configurations """ return os.path.join(_get_base_config_dir(), _QUICK_SCHEDULES_CONFIG_SUB_DIR) def _collect_config_files(config_dir): """ Collect all configuration files (i.e. densepose_*.yaml) directly in the specified directory """ start = _get_base_config_dir() results = [] for entry in os.listdir(config_dir): path = os.path.join(config_dir, entry) if not os.path.isfile(path): continue _, ext = os.path.splitext(entry) if ext != _CONFIG_FILE_EXT: continue if entry.startswith(_BASE_CONFIG_FILE_PREFIX): continue config_file = os.path.relpath(path, start) results.append(config_file) return results def get_config_files(): """ Get all the configuration files (relative to the base configuration directory) """ return _collect_config_files(_get_base_config_dir()) def get_evolution_config_files(): """ Get all the evolution configuration files (relative to the base configuration directory) """ return _collect_config_files(_get_evolution_config_dir()) def get_hrnet_config_files(): """ Get all the HRNet configuration files (relative to the base configuration directory) """ return _collect_config_files(_get_hrnet_config_dir()) def get_quick_schedules_config_files(): """ Get all the quick schedules configuration files (relative to the base configuration directory) """ return _collect_config_files(_get_quick_schedules_config_dir()) def get_model_config(config_file): """ Load and return the configuration from the specified file (relative to the base configuration directory) """ cfg = get_cfg() add_densepose_config(cfg) path = os.path.join(_get_base_config_dir(), config_file) cfg.merge_from_file(path) if not torch.cuda.is_available(): cfg.MODEL.DEVICE = "cpu" return cfg def get_model(config_file): """ Get the model from the specified file (relative to the base configuration directory) """ cfg = get_model_config(config_file) return build_model(cfg) def setup(config_file): """ Setup the configuration from the specified file (relative to the base configuration directory) """ cfg = get_model_config(config_file) cfg.freeze() default_setup(cfg, {}) ================================================ FILE: detectron2/projects/DensePose/tests/test_chart_based_annotations_accumulator.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest import torch from detectron2.structures import Boxes, BoxMode, Instances from densepose.modeling.losses.utils import ChartBasedAnnotationsAccumulator from densepose.structures import DensePoseDataRelative, DensePoseList image_shape = (100, 100) instances = Instances(image_shape) n_instances = 3 instances.proposal_boxes = Boxes(torch.rand(n_instances, 4)) instances.gt_boxes = Boxes(torch.rand(n_instances, 4)) # instances.gt_densepose = None cannot happen because instances attributes need a length class TestChartBasedAnnotationsAccumulator(unittest.TestCase): def test_chart_based_annotations_accumulator_no_gt_densepose(self): accumulator = ChartBasedAnnotationsAccumulator() accumulator.accumulate(instances) expected_values = {"nxt_bbox_with_dp_index": 0, "nxt_bbox_index": n_instances} for key in accumulator.__dict__: self.assertEqual(getattr(accumulator, key), expected_values.get(key, [])) def test_chart_based_annotations_accumulator_gt_densepose_none(self): instances.gt_densepose = [None] * n_instances accumulator = ChartBasedAnnotationsAccumulator() accumulator.accumulate(instances) expected_values = {"nxt_bbox_with_dp_index": 0, "nxt_bbox_index": n_instances} for key in accumulator.__dict__: self.assertEqual(getattr(accumulator, key), expected_values.get(key, [])) def test_chart_based_annotations_accumulator_gt_densepose(self): data_relative_keys = [ DensePoseDataRelative.X_KEY, DensePoseDataRelative.Y_KEY, DensePoseDataRelative.I_KEY, DensePoseDataRelative.U_KEY, DensePoseDataRelative.V_KEY, DensePoseDataRelative.S_KEY, ] annotations = [DensePoseDataRelative({k: [0] for k in data_relative_keys})] * n_instances instances.gt_densepose = DensePoseList(annotations, instances.gt_boxes, image_shape) accumulator = ChartBasedAnnotationsAccumulator() accumulator.accumulate(instances) bbox_xywh_est = BoxMode.convert( instances.proposal_boxes.tensor.clone(), BoxMode.XYXY_ABS, BoxMode.XYWH_ABS ) bbox_xywh_gt = BoxMode.convert( instances.gt_boxes.tensor.clone(), BoxMode.XYXY_ABS, BoxMode.XYWH_ABS ) expected_values = { "s_gt": [ torch.zeros((3, DensePoseDataRelative.MASK_SIZE, DensePoseDataRelative.MASK_SIZE)) ] * n_instances, "bbox_xywh_est": bbox_xywh_est.split(1), "bbox_xywh_gt": bbox_xywh_gt.split(1), "point_bbox_with_dp_indices": [torch.tensor([i]) for i in range(n_instances)], "point_bbox_indices": [torch.tensor([i]) for i in range(n_instances)], "bbox_indices": list(range(n_instances)), "nxt_bbox_with_dp_index": n_instances, "nxt_bbox_index": n_instances, } default_value = [torch.tensor([0])] * 3 for key in accumulator.__dict__: to_test = getattr(accumulator, key) gt_value = expected_values.get(key, default_value) if key in ["nxt_bbox_with_dp_index", "nxt_bbox_index"]: self.assertEqual(to_test, gt_value) elif key == "bbox_indices": self.assertListEqual(to_test, gt_value) else: self.assertTrue(torch.allclose(torch.stack(to_test), torch.stack(gt_value))) ================================================ FILE: detectron2/projects/DensePose/tests/test_combine_data_loader.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import random import unittest from typing import Any, Iterable, Iterator, Tuple from densepose.data import CombinedDataLoader def _grouper(iterable: Iterable[Any], n: int, fillvalue=None) -> Iterator[Tuple[Any]]: """ Group elements of an iterable by chunks of size `n`, e.g. grouper(range(9), 4) -> (0, 1, 2, 3), (4, 5, 6, 7), (8, None, None, None) """ it = iter(iterable) while True: values = [] for _ in range(n): try: value = next(it) except StopIteration: values.extend([fillvalue] * (n - len(values))) yield tuple(values) return values.append(value) yield tuple(values) class TestCombinedDataLoader(unittest.TestCase): def test_combine_loaders_1(self): loader1 = _grouper([f"1_{i}" for i in range(10)], 2) loader2 = _grouper([f"2_{i}" for i in range(11)], 3) batch_size = 4 ratios = (0.1, 0.9) random.seed(43) combined = CombinedDataLoader((loader1, loader2), batch_size, ratios) BATCHES_GT = [ ["1_0", "1_1", "2_0", "2_1"], ["2_2", "2_3", "2_4", "2_5"], ["1_2", "1_3", "2_6", "2_7"], ["2_8", "2_9", "2_10", None], ] for i, batch in enumerate(combined): self.assertEqual(len(batch), batch_size) self.assertEqual(batch, BATCHES_GT[i]) ================================================ FILE: detectron2/projects/DensePose/tests/test_cse_annotations_accumulator.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import unittest import torch from detectron2.structures import Boxes, BoxMode, Instances from densepose.modeling.losses.embed_utils import CseAnnotationsAccumulator from densepose.structures import DensePoseDataRelative, DensePoseList class TestCseAnnotationsAccumulator(unittest.TestCase): def test_cse_annotations_accumulator_nodp(self): instances_lst = [ self._create_instances_nodp(), ] self._test_template(instances_lst) def test_cse_annotations_accumulator_sparsedp(self): instances_lst = [ self._create_instances_sparsedp(), ] self._test_template(instances_lst) def test_cse_annotations_accumulator_fulldp(self): instances_lst = [ self._create_instances_fulldp(), ] self._test_template(instances_lst) def test_cse_annotations_accumulator_combined(self): instances_lst = [ self._create_instances_nodp(), self._create_instances_sparsedp(), self._create_instances_fulldp(), ] self._test_template(instances_lst) def _test_template(self, instances_lst): acc = CseAnnotationsAccumulator() for instances in instances_lst: acc.accumulate(instances) packed_anns = acc.pack() self._check_correspondence(packed_anns, instances_lst) def _create_instances_nodp(self): image_shape = (480, 640) instances = Instances(image_shape) instances.gt_boxes = Boxes( torch.as_tensor( [ [40.0, 40.0, 140.0, 140.0], [160.0, 160.0, 270.0, 270.0], [40.0, 160.0, 160.0, 280.0], ] ) ) instances.proposal_boxes = Boxes( torch.as_tensor( [ [41.0, 39.0, 142.0, 138.0], [161.0, 159.0, 272.0, 268.0], [41.0, 159.0, 162.0, 278.0], ] ) ) # do not add gt_densepose return instances def _create_instances_sparsedp(self): image_shape = (540, 720) instances = Instances(image_shape) instances.gt_boxes = Boxes( torch.as_tensor( [ [50.0, 50.0, 130.0, 130.0], [150.0, 150.0, 240.0, 240.0], [50.0, 150.0, 230.0, 330.0], ] ) ) instances.proposal_boxes = Boxes( torch.as_tensor( [ [49.0, 51.0, 131.0, 129.0], [151.0, 149.0, 241.0, 239.0], [51.0, 149.0, 232.0, 329.0], ] ) ) instances.gt_densepose = DensePoseList( [ None, self._create_dp_data( { "dp_x": [81.69, 153.47, 151.00], "dp_y": [162.24, 128.71, 113.81], "dp_vertex": [0, 1, 2], "ref_model": "zebra_5002", "dp_masks": [], }, {"c": (166, 133), "r": 64}, ), None, ], instances.gt_boxes, image_shape, ) return instances def _create_instances_fulldp(self): image_shape = (680, 840) instances = Instances(image_shape) instances.gt_boxes = Boxes( torch.as_tensor( [ [65.0, 55.0, 165.0, 155.0], [170.0, 175.0, 275.0, 280.0], [55.0, 165.0, 165.0, 275.0], ] ) ) instances.proposal_boxes = Boxes( torch.as_tensor( [ [66.0, 54.0, 166.0, 154.0], [171.0, 174.0, 276.0, 279.0], [56.0, 164.0, 166.0, 274.0], ] ) ) instances.gt_densepose = DensePoseList( [ self._create_dp_data( { "dp_x": [149.99, 198.62, 157.59], "dp_y": [170.74, 197.73, 123.12], "dp_vertex": [3, 4, 5], "ref_model": "cat_5001", "dp_masks": [], }, {"c": (100, 100), "r": 50}, ), self._create_dp_data( { "dp_x": [234.53, 116.72, 71.66], "dp_y": [107.53, 11.31, 142.32], "dp_vertex": [6, 7, 8], "ref_model": "dog_5002", "dp_masks": [], }, {"c": (200, 150), "r": 40}, ), self._create_dp_data( { "dp_x": [225.54, 202.61, 135.90], "dp_y": [167.46, 181.00, 211.47], "dp_vertex": [9, 10, 11], "ref_model": "elephant_5002", "dp_masks": [], }, {"c": (100, 200), "r": 45}, ), ], instances.gt_boxes, image_shape, ) return instances def _create_dp_data(self, anns, blob_def=None): dp_data = DensePoseDataRelative(anns) if blob_def is not None: dp_data.segm[ blob_def["c"][0] - blob_def["r"] : blob_def["c"][0] + blob_def["r"], blob_def["c"][1] - blob_def["r"] : blob_def["c"][1] + blob_def["r"], ] = 1 return dp_data def _check_correspondence(self, packed_anns, instances_lst): instance_idx = 0 data_idx = 0 pt_offset = 0 if packed_anns is not None: bbox_xyxy_gt = BoxMode.convert( packed_anns.bbox_xywh_gt.clone(), BoxMode.XYWH_ABS, BoxMode.XYXY_ABS ) bbox_xyxy_est = BoxMode.convert( packed_anns.bbox_xywh_est.clone(), BoxMode.XYWH_ABS, BoxMode.XYXY_ABS ) for instances in instances_lst: if not hasattr(instances, "gt_densepose"): instance_idx += len(instances) continue for i, dp_data in enumerate(instances.gt_densepose): if dp_data is None: instance_idx += 1 continue n_pts = len(dp_data.x) self.assertTrue( torch.allclose(dp_data.x, packed_anns.x_gt[pt_offset : pt_offset + n_pts]) ) self.assertTrue( torch.allclose(dp_data.y, packed_anns.y_gt[pt_offset : pt_offset + n_pts]) ) self.assertTrue(torch.allclose(dp_data.segm, packed_anns.coarse_segm_gt[data_idx])) self.assertTrue( torch.allclose( torch.ones(n_pts, dtype=torch.long) * dp_data.mesh_id, packed_anns.vertex_mesh_ids_gt[pt_offset : pt_offset + n_pts], ) ) self.assertTrue( torch.allclose( dp_data.vertex_ids, packed_anns.vertex_ids_gt[pt_offset : pt_offset + n_pts] ) ) self.assertTrue( torch.allclose(instances.gt_boxes.tensor[i], bbox_xyxy_gt[data_idx]) ) self.assertTrue( torch.allclose(instances.proposal_boxes.tensor[i], bbox_xyxy_est[data_idx]) ) self.assertTrue( torch.allclose( torch.ones(n_pts, dtype=torch.long) * data_idx, packed_anns.point_bbox_with_dp_indices[pt_offset : pt_offset + n_pts], ) ) self.assertTrue( torch.allclose( torch.ones(n_pts, dtype=torch.long) * instance_idx, packed_anns.point_bbox_indices[pt_offset : pt_offset + n_pts], ) ) self.assertEqual(instance_idx, packed_anns.bbox_indices[data_idx]) pt_offset += n_pts instance_idx += 1 data_idx += 1 if data_idx == 0: self.assertIsNone(packed_anns) ================================================ FILE: detectron2/projects/DensePose/tests/test_dataset_loaded_annotations.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import unittest from densepose.data.datasets.builtin import COCO_DATASETS, DENSEPOSE_ANNOTATIONS_DIR, LVIS_DATASETS from densepose.data.datasets.coco import load_coco_json from densepose.data.datasets.lvis import load_lvis_json from densepose.data.utils import maybe_prepend_base_path from densepose.structures import DensePoseDataRelative class TestDatasetLoadedAnnotations(unittest.TestCase): COCO_DATASET_DATA = { "densepose_coco_2014_train": {"n_instances": 39210}, "densepose_coco_2014_minival": {"n_instances": 2243}, "densepose_coco_2014_minival_100": {"n_instances": 164}, "densepose_coco_2014_valminusminival": {"n_instances": 7297}, "densepose_coco_2014_train_cse": {"n_instances": 39210}, "densepose_coco_2014_minival_cse": {"n_instances": 2243}, "densepose_coco_2014_minival_100_cse": {"n_instances": 164}, "densepose_coco_2014_valminusminival_cse": {"n_instances": 7297}, "densepose_chimps": {"n_instances": 930}, "posetrack2017_train": {"n_instances": 8274}, "posetrack2017_val": {"n_instances": 4753}, "lvis_v05_train": {"n_instances": 5186}, "lvis_v05_val": {"n_instances": 1037}, } LVIS_DATASET_DATA = { "densepose_lvis_v1_train1": {"n_instances": 3394}, "densepose_lvis_v1_train2": {"n_instances": 1800}, "densepose_lvis_v1_val": {"n_instances": 1037}, "densepose_lvis_v1_val_animals_100": {"n_instances": 89}, } def generic_coco_test(self, dataset_info): if dataset_info.name not in self.COCO_DATASET_DATA: return n_inst = self.COCO_DATASET_DATA[dataset_info.name]["n_instances"] self.generic_test(dataset_info, n_inst, load_coco_json) def generic_lvis_test(self, dataset_info): if dataset_info.name not in self.LVIS_DATASET_DATA: return n_inst = self.LVIS_DATASET_DATA[dataset_info.name]["n_instances"] self.generic_test(dataset_info, n_inst, load_lvis_json) def generic_test(self, dataset_info, n_inst, loader_fun): datasets_root = DENSEPOSE_ANNOTATIONS_DIR annotations_fpath = maybe_prepend_base_path(datasets_root, dataset_info.annotations_fpath) images_root = maybe_prepend_base_path(datasets_root, dataset_info.images_root) image_annotation_dicts = loader_fun( annotations_json_file=annotations_fpath, image_root=images_root, dataset_name=dataset_info.name, ) num_valid = sum( 1 for image_annotation_dict in image_annotation_dicts for ann in image_annotation_dict["annotations"] if DensePoseDataRelative.validate_annotation(ann)[0] ) self.assertEqual(num_valid, n_inst) def coco_test_fun(dataset_info): return lambda self: self.generic_coco_test(dataset_info) for dataset_info in COCO_DATASETS: setattr( TestDatasetLoadedAnnotations, f"test_coco_builtin_loaded_annotations_{dataset_info.name}", coco_test_fun(dataset_info), ) def lvis_test_fun(dataset_info): return lambda self: self.generic_lvis_test(dataset_info) for dataset_info in LVIS_DATASETS: setattr( TestDatasetLoadedAnnotations, f"test_lvis_builtin_loaded_annotations_{dataset_info.name}", lvis_test_fun(dataset_info), ) ================================================ FILE: detectron2/projects/DensePose/tests/test_frame_selector.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import random import unittest from densepose.data.video import FirstKFramesSelector, LastKFramesSelector, RandomKFramesSelector class TestFrameSelector(unittest.TestCase): def test_frame_selector_random_k_1(self): _SEED = 43 _K = 4 random.seed(_SEED) selector = RandomKFramesSelector(_K) frame_tss = list(range(0, 20, 2)) _SELECTED_GT = [0, 8, 4, 6] selected = selector(frame_tss) self.assertEqual(_SELECTED_GT, selected) def test_frame_selector_random_k_2(self): _SEED = 43 _K = 10 random.seed(_SEED) selector = RandomKFramesSelector(_K) frame_tss = list(range(0, 6, 2)) _SELECTED_GT = [0, 2, 4] selected = selector(frame_tss) self.assertEqual(_SELECTED_GT, selected) def test_frame_selector_first_k_1(self): _K = 4 selector = FirstKFramesSelector(_K) frame_tss = list(range(0, 20, 2)) _SELECTED_GT = frame_tss[:_K] selected = selector(frame_tss) self.assertEqual(_SELECTED_GT, selected) def test_frame_selector_first_k_2(self): _K = 10 selector = FirstKFramesSelector(_K) frame_tss = list(range(0, 6, 2)) _SELECTED_GT = frame_tss[:_K] selected = selector(frame_tss) self.assertEqual(_SELECTED_GT, selected) def test_frame_selector_last_k_1(self): _K = 4 selector = LastKFramesSelector(_K) frame_tss = list(range(0, 20, 2)) _SELECTED_GT = frame_tss[-_K:] selected = selector(frame_tss) self.assertEqual(_SELECTED_GT, selected) def test_frame_selector_last_k_2(self): _K = 10 selector = LastKFramesSelector(_K) frame_tss = list(range(0, 6, 2)) _SELECTED_GT = frame_tss[-_K:] selected = selector(frame_tss) self.assertEqual(_SELECTED_GT, selected) ================================================ FILE: detectron2/projects/DensePose/tests/test_image_list_dataset.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import contextlib import os import tempfile import unittest import torch from torchvision.utils import save_image from densepose.data.image_list_dataset import ImageListDataset from densepose.data.transform import ImageResizeTransform @contextlib.contextmanager def temp_image(height, width): random_image = torch.rand(height, width) with tempfile.NamedTemporaryFile(suffix=".jpg") as f: f.close() save_image(random_image, f.name) yield f.name os.unlink(f.name) class TestImageListDataset(unittest.TestCase): def test_image_list_dataset(self): height, width = 720, 1280 with temp_image(height, width) as image_fpath: image_list = [image_fpath] category_list = [None] dataset = ImageListDataset(image_list, category_list) self.assertEqual(len(dataset), 1) data1, categories1 = dataset[0]["images"], dataset[0]["categories"] self.assertEqual(data1.shape, torch.Size((1, 3, height, width))) self.assertEqual(data1.dtype, torch.float32) self.assertIsNone(categories1[0]) def test_image_list_dataset_with_transform(self): height, width = 720, 1280 with temp_image(height, width) as image_fpath: image_list = [image_fpath] category_list = [None] transform = ImageResizeTransform() dataset = ImageListDataset(image_list, category_list, transform) self.assertEqual(len(dataset), 1) data1, categories1 = dataset[0]["images"], dataset[0]["categories"] self.assertEqual(data1.shape, torch.Size((1, 3, 749, 1333))) self.assertEqual(data1.dtype, torch.float32) self.assertIsNone(categories1[0]) ================================================ FILE: detectron2/projects/DensePose/tests/test_image_resize_transform.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest import torch from densepose.data.transform import ImageResizeTransform class TestImageResizeTransform(unittest.TestCase): def test_image_resize_1(self): images_batch = torch.ones((3, 3, 100, 100), dtype=torch.uint8) * 100 transform = ImageResizeTransform() images_transformed = transform(images_batch) IMAGES_GT = torch.ones((3, 3, 800, 800), dtype=torch.float) * 100 self.assertEqual(images_transformed.size(), IMAGES_GT.size()) self.assertAlmostEqual(torch.abs(IMAGES_GT - images_transformed).max().item(), 0.0) ================================================ FILE: detectron2/projects/DensePose/tests/test_model_e2e.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest import torch from detectron2.structures import BitMasks, Boxes, Instances from .common import get_model # TODO(plabatut): Modularize detectron2 tests and re-use def make_model_inputs(image, instances=None): if instances is None: return {"image": image} return {"image": image, "instances": instances} def make_empty_instances(h, w): instances = Instances((h, w)) instances.gt_boxes = Boxes(torch.rand(0, 4)) instances.gt_classes = torch.tensor([]).to(dtype=torch.int64) instances.gt_masks = BitMasks(torch.rand(0, h, w)) return instances class ModelE2ETest(unittest.TestCase): CONFIG_PATH = "" def setUp(self): self.model = get_model(self.CONFIG_PATH) def _test_eval(self, sizes): inputs = [make_model_inputs(torch.rand(3, size[0], size[1])) for size in sizes] self.model.eval() self.model(inputs) class DensePoseRCNNE2ETest(ModelE2ETest): CONFIG_PATH = "densepose_rcnn_R_101_FPN_s1x.yaml" def test_empty_data(self): self._test_eval([(200, 250), (200, 249)]) ================================================ FILE: detectron2/projects/DensePose/tests/test_setup.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest from .common import ( get_config_files, get_evolution_config_files, get_hrnet_config_files, get_quick_schedules_config_files, setup, ) class TestSetup(unittest.TestCase): def _test_setup(self, config_file): setup(config_file) def test_setup_configs(self): config_files = get_config_files() for config_file in config_files: self._test_setup(config_file) def test_setup_evolution_configs(self): config_files = get_evolution_config_files() for config_file in config_files: self._test_setup(config_file) def test_setup_hrnet_configs(self): config_files = get_hrnet_config_files() for config_file in config_files: self._test_setup(config_file) def test_setup_quick_schedules_configs(self): config_files = get_quick_schedules_config_files() for config_file in config_files: self._test_setup(config_file) ================================================ FILE: detectron2/projects/DensePose/tests/test_structures.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest from densepose.structures import normalized_coords_transform class TestStructures(unittest.TestCase): def test_normalized_coords_transform(self): bbox = (32, 24, 288, 216) x0, y0, w, h = bbox xmin, ymin, xmax, ymax = x0, y0, x0 + w, y0 + h f = normalized_coords_transform(*bbox) # Top-left expected_p, actual_p = (-1, -1), f((xmin, ymin)) self.assertEqual(expected_p, actual_p) # Top-right expected_p, actual_p = (1, -1), f((xmax, ymin)) self.assertEqual(expected_p, actual_p) # Bottom-left expected_p, actual_p = (-1, 1), f((xmin, ymax)) self.assertEqual(expected_p, actual_p) # Bottom-right expected_p, actual_p = (1, 1), f((xmax, ymax)) self.assertEqual(expected_p, actual_p) ================================================ FILE: detectron2/projects/DensePose/tests/test_tensor_storage.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import io import tempfile import unittest from contextlib import ExitStack import torch import torch.distributed as dist import torch.multiprocessing as mp from detectron2.utils import comm from densepose.evaluation.tensor_storage import ( SingleProcessFileTensorStorage, SingleProcessRamTensorStorage, SizeData, storage_gather, ) class TestSingleProcessRamTensorStorage(unittest.TestCase): def test_read_write_1(self): schema = { "tf": SizeData(dtype="float32", shape=(112, 112)), "ti": SizeData(dtype="int32", shape=(4, 64, 64)), } # generate data which corresponds to the schema data_elts = [] torch.manual_seed(23) for _i in range(3): data_elt = { "tf": torch.rand((112, 112), dtype=torch.float32), "ti": (torch.rand(4, 64, 64) * 1000).to(dtype=torch.int32), } data_elts.append(data_elt) storage = SingleProcessRamTensorStorage(schema, io.BytesIO()) # write data to the storage for i in range(3): record_id = storage.put(data_elts[i]) self.assertEqual(record_id, i) # read data from the storage for i in range(3): record = storage.get(i) self.assertEqual(len(record), len(schema)) for field_name in schema: self.assertTrue(field_name in record) self.assertEqual(data_elts[i][field_name].shape, record[field_name].shape) self.assertEqual(data_elts[i][field_name].dtype, record[field_name].dtype) self.assertTrue(torch.allclose(data_elts[i][field_name], record[field_name])) class TestSingleProcessFileTensorStorage(unittest.TestCase): def test_read_write_1(self): schema = { "tf": SizeData(dtype="float32", shape=(112, 112)), "ti": SizeData(dtype="int32", shape=(4, 64, 64)), } # generate data which corresponds to the schema data_elts = [] torch.manual_seed(23) for _i in range(3): data_elt = { "tf": torch.rand((112, 112), dtype=torch.float32), "ti": (torch.rand(4, 64, 64) * 1000).to(dtype=torch.int32), } data_elts.append(data_elt) # WARNING: opens the file several times! may not work on all platforms with tempfile.NamedTemporaryFile() as hFile: storage = SingleProcessFileTensorStorage(schema, hFile.name, "wb") # write data to the storage for i in range(3): record_id = storage.put(data_elts[i]) self.assertEqual(record_id, i) hFile.seek(0) storage = SingleProcessFileTensorStorage(schema, hFile.name, "rb") # read data from the storage for i in range(3): record = storage.get(i) self.assertEqual(len(record), len(schema)) for field_name in schema: self.assertTrue(field_name in record) self.assertEqual(data_elts[i][field_name].shape, record[field_name].shape) self.assertEqual(data_elts[i][field_name].dtype, record[field_name].dtype) self.assertTrue(torch.allclose(data_elts[i][field_name], record[field_name])) def _find_free_port(): """ Copied from detectron2/engine/launch.py """ import socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Binding to port 0 will cause the OS to find an available port for us sock.bind(("", 0)) port = sock.getsockname()[1] sock.close() # NOTE: there is still a chance the port could be taken by other processes. return port def launch(main_func, nprocs, args=()): port = _find_free_port() dist_url = f"tcp://127.0.0.1:{port}" # dist_url = "env://" mp.spawn( distributed_worker, nprocs=nprocs, args=(main_func, nprocs, dist_url, args), daemon=False ) def distributed_worker(local_rank, main_func, nprocs, dist_url, args): dist.init_process_group( backend="gloo", init_method=dist_url, world_size=nprocs, rank=local_rank ) comm.synchronize() assert comm._LOCAL_PROCESS_GROUP is None pg = dist.new_group(list(range(nprocs))) comm._LOCAL_PROCESS_GROUP = pg main_func(*args) def ram_read_write_worker(): schema = { "tf": SizeData(dtype="float32", shape=(112, 112)), "ti": SizeData(dtype="int32", shape=(4, 64, 64)), } storage = SingleProcessRamTensorStorage(schema, io.BytesIO()) world_size = comm.get_world_size() rank = comm.get_rank() data_elts = [] # prepare different number of tensors in different processes for i in range(rank + 1): data_elt = { "tf": torch.ones((112, 112), dtype=torch.float32) * (rank + i * world_size), "ti": torch.ones((4, 64, 64), dtype=torch.int32) * (rank + i * world_size), } data_elts.append(data_elt) # write data to the single process storage for i in range(rank + 1): record_id = storage.put(data_elts[i]) assert record_id == i, f"Process {rank}: record ID {record_id}, expected {i}" comm.synchronize() # gather all data in process rank 0 multi_storage = storage_gather(storage) if rank != 0: return # read and check data from the multiprocess storage for j in range(world_size): for i in range(j): record = multi_storage.get(j, i) record_gt = { "tf": torch.ones((112, 112), dtype=torch.float32) * (j + i * world_size), "ti": torch.ones((4, 64, 64), dtype=torch.int32) * (j + i * world_size), } assert len(record) == len(schema), ( f"Process {rank}: multi storage record, rank {j}, id {i}: " f"expected {len(schema)} fields in the record, got {len(record)}" ) for field_name in schema: assert field_name in record, ( f"Process {rank}: multi storage record, rank {j}, id {i}: " f"field {field_name} not in the record" ) assert record_gt[field_name].shape == record[field_name].shape, ( f"Process {rank}: multi storage record, rank {j}, id {i}: " f"field {field_name}, expected shape {record_gt[field_name].shape} " f"got {record[field_name].shape}" ) assert record_gt[field_name].dtype == record[field_name].dtype, ( f"Process {rank}: multi storage record, rank {j}, id {i}: " f"field {field_name}, expected dtype {record_gt[field_name].dtype} " f"got {record[field_name].dtype}" ) assert torch.allclose(record_gt[field_name], record[field_name]), ( f"Process {rank}: multi storage record, rank {j}, id {i}: " f"field {field_name}, tensors are not close enough:" f"L-inf {(record_gt[field_name]-record[field_name]).abs_().max()} " f"L1 {(record_gt[field_name]-record[field_name]).abs_().sum()} " ) def file_read_write_worker(rank_to_fpath): schema = { "tf": SizeData(dtype="float32", shape=(112, 112)), "ti": SizeData(dtype="int32", shape=(4, 64, 64)), } world_size = comm.get_world_size() rank = comm.get_rank() storage = SingleProcessFileTensorStorage(schema, rank_to_fpath[rank], "wb") data_elts = [] # prepare different number of tensors in different processes for i in range(rank + 1): data_elt = { "tf": torch.ones((112, 112), dtype=torch.float32) * (rank + i * world_size), "ti": torch.ones((4, 64, 64), dtype=torch.int32) * (rank + i * world_size), } data_elts.append(data_elt) # write data to the single process storage for i in range(rank + 1): record_id = storage.put(data_elts[i]) assert record_id == i, f"Process {rank}: record ID {record_id}, expected {i}" comm.synchronize() # gather all data in process rank 0 multi_storage = storage_gather(storage) if rank != 0: return # read and check data from the multiprocess storage for j in range(world_size): for i in range(j): record = multi_storage.get(j, i) record_gt = { "tf": torch.ones((112, 112), dtype=torch.float32) * (j + i * world_size), "ti": torch.ones((4, 64, 64), dtype=torch.int32) * (j + i * world_size), } assert len(record) == len(schema), ( f"Process {rank}: multi storage record, rank {j}, id {i}: " f"expected {len(schema)} fields in the record, got {len(record)}" ) for field_name in schema: assert field_name in record, ( f"Process {rank}: multi storage record, rank {j}, id {i}: " f"field {field_name} not in the record" ) assert record_gt[field_name].shape == record[field_name].shape, ( f"Process {rank}: multi storage record, rank {j}, id {i}: " f"field {field_name}, expected shape {record_gt[field_name].shape} " f"got {record[field_name].shape}" ) assert record_gt[field_name].dtype == record[field_name].dtype, ( f"Process {rank}: multi storage record, rank {j}, id {i}: " f"field {field_name}, expected dtype {record_gt[field_name].dtype} " f"got {record[field_name].dtype}" ) assert torch.allclose(record_gt[field_name], record[field_name]), ( f"Process {rank}: multi storage record, rank {j}, id {i}: " f"field {field_name}, tensors are not close enough:" f"L-inf {(record_gt[field_name]-record[field_name]).abs_().max()} " f"L1 {(record_gt[field_name]-record[field_name]).abs_().sum()} " ) class TestMultiProcessRamTensorStorage(unittest.TestCase): def test_read_write_1(self): launch(ram_read_write_worker, 8) class TestMultiProcessFileTensorStorage(unittest.TestCase): def test_read_write_1(self): with ExitStack() as stack: # WARNING: opens the files several times! may not work on all platforms rank_to_fpath = { i: stack.enter_context(tempfile.NamedTemporaryFile()).name for i in range(8) } launch(file_read_write_worker, 8, (rank_to_fpath,)) ================================================ FILE: detectron2/projects/DensePose/tests/test_video_keyframe_dataset.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import contextlib import os import random import tempfile import unittest import torch import torchvision.io as io from densepose.data.transform import ImageResizeTransform from densepose.data.video import RandomKFramesSelector, VideoKeyframeDataset try: import av except ImportError: av = None # copied from torchvision test/test_io.py def _create_video_frames(num_frames, height, width): y, x = torch.meshgrid(torch.linspace(-2, 2, height), torch.linspace(-2, 2, width)) data = [] for i in range(num_frames): xc = float(i) / num_frames yc = 1 - float(i) / (2 * num_frames) d = torch.exp(-((x - xc) ** 2 + (y - yc) ** 2) / 2) * 255 data.append(d.unsqueeze(2).repeat(1, 1, 3).byte()) return torch.stack(data, 0) # adapted from torchvision test/test_io.py @contextlib.contextmanager def temp_video(num_frames, height, width, fps, lossless=False, video_codec=None, options=None): if lossless: if video_codec is not None: raise ValueError("video_codec can't be specified together with lossless") if options is not None: raise ValueError("options can't be specified together with lossless") video_codec = "libx264rgb" options = {"crf": "0"} if video_codec is None: video_codec = "libx264" if options is None: options = {} data = _create_video_frames(num_frames, height, width) with tempfile.NamedTemporaryFile(suffix=".mp4") as f: f.close() io.write_video(f.name, data, fps=fps, video_codec=video_codec, options=options) yield f.name, data os.unlink(f.name) @unittest.skipIf(av is None, "PyAV unavailable") class TestVideoKeyframeDataset(unittest.TestCase): def test_read_keyframes_all(self): with temp_video(60, 300, 300, 5, video_codec="mpeg4") as (fname, data): video_list = [fname] category_list = [None] dataset = VideoKeyframeDataset(video_list, category_list) self.assertEqual(len(dataset), 1) data1, categories1 = dataset[0]["images"], dataset[0]["categories"] self.assertEqual(data1.shape, torch.Size((5, 3, 300, 300))) self.assertEqual(data1.dtype, torch.float32) self.assertIsNone(categories1[0]) return self.assertTrue(False) def test_read_keyframes_with_selector(self): with temp_video(60, 300, 300, 5, video_codec="mpeg4") as (fname, data): video_list = [fname] category_list = [None] random.seed(0) frame_selector = RandomKFramesSelector(3) dataset = VideoKeyframeDataset(video_list, category_list, frame_selector) self.assertEqual(len(dataset), 1) data1, categories1 = dataset[0]["images"], dataset[0]["categories"] self.assertEqual(data1.shape, torch.Size((3, 3, 300, 300))) self.assertEqual(data1.dtype, torch.float32) self.assertIsNone(categories1[0]) return self.assertTrue(False) def test_read_keyframes_with_selector_with_transform(self): with temp_video(60, 300, 300, 5, video_codec="mpeg4") as (fname, data): video_list = [fname] category_list = [None] random.seed(0) frame_selector = RandomKFramesSelector(1) transform = ImageResizeTransform() dataset = VideoKeyframeDataset(video_list, category_list, frame_selector, transform) data1, categories1 = dataset[0]["images"], dataset[0]["categories"] self.assertEqual(len(dataset), 1) self.assertEqual(data1.shape, torch.Size((1, 3, 800, 800))) self.assertEqual(data1.dtype, torch.float32) self.assertIsNone(categories1[0]) return self.assertTrue(False) ================================================ FILE: detectron2/projects/DensePose/train_net.py ================================================ #!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. """ DensePose Training Script. This script is similar to the training script in detectron2/tools. It is an example of how a user might use detectron2 for a new project. """ from datetime import timedelta import detectron2.utils.comm as comm from detectron2.config import get_cfg from detectron2.engine import DEFAULT_TIMEOUT, default_argument_parser, default_setup, hooks, launch from detectron2.evaluation import verify_results from detectron2.utils.file_io import PathManager from detectron2.utils.logger import setup_logger from densepose import add_densepose_config from densepose.engine import Trainer from densepose.modeling.densepose_checkpoint import DensePoseCheckpointer def setup(args): cfg = get_cfg() add_densepose_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.freeze() default_setup(cfg, args) # Setup logger for "densepose" module setup_logger(output=cfg.OUTPUT_DIR, distributed_rank=comm.get_rank(), name="densepose") return cfg def main(args): cfg = setup(args) # disable strict kwargs checking: allow one to specify path handle # hints through kwargs, like timeout in DP evaluation PathManager.set_strict_kwargs_checking(False) if args.eval_only: model = Trainer.build_model(cfg) DensePoseCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load( cfg.MODEL.WEIGHTS, resume=args.resume ) res = Trainer.test(cfg, model) if cfg.TEST.AUG.ENABLED: res.update(Trainer.test_with_TTA(cfg, model)) if comm.is_main_process(): verify_results(cfg, res) return res trainer = Trainer(cfg) trainer.resume_or_load(resume=args.resume) if cfg.TEST.AUG.ENABLED: trainer.register_hooks( [hooks.EvalHook(0, lambda: trainer.test_with_TTA(cfg, trainer.model))] ) return trainer.train() if __name__ == "__main__": args = default_argument_parser().parse_args() cfg = setup(args) timeout = ( DEFAULT_TIMEOUT if cfg.DENSEPOSE_EVALUATION.DISTRIBUTED_INFERENCE else timedelta(hours=4) ) print("Command Line Args:", args) launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), timeout=timeout, ) ================================================ FILE: detectron2/projects/MViTv2/README.md ================================================ # MViTv2: Improved Multiscale Vision Transformers for Classification and Detection Yanghao Li*, Chao-Yuan Wu*, Haoqi Fan, Karttikeya Mangalam, Bo Xiong, Jitendra Malik, Christoph Feichtenhofer* [[`arXiv`](https://arxiv.org/abs/2112.01526)] [[`BibTeX`](#CitingMViTv2)] In this repository, we provide detection configs and models for MViTv2 (CVPR 2022) in Detectron2. For image classification tasks, please refer to [MViTv2 repo](https://github.com/facebookresearch/mvit). ## Results and Pretrained Models ### COCO
Name pre-train Method epochs box
AP
mask
AP
#params FLOPS model id download
MViTV2-T IN1K Mask R-CNN 36 48.3 43.8 44M 279G 307611773 model
MViTV2-T IN1K Cascade Mask R-CNN 36 52.2 45.0 76M 701G 308344828 model
MViTV2-S IN1K Cascade Mask R-CNN 36 53.2 46.0 87M 748G 308344647 model
MViTV2-B IN1K Cascade Mask R-CNN 36 54.1 46.7 103M 814G 308109448 model
MViTV2-B IN21K Cascade Mask R-CNN 36 54.9 47.4 103M 814G 309003202 model
MViTV2-L IN21K Cascade Mask R-CNN 50 55.8 48.3 270M 1519G 308099658 model
MViTV2-H IN21K Cascade Mask R-CNN 36 56.1 48.5 718M 3084G 309013744 model
Note that the above models were trained and measured on 8-node with 64 NVIDIA A100 GPUs in total. The ImageNet pre-trained model weights are obtained from [MViTv2 repo](https://github.com/facebookresearch/mvit). ## Training All configs can be trained with: ``` ../../tools/lazyconfig_train_net.py --config-file configs/path/to/config.py ``` By default, we use 64 GPUs with batch size as 64 for training. ## Evaluation Model evaluation can be done similarly: ``` ../../tools/lazyconfig_train_net.py --config-file configs/path/to/config.py --eval-only train.init_checkpoint=/path/to/model_checkpoint ``` ## Citing MViTv2 If you use MViTv2, please use the following BibTeX entry. ```BibTeX @inproceedings{li2021improved, title={MViTv2: Improved multiscale vision transformers for classification and detection}, author={Li, Yanghao and Wu, Chao-Yuan and Fan, Haoqi and Mangalam, Karttikeya and Xiong, Bo and Malik, Jitendra and Feichtenhofer, Christoph}, booktitle={CVPR}, year={2022} } ``` ================================================ FILE: detectron2/projects/MViTv2/configs/cascade_mask_rcnn_mvitv2_b_3x.py ================================================ from .cascade_mask_rcnn_mvitv2_t_3x import model, dataloader, optimizer, lr_multiplier, train model.backbone.bottom_up.depth = 24 model.backbone.bottom_up.last_block_indexes = (1, 4, 20, 23) model.backbone.bottom_up.drop_path_rate = 0.4 train.init_checkpoint = "detectron2://ImageNetPretrained/mvitv2/MViTv2_B_in1k.pyth" ================================================ FILE: detectron2/projects/MViTv2/configs/cascade_mask_rcnn_mvitv2_b_in21k_3x.py ================================================ from .cascade_mask_rcnn_mvitv2_b_3x import model, dataloader, optimizer, lr_multiplier, train train.init_checkpoint = "detectron2://ImageNetPretrained/mvitv2/MViTv2_B_in21k.pyth" ================================================ FILE: detectron2/projects/MViTv2/configs/cascade_mask_rcnn_mvitv2_h_in21k_lsj_3x.py ================================================ from .cascade_mask_rcnn_mvitv2_b_3x import model, optimizer, train, lr_multiplier from .common.coco_loader_lsj import dataloader model.backbone.bottom_up.embed_dim = 192 model.backbone.bottom_up.depth = 80 model.backbone.bottom_up.num_heads = 3 model.backbone.bottom_up.last_block_indexes = (3, 11, 71, 79) model.backbone.bottom_up.drop_path_rate = 0.6 model.backbone.bottom_up.use_act_checkpoint = True train.init_checkpoint = "detectron2://ImageNetPretrained/mvitv2/MViTv2_H_in21k.pyth" ================================================ FILE: detectron2/projects/MViTv2/configs/cascade_mask_rcnn_mvitv2_l_in21k_lsj_50ep.py ================================================ from fvcore.common.param_scheduler import MultiStepParamScheduler from detectron2.config import LazyCall as L from detectron2.solver import WarmupParamScheduler from .cascade_mask_rcnn_mvitv2_b_3x import model, optimizer, train from .common.coco_loader_lsj import dataloader model.backbone.bottom_up.embed_dim = 144 model.backbone.bottom_up.depth = 48 model.backbone.bottom_up.num_heads = 2 model.backbone.bottom_up.last_block_indexes = (1, 7, 43, 47) model.backbone.bottom_up.drop_path_rate = 0.5 train.init_checkpoint = "detectron2://ImageNetPretrained/mvitv2/MViTv2_L_in21k.pyth" # Schedule # 50ep = 184375 // 2 iters * 64 images/iter / 118000 images/ep train.max_iter = 184375 // 2 lr_multiplier = L(WarmupParamScheduler)( scheduler=L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], milestones=[163889 // 2, 177546 // 2], num_updates=train.max_iter, ), warmup_length=250 / train.max_iter, warmup_factor=0.001, ) optimizer.lr = 1e-4 ================================================ FILE: detectron2/projects/MViTv2/configs/cascade_mask_rcnn_mvitv2_s_3x.py ================================================ from .cascade_mask_rcnn_mvitv2_t_3x import model, dataloader, optimizer, lr_multiplier, train model.backbone.bottom_up.depth = 16 model.backbone.bottom_up.last_block_indexes = (0, 2, 13, 15) train.init_checkpoint = "detectron2://ImageNetPretrained/mvitv2/MViTv2_S_in1k.pyth" ================================================ FILE: detectron2/projects/MViTv2/configs/cascade_mask_rcnn_mvitv2_t_3x.py ================================================ from detectron2.config import LazyCall as L from detectron2.layers import ShapeSpec from detectron2.modeling.box_regression import Box2BoxTransform from detectron2.modeling.matcher import Matcher from detectron2.modeling.roi_heads import FastRCNNOutputLayers, FastRCNNConvFCHead, CascadeROIHeads from detectron2.layers.batch_norm import NaiveSyncBatchNorm from .mask_rcnn_mvitv2_t_3x import model, dataloader, optimizer, lr_multiplier, train # arguments that don't exist for Cascade R-CNN [model.roi_heads.pop(k) for k in ["box_head", "box_predictor", "proposal_matcher"]] model.roi_heads.update( _target_=CascadeROIHeads, box_heads=[ L(FastRCNNConvFCHead)( input_shape=ShapeSpec(channels=256, height=7, width=7), conv_dims=[256, 256, 256, 256], fc_dims=[1024], conv_norm=lambda c: NaiveSyncBatchNorm(c, stats_mode="N"), ) for _ in range(3) ], box_predictors=[ L(FastRCNNOutputLayers)( input_shape=ShapeSpec(channels=1024), test_score_thresh=0.05, box2box_transform=L(Box2BoxTransform)(weights=(w1, w1, w2, w2)), cls_agnostic_bbox_reg=True, num_classes="${...num_classes}", ) for (w1, w2) in [(10, 5), (20, 10), (30, 15)] ], proposal_matchers=[ L(Matcher)(thresholds=[th], labels=[0, 1], allow_low_quality_matches=False) for th in [0.5, 0.6, 0.7] ], ) # Using NaiveSyncBatchNorm becase heads may have empty input. That is not supported by # torch.nn.SyncBatchNorm. We can remove this after # https://github.com/pytorch/pytorch/issues/36530 is fixed. model.roi_heads.mask_head.conv_norm = lambda c: NaiveSyncBatchNorm(c, stats_mode="N") # 2conv in RPN: # https://github.com/tensorflow/tpu/blob/b24729de804fdb751b06467d3dce0637fa652060/models/official/detection/modeling/architecture/heads.py#L95-L97 # noqa: E501, B950 model.proposal_generator.head.conv_dims = [-1, -1] ================================================ FILE: detectron2/projects/MViTv2/configs/common/coco_loader.py ================================================ from omegaconf import OmegaConf import detectron2.data.transforms as T from detectron2.config import LazyCall as L from detectron2.data import ( DatasetMapper, build_detection_test_loader, build_detection_train_loader, get_detection_dataset_dicts, ) from detectron2.evaluation import COCOEvaluator dataloader = OmegaConf.create() dataloader.train = L(build_detection_train_loader)( dataset=L(get_detection_dataset_dicts)(names="coco_2017_train"), mapper=L(DatasetMapper)( is_train=True, augmentations=[ L(T.RandomApply)( tfm_or_aug=L(T.AugmentationList)( augs=[ L(T.ResizeShortestEdge)( short_edge_length=[400, 500, 600], sample_style="choice" ), L(T.RandomCrop)(crop_type="absolute_range", crop_size=(384, 600)), ] ), prob=0.5, ), L(T.ResizeShortestEdge)( short_edge_length=(480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800), sample_style="choice", max_size=1333, ), L(T.RandomFlip)(horizontal=True), ], image_format="RGB", use_instance_mask=True, ), total_batch_size=16, num_workers=4, ) dataloader.test = L(build_detection_test_loader)( dataset=L(get_detection_dataset_dicts)(names="coco_2017_val", filter_empty=False), mapper=L(DatasetMapper)( is_train=False, augmentations=[ L(T.ResizeShortestEdge)(short_edge_length=800, max_size=1333), ], image_format="${...train.mapper.image_format}", ), num_workers=4, ) dataloader.evaluator = L(COCOEvaluator)( dataset_name="${..test.dataset.names}", ) ================================================ FILE: detectron2/projects/MViTv2/configs/common/coco_loader_lsj.py ================================================ import detectron2.data.transforms as T from detectron2 import model_zoo from detectron2.config import LazyCall as L from .coco_loader import dataloader # Data using LSJ image_size = 1024 dataloader.train.mapper.augmentations = [ L(T.RandomFlip)(horizontal=True), # flip first L(T.ResizeScale)( min_scale=0.1, max_scale=2.0, target_height=image_size, target_width=image_size ), L(T.FixedSizeCrop)(crop_size=(image_size, image_size)), ] dataloader.train.mapper.image_format = "RGB" dataloader.train.total_batch_size = 64 # recompute boxes due to cropping dataloader.train.mapper.recompute_boxes = True ================================================ FILE: detectron2/projects/MViTv2/configs/mask_rcnn_mvitv2_t_3x.py ================================================ from functools import partial import torch.nn as nn from fvcore.common.param_scheduler import MultiStepParamScheduler from detectron2 import model_zoo from detectron2.config import LazyCall as L from detectron2.solver import WarmupParamScheduler from detectron2.modeling import MViT from .common.coco_loader import dataloader model = model_zoo.get_config("common/models/mask_rcnn_fpn.py").model constants = model_zoo.get_config("common/data/constants.py").constants model.pixel_mean = constants.imagenet_rgb256_mean model.pixel_std = constants.imagenet_rgb256_std model.input_format = "RGB" model.backbone.bottom_up = L(MViT)( embed_dim=96, depth=10, num_heads=1, last_block_indexes=(0, 2, 7, 9), residual_pooling=True, drop_path_rate=0.2, norm_layer=partial(nn.LayerNorm, eps=1e-6), out_features=("scale2", "scale3", "scale4", "scale5"), ) model.backbone.in_features = "${.bottom_up.out_features}" # Initialization and trainer settings train = model_zoo.get_config("common/train.py").train train.amp.enabled = True train.ddp.fp16_compression = True train.init_checkpoint = "detectron2://ImageNetPretrained/mvitv2/MViTv2_T_in1k.pyth" dataloader.train.total_batch_size = 64 # 36 epochs train.max_iter = 67500 lr_multiplier = L(WarmupParamScheduler)( scheduler=L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], milestones=[52500, 62500, 67500], ), warmup_length=250 / train.max_iter, warmup_factor=0.001, ) optimizer = model_zoo.get_config("common/optim.py").AdamW optimizer.params.overrides = { "pos_embed": {"weight_decay": 0.0}, "rel_pos_h": {"weight_decay": 0.0}, "rel_pos_w": {"weight_decay": 0.0}, } optimizer.lr = 1.6e-4 ================================================ FILE: detectron2/projects/Panoptic-DeepLab/README.md ================================================ # Panoptic-DeepLab: A Simple, Strong, and Fast Baseline for Bottom-Up Panoptic Segmentation Bowen Cheng, Maxwell D. Collins, Yukun Zhu, Ting Liu, Thomas S. Huang, Hartwig Adam, Liang-Chieh Chen [[`arXiv`](https://arxiv.org/abs/1911.10194)] [[`BibTeX`](#CitingPanopticDeepLab)] [[`Reference implementation`](https://github.com/bowenc0221/panoptic-deeplab)]

## Installation Install Detectron2 following [the instructions](https://detectron2.readthedocs.io/tutorials/install.html). To use cityscapes, prepare data follow the [tutorial](https://detectron2.readthedocs.io/tutorials/builtin_datasets.html#expected-dataset-structure-for-cityscapes). ## Training To train a model with 8 GPUs run: ```bash cd /path/to/detectron2/projects/Panoptic-DeepLab python train_net.py --config-file configs/Cityscapes-PanopticSegmentation/panoptic_deeplab_R_52_os16_mg124_poly_90k_bs32_crop_512_1024_dsconv.yaml --num-gpus 8 ``` ## Evaluation Model evaluation can be done similarly: ```bash cd /path/to/detectron2/projects/Panoptic-DeepLab python train_net.py --config-file configs/Cityscapes-PanopticSegmentation/panoptic_deeplab_R_52_os16_mg124_poly_90k_bs32_crop_512_1024_dsconv.yaml --eval-only MODEL.WEIGHTS /path/to/model_checkpoint ``` ## Benchmark network speed If you want to benchmark the network speed without post-processing, you can run the evaluation script with `MODEL.PANOPTIC_DEEPLAB.BENCHMARK_NETWORK_SPEED True`: ```bash cd /path/to/detectron2/projects/Panoptic-DeepLab python train_net.py --config-file configs/Cityscapes-PanopticSegmentation/panoptic_deeplab_R_52_os16_mg124_poly_90k_bs32_crop_512_1024_dsconv.yaml --eval-only MODEL.WEIGHTS /path/to/model_checkpoint MODEL.PANOPTIC_DEEPLAB.BENCHMARK_NETWORK_SPEED True ``` ## Cityscapes Panoptic Segmentation Cityscapes models are trained with ImageNet pretraining.
Method Backbone Output
resolution
PQ SQ RQ mIoU AP Memory (M) model id download
Panoptic-DeepLab R50-DC5 1024×2048 58.6 80.9 71.2 75.9 29.8 8668 - model | metrics
Panoptic-DeepLab R52-DC5 1024×2048 60.3 81.5 72.9 78.2 33.2 9682 30841561 model | metrics
Panoptic-DeepLab (DSConv) R52-DC5 1024×2048 60.3 81.0 73.2 78.7 32.1 10466 33148034 model | metrics
Note: - [R52](https://dl.fbaipublicfiles.com/detectron2/DeepLab/R-52.pkl): a ResNet-50 with its first 7x7 convolution replaced by 3 3x3 convolutions. This modification has been used in most semantic segmentation papers. We pre-train this backbone on ImageNet using the default recipe of [pytorch examples](https://github.com/pytorch/examples/tree/master/imagenet). - DC5 means using dilated convolution in `res5`. - We use a smaller training crop size (512x1024) than the original paper (1025x2049), we find using larger crop size (1024x2048) could further improve PQ by 1.5% but also degrades AP by 3%. - The implementation with regular Conv2d in ASPP and head is much heavier head than the original paper. - This implementation does not include optimized post-processing code needed for deployment. Post-processing the network outputs now takes similar amount of time to the network itself. Please refer to speed in the original paper for comparison. - DSConv refers to using DepthwiseSeparableConv2d in ASPP and decoder. The implementation with DSConv is identical to the original paper. ## COCO Panoptic Segmentation COCO models are trained with ImageNet pretraining on 16 V100s.
Method Backbone Output
resolution
PQ SQ RQ Box AP Mask AP Memory (M) model id download
Panoptic-DeepLab (DSConv) R52-DC5 640×640 35.5 77.3 44.7 18.6 19.7 246448865 model | metrics
Note: - [R52](https://dl.fbaipublicfiles.com/detectron2/DeepLab/R-52.pkl): a ResNet-50 with its first 7x7 convolution replaced by 3 3x3 convolutions. This modification has been used in most semantic segmentation papers. We pre-train this backbone on ImageNet using the default recipe of [pytorch examples](https://github.com/pytorch/examples/tree/master/imagenet). - DC5 means using dilated convolution in `res5`. - This reproduced number matches the original paper (35.5 vs. 35.1 PQ). - This implementation does not include optimized post-processing code needed for deployment. Post-processing the network outputs now takes more time than the network itself. Please refer to speed in the original paper for comparison. - DSConv refers to using DepthwiseSeparableConv2d in ASPP and decoder. ## Citing Panoptic-DeepLab If you use Panoptic-DeepLab, please use the following BibTeX entry. * CVPR 2020 paper: ``` @inproceedings{cheng2020panoptic, title={Panoptic-DeepLab: A Simple, Strong, and Fast Baseline for Bottom-Up Panoptic Segmentation}, author={Cheng, Bowen and Collins, Maxwell D and Zhu, Yukun and Liu, Ting and Huang, Thomas S and Adam, Hartwig and Chen, Liang-Chieh}, booktitle={CVPR}, year={2020} } ``` * ICCV 2019 COCO-Mapillary workshp challenge report: ``` @inproceedings{cheng2019panoptic, title={Panoptic-DeepLab}, author={Cheng, Bowen and Collins, Maxwell D and Zhu, Yukun and Liu, Ting and Huang, Thomas S and Adam, Hartwig and Chen, Liang-Chieh}, booktitle={ICCV COCO + Mapillary Joint Recognition Challenge Workshop}, year={2019} } ``` ================================================ FILE: detectron2/projects/Panoptic-DeepLab/configs/COCO-PanopticSegmentation/panoptic_deeplab_R_52_os16_mg124_poly_200k_bs64_crop_640_640_coco_dsconv.yaml ================================================ _BASE_: ../Cityscapes-PanopticSegmentation/Base-PanopticDeepLab-OS16.yaml MODEL: WEIGHTS: "detectron2://DeepLab/R-52.pkl" PIXEL_MEAN: [123.675, 116.280, 103.530] PIXEL_STD: [58.395, 57.120, 57.375] BACKBONE: NAME: "build_resnet_deeplab_backbone" RESNETS: DEPTH: 50 NORM: "SyncBN" RES5_MULTI_GRID: [1, 2, 4] STEM_TYPE: "deeplab" STEM_OUT_CHANNELS: 128 STRIDE_IN_1X1: False SEM_SEG_HEAD: NUM_CLASSES: 133 LOSS_TOP_K: 1.0 USE_DEPTHWISE_SEPARABLE_CONV: True PANOPTIC_DEEPLAB: STUFF_AREA: 4096 NMS_KERNEL: 41 SIZE_DIVISIBILITY: 640 USE_DEPTHWISE_SEPARABLE_CONV: True DATASETS: TRAIN: ("coco_2017_train_panoptic",) TEST: ("coco_2017_val_panoptic",) SOLVER: BASE_LR: 0.0005 MAX_ITER: 200000 IMS_PER_BATCH: 64 INPUT: FORMAT: "RGB" GAUSSIAN_SIGMA: 8 MIN_SIZE_TRAIN: !!python/object/apply:eval ["[int(x * 0.1 * 640) for x in range(5, 16)]"] MIN_SIZE_TRAIN_SAMPLING: "choice" MIN_SIZE_TEST: 640 MAX_SIZE_TRAIN: 960 MAX_SIZE_TEST: 640 CROP: ENABLED: True TYPE: "absolute" SIZE: (640, 640) ================================================ FILE: detectron2/projects/Panoptic-DeepLab/configs/Cityscapes-PanopticSegmentation/Base-PanopticDeepLab-OS16.yaml ================================================ MODEL: META_ARCHITECTURE: "PanopticDeepLab" BACKBONE: FREEZE_AT: 0 RESNETS: OUT_FEATURES: ["res2", "res3", "res5"] RES5_DILATION: 2 SEM_SEG_HEAD: NAME: "PanopticDeepLabSemSegHead" IN_FEATURES: ["res2", "res3", "res5"] PROJECT_FEATURES: ["res2", "res3"] PROJECT_CHANNELS: [32, 64] ASPP_CHANNELS: 256 ASPP_DILATIONS: [6, 12, 18] ASPP_DROPOUT: 0.1 HEAD_CHANNELS: 256 CONVS_DIM: 256 COMMON_STRIDE: 4 NUM_CLASSES: 19 LOSS_TYPE: "hard_pixel_mining" NORM: "SyncBN" INS_EMBED_HEAD: NAME: "PanopticDeepLabInsEmbedHead" IN_FEATURES: ["res2", "res3", "res5"] PROJECT_FEATURES: ["res2", "res3"] PROJECT_CHANNELS: [32, 64] ASPP_CHANNELS: 256 ASPP_DILATIONS: [6, 12, 18] ASPP_DROPOUT: 0.1 HEAD_CHANNELS: 32 CONVS_DIM: 128 COMMON_STRIDE: 4 NORM: "SyncBN" CENTER_LOSS_WEIGHT: 200.0 OFFSET_LOSS_WEIGHT: 0.01 PANOPTIC_DEEPLAB: STUFF_AREA: 2048 CENTER_THRESHOLD: 0.1 NMS_KERNEL: 7 TOP_K_INSTANCE: 200 DATASETS: TRAIN: ("cityscapes_fine_panoptic_train",) TEST: ("cityscapes_fine_panoptic_val",) SOLVER: OPTIMIZER: "ADAM" BASE_LR: 0.001 WEIGHT_DECAY: 0.0 WEIGHT_DECAY_NORM: 0.0 WEIGHT_DECAY_BIAS: 0.0 MAX_ITER: 60000 LR_SCHEDULER_NAME: "WarmupPolyLR" IMS_PER_BATCH: 32 INPUT: MIN_SIZE_TRAIN: (512, 640, 704, 832, 896, 1024, 1152, 1216, 1344, 1408, 1536, 1664, 1728, 1856, 1920, 2048) MIN_SIZE_TRAIN_SAMPLING: "choice" MIN_SIZE_TEST: 1024 MAX_SIZE_TRAIN: 4096 MAX_SIZE_TEST: 2048 CROP: ENABLED: True TYPE: "absolute" SIZE: (1024, 2048) DATALOADER: NUM_WORKERS: 10 VERSION: 2 ================================================ FILE: detectron2/projects/Panoptic-DeepLab/configs/Cityscapes-PanopticSegmentation/panoptic_deeplab_R_52_os16_mg124_poly_90k_bs32_crop_512_1024.yaml ================================================ _BASE_: Base-PanopticDeepLab-OS16.yaml MODEL: WEIGHTS: "detectron2://DeepLab/R-52.pkl" PIXEL_MEAN: [123.675, 116.280, 103.530] PIXEL_STD: [58.395, 57.120, 57.375] BACKBONE: NAME: "build_resnet_deeplab_backbone" RESNETS: DEPTH: 50 NORM: "SyncBN" RES5_MULTI_GRID: [1, 2, 4] STEM_TYPE: "deeplab" STEM_OUT_CHANNELS: 128 STRIDE_IN_1X1: False SOLVER: MAX_ITER: 90000 INPUT: FORMAT: "RGB" CROP: SIZE: (512, 1024) ================================================ FILE: detectron2/projects/Panoptic-DeepLab/configs/Cityscapes-PanopticSegmentation/panoptic_deeplab_R_52_os16_mg124_poly_90k_bs32_crop_512_1024_dsconv.yaml ================================================ _BASE_: Base-PanopticDeepLab-OS16.yaml MODEL: WEIGHTS: "detectron2://DeepLab/R-52.pkl" PIXEL_MEAN: [123.675, 116.280, 103.530] PIXEL_STD: [58.395, 57.120, 57.375] BACKBONE: NAME: "build_resnet_deeplab_backbone" RESNETS: DEPTH: 50 NORM: "SyncBN" RES5_MULTI_GRID: [1, 2, 4] STEM_TYPE: "deeplab" STEM_OUT_CHANNELS: 128 STRIDE_IN_1X1: False PANOPTIC_DEEPLAB: USE_DEPTHWISE_SEPARABLE_CONV: True SEM_SEG_HEAD: USE_DEPTHWISE_SEPARABLE_CONV: True SOLVER: MAX_ITER: 90000 INPUT: FORMAT: "RGB" CROP: SIZE: (512, 1024) ================================================ FILE: detectron2/projects/Panoptic-DeepLab/panoptic_deeplab/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .config import add_panoptic_deeplab_config from .dataset_mapper import PanopticDeeplabDatasetMapper from .panoptic_seg import ( PanopticDeepLab, INS_EMBED_BRANCHES_REGISTRY, build_ins_embed_branch, PanopticDeepLabSemSegHead, PanopticDeepLabInsEmbedHead, ) ================================================ FILE: detectron2/projects/Panoptic-DeepLab/panoptic_deeplab/config.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.config import CfgNode as CN from detectron2.projects.deeplab import add_deeplab_config def add_panoptic_deeplab_config(cfg): """ Add config for Panoptic-DeepLab. """ # Reuse DeepLab config. add_deeplab_config(cfg) # Target generation parameters. cfg.INPUT.GAUSSIAN_SIGMA = 10 cfg.INPUT.IGNORE_STUFF_IN_OFFSET = True cfg.INPUT.SMALL_INSTANCE_AREA = 4096 cfg.INPUT.SMALL_INSTANCE_WEIGHT = 3 cfg.INPUT.IGNORE_CROWD_IN_SEMANTIC = False # Optimizer type. cfg.SOLVER.OPTIMIZER = "ADAM" # Panoptic-DeepLab semantic segmentation head. # We add an extra convolution before predictor. cfg.MODEL.SEM_SEG_HEAD.HEAD_CHANNELS = 256 cfg.MODEL.SEM_SEG_HEAD.LOSS_TOP_K = 0.2 # Panoptic-DeepLab instance segmentation head. cfg.MODEL.INS_EMBED_HEAD = CN() cfg.MODEL.INS_EMBED_HEAD.NAME = "PanopticDeepLabInsEmbedHead" cfg.MODEL.INS_EMBED_HEAD.IN_FEATURES = ["res2", "res3", "res5"] cfg.MODEL.INS_EMBED_HEAD.PROJECT_FEATURES = ["res2", "res3"] cfg.MODEL.INS_EMBED_HEAD.PROJECT_CHANNELS = [32, 64] cfg.MODEL.INS_EMBED_HEAD.ASPP_CHANNELS = 256 cfg.MODEL.INS_EMBED_HEAD.ASPP_DILATIONS = [6, 12, 18] cfg.MODEL.INS_EMBED_HEAD.ASPP_DROPOUT = 0.1 # We add an extra convolution before predictor. cfg.MODEL.INS_EMBED_HEAD.HEAD_CHANNELS = 32 cfg.MODEL.INS_EMBED_HEAD.CONVS_DIM = 128 cfg.MODEL.INS_EMBED_HEAD.COMMON_STRIDE = 4 cfg.MODEL.INS_EMBED_HEAD.NORM = "SyncBN" cfg.MODEL.INS_EMBED_HEAD.CENTER_LOSS_WEIGHT = 200.0 cfg.MODEL.INS_EMBED_HEAD.OFFSET_LOSS_WEIGHT = 0.01 # Panoptic-DeepLab post-processing setting. cfg.MODEL.PANOPTIC_DEEPLAB = CN() # Stuff area limit, ignore stuff region below this number. cfg.MODEL.PANOPTIC_DEEPLAB.STUFF_AREA = 2048 cfg.MODEL.PANOPTIC_DEEPLAB.CENTER_THRESHOLD = 0.1 cfg.MODEL.PANOPTIC_DEEPLAB.NMS_KERNEL = 7 cfg.MODEL.PANOPTIC_DEEPLAB.TOP_K_INSTANCE = 200 # If set to False, Panoptic-DeepLab will not evaluate instance segmentation. cfg.MODEL.PANOPTIC_DEEPLAB.PREDICT_INSTANCES = True cfg.MODEL.PANOPTIC_DEEPLAB.USE_DEPTHWISE_SEPARABLE_CONV = False # This is the padding parameter for images with various sizes. ASPP layers # requires input images to be divisible by the average pooling size and we # can use `MODEL.PANOPTIC_DEEPLAB.SIZE_DIVISIBILITY` to pad all images to # a fixed resolution (e.g. 640x640 for COCO) to avoid having a image size # that is not divisible by ASPP average pooling size. cfg.MODEL.PANOPTIC_DEEPLAB.SIZE_DIVISIBILITY = -1 # Only evaluates network speed (ignores post-processing). cfg.MODEL.PANOPTIC_DEEPLAB.BENCHMARK_NETWORK_SPEED = False ================================================ FILE: detectron2/projects/Panoptic-DeepLab/panoptic_deeplab/dataset_mapper.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import logging import numpy as np from typing import Callable, List, Union import torch from panopticapi.utils import rgb2id from detectron2.config import configurable from detectron2.data import MetadataCatalog from detectron2.data import detection_utils as utils from detectron2.data import transforms as T from .target_generator import PanopticDeepLabTargetGenerator __all__ = ["PanopticDeeplabDatasetMapper"] class PanopticDeeplabDatasetMapper: """ The callable currently does the following: 1. Read the image from "file_name" and label from "pan_seg_file_name" 2. Applies random scale, crop and flip transforms to image and label 3. Prepare data to Tensor and generate training targets from label """ @configurable def __init__( self, *, augmentations: List[Union[T.Augmentation, T.Transform]], image_format: str, panoptic_target_generator: Callable, ): """ NOTE: this interface is experimental. Args: augmentations: a list of augmentations or deterministic transforms to apply image_format: an image format supported by :func:`detection_utils.read_image`. panoptic_target_generator: a callable that takes "panoptic_seg" and "segments_info" to generate training targets for the model. """ # fmt: off self.augmentations = T.AugmentationList(augmentations) self.image_format = image_format # fmt: on logger = logging.getLogger(__name__) logger.info("Augmentations used in training: " + str(augmentations)) self.panoptic_target_generator = panoptic_target_generator @classmethod def from_config(cls, cfg): augs = [ T.ResizeShortestEdge( cfg.INPUT.MIN_SIZE_TRAIN, cfg.INPUT.MAX_SIZE_TRAIN, cfg.INPUT.MIN_SIZE_TRAIN_SAMPLING, ) ] if cfg.INPUT.CROP.ENABLED: augs.append(T.RandomCrop(cfg.INPUT.CROP.TYPE, cfg.INPUT.CROP.SIZE)) augs.append(T.RandomFlip()) # Assume always applies to the training set. dataset_names = cfg.DATASETS.TRAIN meta = MetadataCatalog.get(dataset_names[0]) panoptic_target_generator = PanopticDeepLabTargetGenerator( ignore_label=meta.ignore_label, thing_ids=list(meta.thing_dataset_id_to_contiguous_id.values()), sigma=cfg.INPUT.GAUSSIAN_SIGMA, ignore_stuff_in_offset=cfg.INPUT.IGNORE_STUFF_IN_OFFSET, small_instance_area=cfg.INPUT.SMALL_INSTANCE_AREA, small_instance_weight=cfg.INPUT.SMALL_INSTANCE_WEIGHT, ignore_crowd_in_semantic=cfg.INPUT.IGNORE_CROWD_IN_SEMANTIC, ) ret = { "augmentations": augs, "image_format": cfg.INPUT.FORMAT, "panoptic_target_generator": panoptic_target_generator, } return ret def __call__(self, dataset_dict): """ Args: dataset_dict (dict): Metadata of one image, in Detectron2 Dataset format. Returns: dict: a format that builtin models in detectron2 accept """ dataset_dict = copy.deepcopy(dataset_dict) # it will be modified by code below # Load image. image = utils.read_image(dataset_dict["file_name"], format=self.image_format) utils.check_image_size(dataset_dict, image) # Panoptic label is encoded in RGB image. pan_seg_gt = utils.read_image(dataset_dict.pop("pan_seg_file_name"), "RGB") # Reuses semantic transform for panoptic labels. aug_input = T.AugInput(image, sem_seg=pan_seg_gt) _ = self.augmentations(aug_input) image, pan_seg_gt = aug_input.image, aug_input.sem_seg # Pytorch's dataloader is efficient on torch.Tensor due to shared-memory, # but not efficient on large generic data structures due to the use of pickle & mp.Queue. # Therefore it's important to use torch.Tensor. dataset_dict["image"] = torch.as_tensor(np.ascontiguousarray(image.transpose(2, 0, 1))) # Generates training targets for Panoptic-DeepLab. targets = self.panoptic_target_generator(rgb2id(pan_seg_gt), dataset_dict["segments_info"]) dataset_dict.update(targets) return dataset_dict ================================================ FILE: detectron2/projects/Panoptic-DeepLab/panoptic_deeplab/panoptic_seg.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from typing import Callable, Dict, List, Union import fvcore.nn.weight_init as weight_init import torch from torch import nn from torch.nn import functional as F from detectron2.config import configurable from detectron2.data import MetadataCatalog from detectron2.layers import Conv2d, DepthwiseSeparableConv2d, ShapeSpec, get_norm from detectron2.modeling import ( META_ARCH_REGISTRY, SEM_SEG_HEADS_REGISTRY, build_backbone, build_sem_seg_head, ) from detectron2.modeling.postprocessing import sem_seg_postprocess from detectron2.projects.deeplab import DeepLabV3PlusHead from detectron2.projects.deeplab.loss import DeepLabCE from detectron2.structures import BitMasks, ImageList, Instances from detectron2.utils.registry import Registry from .post_processing import get_panoptic_segmentation __all__ = ["PanopticDeepLab", "INS_EMBED_BRANCHES_REGISTRY", "build_ins_embed_branch"] INS_EMBED_BRANCHES_REGISTRY = Registry("INS_EMBED_BRANCHES") INS_EMBED_BRANCHES_REGISTRY.__doc__ = """ Registry for instance embedding branches, which make instance embedding predictions from feature maps. """ @META_ARCH_REGISTRY.register() class PanopticDeepLab(nn.Module): """ Main class for panoptic segmentation architectures. """ def __init__(self, cfg): super().__init__() self.backbone = build_backbone(cfg) self.sem_seg_head = build_sem_seg_head(cfg, self.backbone.output_shape()) self.ins_embed_head = build_ins_embed_branch(cfg, self.backbone.output_shape()) self.register_buffer("pixel_mean", torch.tensor(cfg.MODEL.PIXEL_MEAN).view(-1, 1, 1), False) self.register_buffer("pixel_std", torch.tensor(cfg.MODEL.PIXEL_STD).view(-1, 1, 1), False) self.meta = MetadataCatalog.get(cfg.DATASETS.TRAIN[0]) self.stuff_area = cfg.MODEL.PANOPTIC_DEEPLAB.STUFF_AREA self.threshold = cfg.MODEL.PANOPTIC_DEEPLAB.CENTER_THRESHOLD self.nms_kernel = cfg.MODEL.PANOPTIC_DEEPLAB.NMS_KERNEL self.top_k = cfg.MODEL.PANOPTIC_DEEPLAB.TOP_K_INSTANCE self.predict_instances = cfg.MODEL.PANOPTIC_DEEPLAB.PREDICT_INSTANCES self.use_depthwise_separable_conv = cfg.MODEL.PANOPTIC_DEEPLAB.USE_DEPTHWISE_SEPARABLE_CONV assert ( cfg.MODEL.SEM_SEG_HEAD.USE_DEPTHWISE_SEPARABLE_CONV == cfg.MODEL.PANOPTIC_DEEPLAB.USE_DEPTHWISE_SEPARABLE_CONV ) self.size_divisibility = cfg.MODEL.PANOPTIC_DEEPLAB.SIZE_DIVISIBILITY self.benchmark_network_speed = cfg.MODEL.PANOPTIC_DEEPLAB.BENCHMARK_NETWORK_SPEED @property def device(self): return self.pixel_mean.device def forward(self, batched_inputs): """ Args: batched_inputs: a list, batched outputs of :class:`DatasetMapper`. Each item in the list contains the inputs for one image. For now, each item in the list is a dict that contains: * "image": Tensor, image in (C, H, W) format. * "sem_seg": semantic segmentation ground truth * "center": center points heatmap ground truth * "offset": pixel offsets to center points ground truth * Other information that's included in the original dicts, such as: "height", "width" (int): the output resolution of the model (may be different from input resolution), used in inference. Returns: list[dict]: each dict is the results for one image. The dict contains the following keys: * "panoptic_seg", "sem_seg": see documentation :doc:`/tutorials/models` for the standard output format * "instances": available if ``predict_instances is True``. see documentation :doc:`/tutorials/models` for the standard output format """ images = [x["image"].to(self.device) for x in batched_inputs] images = [(x - self.pixel_mean) / self.pixel_std for x in images] # To avoid error in ASPP layer when input has different size. size_divisibility = ( self.size_divisibility if self.size_divisibility > 0 else self.backbone.size_divisibility ) images = ImageList.from_tensors(images, size_divisibility) features = self.backbone(images.tensor) losses = {} if "sem_seg" in batched_inputs[0]: targets = [x["sem_seg"].to(self.device) for x in batched_inputs] targets = ImageList.from_tensors( targets, size_divisibility, self.sem_seg_head.ignore_value ).tensor if "sem_seg_weights" in batched_inputs[0]: # The default D2 DatasetMapper may not contain "sem_seg_weights" # Avoid error in testing when default DatasetMapper is used. weights = [x["sem_seg_weights"].to(self.device) for x in batched_inputs] weights = ImageList.from_tensors(weights, size_divisibility).tensor else: weights = None else: targets = None weights = None sem_seg_results, sem_seg_losses = self.sem_seg_head(features, targets, weights) losses.update(sem_seg_losses) if "center" in batched_inputs[0] and "offset" in batched_inputs[0]: center_targets = [x["center"].to(self.device) for x in batched_inputs] center_targets = ImageList.from_tensors( center_targets, size_divisibility ).tensor.unsqueeze(1) center_weights = [x["center_weights"].to(self.device) for x in batched_inputs] center_weights = ImageList.from_tensors(center_weights, size_divisibility).tensor offset_targets = [x["offset"].to(self.device) for x in batched_inputs] offset_targets = ImageList.from_tensors(offset_targets, size_divisibility).tensor offset_weights = [x["offset_weights"].to(self.device) for x in batched_inputs] offset_weights = ImageList.from_tensors(offset_weights, size_divisibility).tensor else: center_targets = None center_weights = None offset_targets = None offset_weights = None center_results, offset_results, center_losses, offset_losses = self.ins_embed_head( features, center_targets, center_weights, offset_targets, offset_weights ) losses.update(center_losses) losses.update(offset_losses) if self.training: return losses if self.benchmark_network_speed: return [] processed_results = [] for sem_seg_result, center_result, offset_result, input_per_image, image_size in zip( sem_seg_results, center_results, offset_results, batched_inputs, images.image_sizes ): height = input_per_image.get("height") width = input_per_image.get("width") r = sem_seg_postprocess(sem_seg_result, image_size, height, width) c = sem_seg_postprocess(center_result, image_size, height, width) o = sem_seg_postprocess(offset_result, image_size, height, width) # Post-processing to get panoptic segmentation. panoptic_image, _ = get_panoptic_segmentation( r.argmax(dim=0, keepdim=True), c, o, thing_ids=self.meta.thing_dataset_id_to_contiguous_id.values(), label_divisor=self.meta.label_divisor, stuff_area=self.stuff_area, void_label=-1, threshold=self.threshold, nms_kernel=self.nms_kernel, top_k=self.top_k, ) # For semantic segmentation evaluation. processed_results.append({"sem_seg": r}) panoptic_image = panoptic_image.squeeze(0) semantic_prob = F.softmax(r, dim=0) # For panoptic segmentation evaluation. processed_results[-1]["panoptic_seg"] = (panoptic_image, None) # For instance segmentation evaluation. if self.predict_instances: instances = [] panoptic_image_cpu = panoptic_image.cpu().numpy() for panoptic_label in np.unique(panoptic_image_cpu): if panoptic_label == -1: continue pred_class = panoptic_label // self.meta.label_divisor isthing = pred_class in list( self.meta.thing_dataset_id_to_contiguous_id.values() ) # Get instance segmentation results. if isthing: instance = Instances((height, width)) # Evaluation code takes continuous id starting from 0 instance.pred_classes = torch.tensor( [pred_class], device=panoptic_image.device ) mask = panoptic_image == panoptic_label instance.pred_masks = mask.unsqueeze(0) # Average semantic probability sem_scores = semantic_prob[pred_class, ...] sem_scores = torch.mean(sem_scores[mask]) # Center point probability mask_indices = torch.nonzero(mask).float() center_y, center_x = ( torch.mean(mask_indices[:, 0]), torch.mean(mask_indices[:, 1]), ) center_scores = c[0, int(center_y.item()), int(center_x.item())] # Confidence score is semantic prob * center prob. instance.scores = torch.tensor( [sem_scores * center_scores], device=panoptic_image.device ) # Get bounding boxes instance.pred_boxes = BitMasks(instance.pred_masks).get_bounding_boxes() instances.append(instance) if len(instances) > 0: processed_results[-1]["instances"] = Instances.cat(instances) return processed_results @SEM_SEG_HEADS_REGISTRY.register() class PanopticDeepLabSemSegHead(DeepLabV3PlusHead): """ A semantic segmentation head described in :paper:`Panoptic-DeepLab`. """ @configurable def __init__( self, input_shape: Dict[str, ShapeSpec], *, decoder_channels: List[int], norm: Union[str, Callable], head_channels: int, loss_weight: float, loss_type: str, loss_top_k: float, ignore_value: int, num_classes: int, **kwargs, ): """ NOTE: this interface is experimental. Args: input_shape (ShapeSpec): shape of the input feature decoder_channels (list[int]): a list of output channels of each decoder stage. It should have the same length as "input_shape" (each element in "input_shape" corresponds to one decoder stage). norm (str or callable): normalization for all conv layers. head_channels (int): the output channels of extra convolutions between decoder and predictor. loss_weight (float): loss weight. loss_top_k: (float): setting the top k% hardest pixels for "hard_pixel_mining" loss. loss_type, ignore_value, num_classes: the same as the base class. """ super().__init__( input_shape, decoder_channels=decoder_channels, norm=norm, ignore_value=ignore_value, **kwargs, ) assert self.decoder_only self.loss_weight = loss_weight use_bias = norm == "" # `head` is additional transform before predictor if self.use_depthwise_separable_conv: # We use a single 5x5 DepthwiseSeparableConv2d to replace # 2 3x3 Conv2d since they have the same receptive field. self.head = DepthwiseSeparableConv2d( decoder_channels[0], head_channels, kernel_size=5, padding=2, norm1=norm, activation1=F.relu, norm2=norm, activation2=F.relu, ) else: self.head = nn.Sequential( Conv2d( decoder_channels[0], decoder_channels[0], kernel_size=3, padding=1, bias=use_bias, norm=get_norm(norm, decoder_channels[0]), activation=F.relu, ), Conv2d( decoder_channels[0], head_channels, kernel_size=3, padding=1, bias=use_bias, norm=get_norm(norm, head_channels), activation=F.relu, ), ) weight_init.c2_xavier_fill(self.head[0]) weight_init.c2_xavier_fill(self.head[1]) self.predictor = Conv2d(head_channels, num_classes, kernel_size=1) nn.init.normal_(self.predictor.weight, 0, 0.001) nn.init.constant_(self.predictor.bias, 0) if loss_type == "cross_entropy": self.loss = nn.CrossEntropyLoss(reduction="mean", ignore_index=ignore_value) elif loss_type == "hard_pixel_mining": self.loss = DeepLabCE(ignore_label=ignore_value, top_k_percent_pixels=loss_top_k) else: raise ValueError("Unexpected loss type: %s" % loss_type) @classmethod def from_config(cls, cfg, input_shape): ret = super().from_config(cfg, input_shape) ret["head_channels"] = cfg.MODEL.SEM_SEG_HEAD.HEAD_CHANNELS ret["loss_top_k"] = cfg.MODEL.SEM_SEG_HEAD.LOSS_TOP_K return ret def forward(self, features, targets=None, weights=None): """ Returns: In training, returns (None, dict of losses) In inference, returns (CxHxW logits, {}) """ y = self.layers(features) if self.training: return None, self.losses(y, targets, weights) else: y = F.interpolate( y, scale_factor=self.common_stride, mode="bilinear", align_corners=False ) return y, {} def layers(self, features): assert self.decoder_only y = super().layers(features) y = self.head(y) y = self.predictor(y) return y def losses(self, predictions, targets, weights=None): predictions = F.interpolate( predictions, scale_factor=self.common_stride, mode="bilinear", align_corners=False ) loss = self.loss(predictions, targets, weights) losses = {"loss_sem_seg": loss * self.loss_weight} return losses def build_ins_embed_branch(cfg, input_shape): """ Build a instance embedding branch from `cfg.MODEL.INS_EMBED_HEAD.NAME`. """ name = cfg.MODEL.INS_EMBED_HEAD.NAME return INS_EMBED_BRANCHES_REGISTRY.get(name)(cfg, input_shape) @INS_EMBED_BRANCHES_REGISTRY.register() class PanopticDeepLabInsEmbedHead(DeepLabV3PlusHead): """ A instance embedding head described in :paper:`Panoptic-DeepLab`. """ @configurable def __init__( self, input_shape: Dict[str, ShapeSpec], *, decoder_channels: List[int], norm: Union[str, Callable], head_channels: int, center_loss_weight: float, offset_loss_weight: float, **kwargs, ): """ NOTE: this interface is experimental. Args: input_shape (ShapeSpec): shape of the input feature decoder_channels (list[int]): a list of output channels of each decoder stage. It should have the same length as "input_shape" (each element in "input_shape" corresponds to one decoder stage). norm (str or callable): normalization for all conv layers. head_channels (int): the output channels of extra convolutions between decoder and predictor. center_loss_weight (float): loss weight for center point prediction. offset_loss_weight (float): loss weight for center offset prediction. """ super().__init__(input_shape, decoder_channels=decoder_channels, norm=norm, **kwargs) assert self.decoder_only self.center_loss_weight = center_loss_weight self.offset_loss_weight = offset_loss_weight use_bias = norm == "" # center prediction # `head` is additional transform before predictor self.center_head = nn.Sequential( Conv2d( decoder_channels[0], decoder_channels[0], kernel_size=3, padding=1, bias=use_bias, norm=get_norm(norm, decoder_channels[0]), activation=F.relu, ), Conv2d( decoder_channels[0], head_channels, kernel_size=3, padding=1, bias=use_bias, norm=get_norm(norm, head_channels), activation=F.relu, ), ) weight_init.c2_xavier_fill(self.center_head[0]) weight_init.c2_xavier_fill(self.center_head[1]) self.center_predictor = Conv2d(head_channels, 1, kernel_size=1) nn.init.normal_(self.center_predictor.weight, 0, 0.001) nn.init.constant_(self.center_predictor.bias, 0) # offset prediction # `head` is additional transform before predictor if self.use_depthwise_separable_conv: # We use a single 5x5 DepthwiseSeparableConv2d to replace # 2 3x3 Conv2d since they have the same receptive field. self.offset_head = DepthwiseSeparableConv2d( decoder_channels[0], head_channels, kernel_size=5, padding=2, norm1=norm, activation1=F.relu, norm2=norm, activation2=F.relu, ) else: self.offset_head = nn.Sequential( Conv2d( decoder_channels[0], decoder_channels[0], kernel_size=3, padding=1, bias=use_bias, norm=get_norm(norm, decoder_channels[0]), activation=F.relu, ), Conv2d( decoder_channels[0], head_channels, kernel_size=3, padding=1, bias=use_bias, norm=get_norm(norm, head_channels), activation=F.relu, ), ) weight_init.c2_xavier_fill(self.offset_head[0]) weight_init.c2_xavier_fill(self.offset_head[1]) self.offset_predictor = Conv2d(head_channels, 2, kernel_size=1) nn.init.normal_(self.offset_predictor.weight, 0, 0.001) nn.init.constant_(self.offset_predictor.bias, 0) self.center_loss = nn.MSELoss(reduction="none") self.offset_loss = nn.L1Loss(reduction="none") @classmethod def from_config(cls, cfg, input_shape): if cfg.INPUT.CROP.ENABLED: assert cfg.INPUT.CROP.TYPE == "absolute" train_size = cfg.INPUT.CROP.SIZE else: train_size = None decoder_channels = [cfg.MODEL.INS_EMBED_HEAD.CONVS_DIM] * ( len(cfg.MODEL.INS_EMBED_HEAD.IN_FEATURES) - 1 ) + [cfg.MODEL.INS_EMBED_HEAD.ASPP_CHANNELS] ret = dict( input_shape={ k: v for k, v in input_shape.items() if k in cfg.MODEL.INS_EMBED_HEAD.IN_FEATURES }, project_channels=cfg.MODEL.INS_EMBED_HEAD.PROJECT_CHANNELS, aspp_dilations=cfg.MODEL.INS_EMBED_HEAD.ASPP_DILATIONS, aspp_dropout=cfg.MODEL.INS_EMBED_HEAD.ASPP_DROPOUT, decoder_channels=decoder_channels, common_stride=cfg.MODEL.INS_EMBED_HEAD.COMMON_STRIDE, norm=cfg.MODEL.INS_EMBED_HEAD.NORM, train_size=train_size, head_channels=cfg.MODEL.INS_EMBED_HEAD.HEAD_CHANNELS, center_loss_weight=cfg.MODEL.INS_EMBED_HEAD.CENTER_LOSS_WEIGHT, offset_loss_weight=cfg.MODEL.INS_EMBED_HEAD.OFFSET_LOSS_WEIGHT, use_depthwise_separable_conv=cfg.MODEL.SEM_SEG_HEAD.USE_DEPTHWISE_SEPARABLE_CONV, ) return ret def forward( self, features, center_targets=None, center_weights=None, offset_targets=None, offset_weights=None, ): """ Returns: In training, returns (None, dict of losses) In inference, returns (CxHxW logits, {}) """ center, offset = self.layers(features) if self.training: return ( None, None, self.center_losses(center, center_targets, center_weights), self.offset_losses(offset, offset_targets, offset_weights), ) else: center = F.interpolate( center, scale_factor=self.common_stride, mode="bilinear", align_corners=False ) offset = ( F.interpolate( offset, scale_factor=self.common_stride, mode="bilinear", align_corners=False ) * self.common_stride ) return center, offset, {}, {} def layers(self, features): assert self.decoder_only y = super().layers(features) # center center = self.center_head(y) center = self.center_predictor(center) # offset offset = self.offset_head(y) offset = self.offset_predictor(offset) return center, offset def center_losses(self, predictions, targets, weights): predictions = F.interpolate( predictions, scale_factor=self.common_stride, mode="bilinear", align_corners=False ) loss = self.center_loss(predictions, targets) * weights if weights.sum() > 0: loss = loss.sum() / weights.sum() else: loss = loss.sum() * 0 losses = {"loss_center": loss * self.center_loss_weight} return losses def offset_losses(self, predictions, targets, weights): predictions = ( F.interpolate( predictions, scale_factor=self.common_stride, mode="bilinear", align_corners=False ) * self.common_stride ) loss = self.offset_loss(predictions, targets) * weights if weights.sum() > 0: loss = loss.sum() / weights.sum() else: loss = loss.sum() * 0 losses = {"loss_offset": loss * self.offset_loss_weight} return losses ================================================ FILE: detectron2/projects/Panoptic-DeepLab/panoptic_deeplab/post_processing.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # Reference: https://github.com/bowenc0221/panoptic-deeplab/blob/master/segmentation/model/post_processing/instance_post_processing.py # noqa from collections import Counter import torch import torch.nn.functional as F def find_instance_center(center_heatmap, threshold=0.1, nms_kernel=3, top_k=None): """ Find the center points from the center heatmap. Args: center_heatmap: A Tensor of shape [1, H, W] of raw center heatmap output. threshold: A float, threshold applied to center heatmap score. nms_kernel: An integer, NMS max pooling kernel size. top_k: An integer, top k centers to keep. Returns: A Tensor of shape [K, 2] where K is the number of center points. The order of second dim is (y, x). """ # Thresholding, setting values below threshold to -1. center_heatmap = F.threshold(center_heatmap, threshold, -1) # NMS nms_padding = (nms_kernel - 1) // 2 center_heatmap_max_pooled = F.max_pool2d( center_heatmap, kernel_size=nms_kernel, stride=1, padding=nms_padding ) center_heatmap[center_heatmap != center_heatmap_max_pooled] = -1 # Squeeze first two dimensions. center_heatmap = center_heatmap.squeeze() assert len(center_heatmap.size()) == 2, "Something is wrong with center heatmap dimension." # Find non-zero elements. if top_k is None: return torch.nonzero(center_heatmap > 0) else: # find top k centers. top_k_scores, _ = torch.topk(torch.flatten(center_heatmap), top_k) return torch.nonzero(center_heatmap > top_k_scores[-1].clamp_(min=0)) def group_pixels(center_points, offsets): """ Gives each pixel in the image an instance id. Args: center_points: A Tensor of shape [K, 2] where K is the number of center points. The order of second dim is (y, x). offsets: A Tensor of shape [2, H, W] of raw offset output. The order of second dim is (offset_y, offset_x). Returns: A Tensor of shape [1, H, W] with values in range [1, K], which represents the center this pixel belongs to. """ height, width = offsets.size()[1:] # Generates a coordinate map, where each location is the coordinate of # that location. y_coord, x_coord = torch.meshgrid( torch.arange(height, dtype=offsets.dtype, device=offsets.device), torch.arange(width, dtype=offsets.dtype, device=offsets.device), ) coord = torch.cat((y_coord.unsqueeze(0), x_coord.unsqueeze(0)), dim=0) center_loc = coord + offsets center_loc = center_loc.flatten(1).T.unsqueeze_(0) # [1, H*W, 2] center_points = center_points.unsqueeze(1) # [K, 1, 2] # Distance: [K, H*W]. distance = torch.norm(center_points - center_loc, dim=-1) # Finds center with minimum distance at each location, offset by 1, to # reserve id=0 for stuff. instance_id = torch.argmin(distance, dim=0).reshape((1, height, width)) + 1 return instance_id def get_instance_segmentation( sem_seg, center_heatmap, offsets, thing_seg, thing_ids, threshold=0.1, nms_kernel=3, top_k=None ): """ Post-processing for instance segmentation, gets class agnostic instance id. Args: sem_seg: A Tensor of shape [1, H, W], predicted semantic label. center_heatmap: A Tensor of shape [1, H, W] of raw center heatmap output. offsets: A Tensor of shape [2, H, W] of raw offset output. The order of second dim is (offset_y, offset_x). thing_seg: A Tensor of shape [1, H, W], predicted foreground mask, if not provided, inference from semantic prediction. thing_ids: A set of ids from contiguous category ids belonging to thing categories. threshold: A float, threshold applied to center heatmap score. nms_kernel: An integer, NMS max pooling kernel size. top_k: An integer, top k centers to keep. Returns: A Tensor of shape [1, H, W] with value 0 represent stuff (not instance) and other positive values represent different instances. A Tensor of shape [1, K, 2] where K is the number of center points. The order of second dim is (y, x). """ center_points = find_instance_center( center_heatmap, threshold=threshold, nms_kernel=nms_kernel, top_k=top_k ) if center_points.size(0) == 0: return torch.zeros_like(sem_seg), center_points.unsqueeze(0) ins_seg = group_pixels(center_points, offsets) return thing_seg * ins_seg, center_points.unsqueeze(0) def merge_semantic_and_instance( sem_seg, ins_seg, semantic_thing_seg, label_divisor, thing_ids, stuff_area, void_label ): """ Post-processing for panoptic segmentation, by merging semantic segmentation label and class agnostic instance segmentation label. Args: sem_seg: A Tensor of shape [1, H, W], predicted category id for each pixel. ins_seg: A Tensor of shape [1, H, W], predicted instance id for each pixel. semantic_thing_seg: A Tensor of shape [1, H, W], predicted foreground mask. label_divisor: An integer, used to convert panoptic id = semantic id * label_divisor + instance_id. thing_ids: Set, a set of ids from contiguous category ids belonging to thing categories. stuff_area: An integer, remove stuff whose area is less tan stuff_area. void_label: An integer, indicates the region has no confident prediction. Returns: A Tensor of shape [1, H, W]. """ # In case thing mask does not align with semantic prediction. pan_seg = torch.zeros_like(sem_seg) + void_label is_thing = (ins_seg > 0) & (semantic_thing_seg > 0) # Keep track of instance id for each class. class_id_tracker = Counter() # Paste thing by majority voting. instance_ids = torch.unique(ins_seg) for ins_id in instance_ids: if ins_id == 0: continue # Make sure only do majority voting within `semantic_thing_seg`. thing_mask = (ins_seg == ins_id) & is_thing if torch.nonzero(thing_mask).size(0) == 0: continue class_id, _ = torch.mode(sem_seg[thing_mask].view(-1)) class_id_tracker[class_id.item()] += 1 new_ins_id = class_id_tracker[class_id.item()] pan_seg[thing_mask] = class_id * label_divisor + new_ins_id # Paste stuff to unoccupied area. class_ids = torch.unique(sem_seg) for class_id in class_ids: if class_id.item() in thing_ids: # thing class continue # Calculate stuff area. stuff_mask = (sem_seg == class_id) & (ins_seg == 0) if stuff_mask.sum().item() >= stuff_area: pan_seg[stuff_mask] = class_id * label_divisor return pan_seg def get_panoptic_segmentation( sem_seg, center_heatmap, offsets, thing_ids, label_divisor, stuff_area, void_label, threshold=0.1, nms_kernel=7, top_k=200, foreground_mask=None, ): """ Post-processing for panoptic segmentation. Args: sem_seg: A Tensor of shape [1, H, W] of predicted semantic label. center_heatmap: A Tensor of shape [1, H, W] of raw center heatmap output. offsets: A Tensor of shape [2, H, W] of raw offset output. The order of second dim is (offset_y, offset_x). thing_ids: A set of ids from contiguous category ids belonging to thing categories. label_divisor: An integer, used to convert panoptic id = semantic id * label_divisor + instance_id. stuff_area: An integer, remove stuff whose area is less tan stuff_area. void_label: An integer, indicates the region has no confident prediction. threshold: A float, threshold applied to center heatmap score. nms_kernel: An integer, NMS max pooling kernel size. top_k: An integer, top k centers to keep. foreground_mask: Optional, A Tensor of shape [1, H, W] of predicted binary foreground mask. If not provided, it will be generated from sem_seg. Returns: A Tensor of shape [1, H, W], int64. """ if sem_seg.dim() != 3 and sem_seg.size(0) != 1: raise ValueError("Semantic prediction with un-supported shape: {}.".format(sem_seg.size())) if center_heatmap.dim() != 3: raise ValueError( "Center prediction with un-supported dimension: {}.".format(center_heatmap.dim()) ) if offsets.dim() != 3: raise ValueError("Offset prediction with un-supported dimension: {}.".format(offsets.dim())) if foreground_mask is not None: if foreground_mask.dim() != 3 and foreground_mask.size(0) != 1: raise ValueError( "Foreground prediction with un-supported shape: {}.".format(sem_seg.size()) ) thing_seg = foreground_mask else: # inference from semantic segmentation thing_seg = torch.zeros_like(sem_seg) for thing_class in list(thing_ids): thing_seg[sem_seg == thing_class] = 1 instance, center = get_instance_segmentation( sem_seg, center_heatmap, offsets, thing_seg, thing_ids, threshold=threshold, nms_kernel=nms_kernel, top_k=top_k, ) panoptic = merge_semantic_and_instance( sem_seg, instance, thing_seg, label_divisor, thing_ids, stuff_area, void_label ) return panoptic, center ================================================ FILE: detectron2/projects/Panoptic-DeepLab/panoptic_deeplab/target_generator.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # Reference: https://github.com/bowenc0221/panoptic-deeplab/blob/aa934324b55a34ce95fea143aea1cb7a6dbe04bd/segmentation/data/transforms/target_transforms.py#L11 # noqa import numpy as np import torch class PanopticDeepLabTargetGenerator(object): """ Generates training targets for Panoptic-DeepLab. """ def __init__( self, ignore_label, thing_ids, sigma=8, ignore_stuff_in_offset=False, small_instance_area=0, small_instance_weight=1, ignore_crowd_in_semantic=False, ): """ Args: ignore_label: Integer, the ignore label for semantic segmentation. thing_ids: Set, a set of ids from contiguous category ids belonging to thing categories. sigma: the sigma for Gaussian kernel. ignore_stuff_in_offset: Boolean, whether to ignore stuff region when training the offset branch. small_instance_area: Integer, indicates largest area for small instances. small_instance_weight: Integer, indicates semantic loss weights for small instances. ignore_crowd_in_semantic: Boolean, whether to ignore crowd region in semantic segmentation branch, crowd region is ignored in the original TensorFlow implementation. """ self.ignore_label = ignore_label self.thing_ids = set(thing_ids) self.ignore_stuff_in_offset = ignore_stuff_in_offset self.small_instance_area = small_instance_area self.small_instance_weight = small_instance_weight self.ignore_crowd_in_semantic = ignore_crowd_in_semantic # Generate the default Gaussian image for each center self.sigma = sigma size = 6 * sigma + 3 x = np.arange(0, size, 1, float) y = x[:, np.newaxis] x0, y0 = 3 * sigma + 1, 3 * sigma + 1 self.g = np.exp(-((x - x0) ** 2 + (y - y0) ** 2) / (2 * sigma**2)) def __call__(self, panoptic, segments_info): """Generates the training target. reference: https://github.com/mcordts/cityscapesScripts/blob/master/cityscapesscripts/preparation/createPanopticImgs.py # noqa reference: https://github.com/facebookresearch/detectron2/blob/main/datasets/prepare_panoptic_fpn.py#L18 # noqa Args: panoptic: numpy.array, panoptic label, we assume it is already converted from rgb image by panopticapi.utils.rgb2id. segments_info (list[dict]): see detectron2 documentation of "Use Custom Datasets". Returns: A dictionary with fields: - sem_seg: Tensor, semantic label, shape=(H, W). - center: Tensor, center heatmap, shape=(H, W). - center_points: List, center coordinates, with tuple (y-coord, x-coord). - offset: Tensor, offset, shape=(2, H, W), first dim is (offset_y, offset_x). - sem_seg_weights: Tensor, loss weight for semantic prediction, shape=(H, W). - center_weights: Tensor, ignore region of center prediction, shape=(H, W), used as weights for center regression 0 is ignore, 1 is has instance. Multiply this mask to loss. - offset_weights: Tensor, ignore region of offset prediction, shape=(H, W), used as weights for offset regression 0 is ignore, 1 is has instance. Multiply this mask to loss. """ height, width = panoptic.shape[0], panoptic.shape[1] semantic = np.zeros_like(panoptic, dtype=np.uint8) + self.ignore_label center = np.zeros((height, width), dtype=np.float32) center_pts = [] offset = np.zeros((2, height, width), dtype=np.float32) y_coord, x_coord = np.meshgrid( np.arange(height, dtype=np.float32), np.arange(width, dtype=np.float32), indexing="ij" ) # Generate pixel-wise loss weights semantic_weights = np.ones_like(panoptic, dtype=np.uint8) # 0: ignore, 1: has instance # three conditions for a region to be ignored for instance branches: # (1) It is labeled as `ignore_label` # (2) It is crowd region (iscrowd=1) # (3) (Optional) It is stuff region (for offset branch) center_weights = np.zeros_like(panoptic, dtype=np.uint8) offset_weights = np.zeros_like(panoptic, dtype=np.uint8) for seg in segments_info: cat_id = seg["category_id"] if not (self.ignore_crowd_in_semantic and seg["iscrowd"]): semantic[panoptic == seg["id"]] = cat_id if not seg["iscrowd"]: # Ignored regions are not in `segments_info`. # Handle crowd region. center_weights[panoptic == seg["id"]] = 1 if not self.ignore_stuff_in_offset or cat_id in self.thing_ids: offset_weights[panoptic == seg["id"]] = 1 if cat_id in self.thing_ids: # find instance center mask_index = np.where(panoptic == seg["id"]) if len(mask_index[0]) == 0: # the instance is completely cropped continue # Find instance area ins_area = len(mask_index[0]) if ins_area < self.small_instance_area: semantic_weights[panoptic == seg["id"]] = self.small_instance_weight center_y, center_x = np.mean(mask_index[0]), np.mean(mask_index[1]) center_pts.append([center_y, center_x]) # generate center heatmap y, x = int(round(center_y)), int(round(center_x)) sigma = self.sigma # upper left ul = int(np.round(x - 3 * sigma - 1)), int(np.round(y - 3 * sigma - 1)) # bottom right br = int(np.round(x + 3 * sigma + 2)), int(np.round(y + 3 * sigma + 2)) # start and end indices in default Gaussian image gaussian_x0, gaussian_x1 = max(0, -ul[0]), min(br[0], width) - ul[0] gaussian_y0, gaussian_y1 = max(0, -ul[1]), min(br[1], height) - ul[1] # start and end indices in center heatmap image center_x0, center_x1 = max(0, ul[0]), min(br[0], width) center_y0, center_y1 = max(0, ul[1]), min(br[1], height) center[center_y0:center_y1, center_x0:center_x1] = np.maximum( center[center_y0:center_y1, center_x0:center_x1], self.g[gaussian_y0:gaussian_y1, gaussian_x0:gaussian_x1], ) # generate offset (2, h, w) -> (y-dir, x-dir) offset[0][mask_index] = center_y - y_coord[mask_index] offset[1][mask_index] = center_x - x_coord[mask_index] center_weights = center_weights[None] offset_weights = offset_weights[None] return dict( sem_seg=torch.as_tensor(semantic.astype("long")), center=torch.as_tensor(center.astype(np.float32)), center_points=center_pts, offset=torch.as_tensor(offset.astype(np.float32)), sem_seg_weights=torch.as_tensor(semantic_weights.astype(np.float32)), center_weights=torch.as_tensor(center_weights.astype(np.float32)), offset_weights=torch.as_tensor(offset_weights.astype(np.float32)), ) ================================================ FILE: detectron2/projects/Panoptic-DeepLab/train_net.py ================================================ #!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. """ Panoptic-DeepLab Training Script. This script is a simplified version of the training script in detectron2/tools. """ import os import torch import detectron2.data.transforms as T from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import get_cfg from detectron2.data import MetadataCatalog, build_detection_train_loader from detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, launch from detectron2.evaluation import ( CityscapesInstanceEvaluator, CityscapesSemSegEvaluator, COCOEvaluator, COCOPanopticEvaluator, DatasetEvaluators, ) from detectron2.projects.deeplab import build_lr_scheduler from detectron2.projects.panoptic_deeplab import ( PanopticDeeplabDatasetMapper, add_panoptic_deeplab_config, ) from detectron2.solver import get_default_optimizer_params from detectron2.solver.build import maybe_add_gradient_clipping def build_sem_seg_train_aug(cfg): augs = [ T.ResizeShortestEdge( cfg.INPUT.MIN_SIZE_TRAIN, cfg.INPUT.MAX_SIZE_TRAIN, cfg.INPUT.MIN_SIZE_TRAIN_SAMPLING ) ] if cfg.INPUT.CROP.ENABLED: augs.append(T.RandomCrop(cfg.INPUT.CROP.TYPE, cfg.INPUT.CROP.SIZE)) augs.append(T.RandomFlip()) return augs class Trainer(DefaultTrainer): """ We use the "DefaultTrainer" which contains a number pre-defined logic for standard training workflow. They may not work for you, especially if you are working on a new research project. In that case you can use the cleaner "SimpleTrainer", or write your own training loop. """ @classmethod def build_evaluator(cls, cfg, dataset_name, output_folder=None): """ Create evaluator(s) for a given dataset. This uses the special metadata "evaluator_type" associated with each builtin dataset. For your own dataset, you can simply create an evaluator manually in your script and do not have to worry about the hacky if-else logic here. """ if cfg.MODEL.PANOPTIC_DEEPLAB.BENCHMARK_NETWORK_SPEED: return None if output_folder is None: output_folder = os.path.join(cfg.OUTPUT_DIR, "inference") evaluator_list = [] evaluator_type = MetadataCatalog.get(dataset_name).evaluator_type if evaluator_type in ["cityscapes_panoptic_seg", "coco_panoptic_seg"]: evaluator_list.append(COCOPanopticEvaluator(dataset_name, output_folder)) if evaluator_type == "cityscapes_panoptic_seg": evaluator_list.append(CityscapesSemSegEvaluator(dataset_name)) evaluator_list.append(CityscapesInstanceEvaluator(dataset_name)) if evaluator_type == "coco_panoptic_seg": # `thing_classes` in COCO panoptic metadata includes both thing and # stuff classes for visualization. COCOEvaluator requires metadata # which only contains thing classes, thus we map the name of # panoptic datasets to their corresponding instance datasets. dataset_name_mapper = { "coco_2017_val_panoptic": "coco_2017_val", "coco_2017_val_100_panoptic": "coco_2017_val_100", } evaluator_list.append( COCOEvaluator(dataset_name_mapper[dataset_name], output_dir=output_folder) ) if len(evaluator_list) == 0: raise NotImplementedError( "no Evaluator for the dataset {} with the type {}".format( dataset_name, evaluator_type ) ) elif len(evaluator_list) == 1: return evaluator_list[0] return DatasetEvaluators(evaluator_list) @classmethod def build_train_loader(cls, cfg): mapper = PanopticDeeplabDatasetMapper(cfg, augmentations=build_sem_seg_train_aug(cfg)) return build_detection_train_loader(cfg, mapper=mapper) @classmethod def build_lr_scheduler(cls, cfg, optimizer): """ It now calls :func:`detectron2.solver.build_lr_scheduler`. Overwrite it if you'd like a different scheduler. """ return build_lr_scheduler(cfg, optimizer) @classmethod def build_optimizer(cls, cfg, model): """ Build an optimizer from config. """ params = get_default_optimizer_params( model, weight_decay=cfg.SOLVER.WEIGHT_DECAY, weight_decay_norm=cfg.SOLVER.WEIGHT_DECAY_NORM, ) optimizer_type = cfg.SOLVER.OPTIMIZER if optimizer_type == "SGD": return maybe_add_gradient_clipping(cfg, torch.optim.SGD)( params, cfg.SOLVER.BASE_LR, momentum=cfg.SOLVER.MOMENTUM, nesterov=cfg.SOLVER.NESTEROV, ) elif optimizer_type == "ADAM": return maybe_add_gradient_clipping(cfg, torch.optim.Adam)(params, cfg.SOLVER.BASE_LR) else: raise NotImplementedError(f"no optimizer type {optimizer_type}") def setup(args): """ Create configs and perform basic setups. """ cfg = get_cfg() add_panoptic_deeplab_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.freeze() default_setup(cfg, args) return cfg def main(args): cfg = setup(args) if args.eval_only: model = Trainer.build_model(cfg) DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load( cfg.MODEL.WEIGHTS, resume=args.resume ) res = Trainer.test(cfg, model) return res trainer = Trainer(cfg) trainer.resume_or_load(resume=args.resume) return trainer.train() if __name__ == "__main__": args = default_argument_parser().parse_args() print("Command Line Args:", args) launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), ) ================================================ FILE: detectron2/projects/PointRend/README.md ================================================ # PointRend: Image Segmentation as Rendering Alexander Kirillov, Yuxin Wu, Kaiming He, Ross Girshick [[`arXiv`](https://arxiv.org/abs/1912.08193)] [[`BibTeX`](#CitingPointRend)]

In this repository, we release code for PointRend in Detectron2. PointRend can be flexibly applied to both instance and semantic segmentation tasks by building on top of existing state-of-the-art models. ## Quick start and visualization This [Colab Notebook](https://colab.research.google.com/drive/1isGPL5h5_cKoPPhVL9XhMokRtHDvmMVL) tutorial contains examples of PointRend usage and visualizations of its point sampling stages. ## Training To train a model with 8 GPUs run: ```bash cd /path/to/detectron2/projects/PointRend python train_net.py --config-file configs/InstanceSegmentation/pointrend_rcnn_R_50_FPN_1x_coco.yaml --num-gpus 8 ``` ## Evaluation Model evaluation can be done similarly: ```bash cd /path/to/detectron2/projects/PointRend python train_net.py --config-file configs/InstanceSegmentation/pointrend_rcnn_R_50_FPN_1x_coco.yaml --eval-only MODEL.WEIGHTS /path/to/model_checkpoint ``` # Pretrained Models ## Instance Segmentation #### COCO
Mask
head
Backbone lr
sched
Output
resolution
mask
AP
mask
AP*
model id download
PointRend R50-FPN 224×224 36.2 39.7 164254221 model | metrics
PointRend R50-FPN 224×224 38.3 41.6 164955410 model | metrics
PointRend R101-FPN 224×224 40.1 43.8 model | metrics
PointRend X101-FPN 224×224 41.1 44.7 model | metrics
AP* is COCO mask AP evaluated against the higher-quality LVIS annotations; see the paper for details. Run `python detectron2/datasets/prepare_cocofied_lvis.py` to prepare GT files for AP* evaluation. Since LVIS annotations are not exhaustive, `lvis-api` and not `cocoapi` should be used to evaluate AP*. #### Cityscapes Cityscapes model is trained with ImageNet pretraining.
Mask
head
Backbone lr
sched
Output
resolution
mask
AP
model id download
PointRend R50-FPN 224×224 35.9 164255101 model | metrics
## Semantic Segmentation #### Cityscapes Cityscapes model is trained with ImageNet pretraining.
Method Backbone Output
resolution
mIoU model id download
SemanticFPN + PointRend R101-FPN 1024×2048 78.9 202576688 model | metrics
## Citing PointRend If you use PointRend, please use the following BibTeX entry. ```BibTeX @InProceedings{kirillov2019pointrend, title={{PointRend}: Image Segmentation as Rendering}, author={Alexander Kirillov and Yuxin Wu and Kaiming He and Ross Girshick}, journal={ArXiv:1912.08193}, year={2019} } ``` ## Citing Implicit PointRend If you use Implicit PointRend, please use the following BibTeX entry. ```BibTeX @InProceedings{cheng2021pointly, title={Pointly-Supervised Instance Segmentation, author={Bowen Cheng and Omkar Parkhi and Alexander Kirillov}, journal={ArXiv}, year={2021} } ``` ================================================ FILE: detectron2/projects/PointRend/configs/InstanceSegmentation/Base-Implicit-PointRend.yaml ================================================ _BASE_: "../../../../configs/Base-RCNN-FPN.yaml" MODEL: MASK_ON: true ROI_MASK_HEAD: NAME: "ImplicitPointRendMaskHead" POOLER_TYPE: "" # No RoI pooling, let the head process image features directly FC_DIM: 1024 NUM_FC: 2 POINT_HEAD: NAME: "ImplicitPointHead" FC_DIM: 256 NUM_FC: 3 IN_FEATURES: ["p2"] NUM_CLASSES: 80 CLS_AGNOSTIC_MASK: False TRAIN_NUM_POINTS: 196 SUBDIVISION_STEPS: 3 SUBDIVISION_NUM_POINTS: 784 IMPLICIT_POINTREND: IMAGE_FEATURE_ENABLED: True POS_ENC_ENABLED: True PARAMS_L2_REGULARIZER: 0.00001 INPUT: # PointRend for instance segmentation does not work with "polygon" mask_format. MASK_FORMAT: "bitmask" ================================================ FILE: detectron2/projects/PointRend/configs/InstanceSegmentation/Base-PointRend-RCNN-FPN.yaml ================================================ _BASE_: "../../../../configs/Base-RCNN-FPN.yaml" MODEL: MASK_ON: true ROI_BOX_HEAD: TRAIN_ON_PRED_BOXES: True ROI_MASK_HEAD: POOLER_TYPE: "" # No RoI pooling, let the head process image features directly NAME: "PointRendMaskHead" FC_DIM: 1024 NUM_FC: 2 OUTPUT_SIDE_RESOLUTION: 7 IN_FEATURES: ["p2"] # for the coarse mask head POINT_HEAD_ON: True POINT_HEAD: FC_DIM: 256 NUM_FC: 3 IN_FEATURES: ["p2"] INPUT: # PointRend for instance segmentation does not work with "polygon" mask_format. MASK_FORMAT: "bitmask" ================================================ FILE: detectron2/projects/PointRend/configs/InstanceSegmentation/implicit_pointrend_R_50_FPN_1x_coco.yaml ================================================ _BASE_: "Base-Implicit-PointRend.yaml" MODEL: WEIGHTS: detectron2://ImageNetPretrained/MSRA/R-50.pkl RESNETS: DEPTH: 50 # To add COCO AP evaluation against the higher-quality LVIS annotations. # DATASETS: # TEST: ("coco_2017_val", "lvis_v0.5_val_cocofied") ================================================ FILE: detectron2/projects/PointRend/configs/InstanceSegmentation/implicit_pointrend_R_50_FPN_3x_coco.yaml ================================================ _BASE_: "Base-Implicit-PointRend.yaml" MODEL: WEIGHTS: detectron2://ImageNetPretrained/MSRA/R-50.pkl RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 # To add COCO AP evaluation against the higher-quality LVIS annotations. # DATASETS: # TEST: ("coco_2017_val", "lvis_v0.5_val_cocofied") ================================================ FILE: detectron2/projects/PointRend/configs/InstanceSegmentation/pointrend_rcnn_R_101_FPN_3x_coco.yaml ================================================ _BASE_: Base-PointRend-RCNN-FPN.yaml MODEL: WEIGHTS: detectron2://ImageNetPretrained/MSRA/R-101.pkl MASK_ON: true RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 # To add COCO AP evaluation against the higher-quality LVIS annotations. # DATASETS: # TEST: ("coco_2017_val", "lvis_v0.5_val_cocofied") ================================================ FILE: detectron2/projects/PointRend/configs/InstanceSegmentation/pointrend_rcnn_R_50_FPN_1x_cityscapes.yaml ================================================ _BASE_: Base-PointRend-RCNN-FPN.yaml MODEL: WEIGHTS: detectron2://ImageNetPretrained/MSRA/R-50.pkl RESNETS: DEPTH: 50 ROI_HEADS: NUM_CLASSES: 8 POINT_HEAD: NUM_CLASSES: 8 DATASETS: TEST: ("cityscapes_fine_instance_seg_val",) TRAIN: ("cityscapes_fine_instance_seg_train",) SOLVER: BASE_LR: 0.01 IMS_PER_BATCH: 8 MAX_ITER: 24000 STEPS: (18000,) INPUT: MAX_SIZE_TEST: 2048 MAX_SIZE_TRAIN: 2048 MIN_SIZE_TEST: 1024 MIN_SIZE_TRAIN: (800, 832, 864, 896, 928, 960, 992, 1024) ================================================ FILE: detectron2/projects/PointRend/configs/InstanceSegmentation/pointrend_rcnn_R_50_FPN_1x_coco.yaml ================================================ _BASE_: Base-PointRend-RCNN-FPN.yaml MODEL: WEIGHTS: detectron2://ImageNetPretrained/MSRA/R-50.pkl RESNETS: DEPTH: 50 # To add COCO AP evaluation against the higher-quality LVIS annotations. # DATASETS: # TEST: ("coco_2017_val", "lvis_v0.5_val_cocofied") ================================================ FILE: detectron2/projects/PointRend/configs/InstanceSegmentation/pointrend_rcnn_R_50_FPN_3x_coco.yaml ================================================ _BASE_: Base-PointRend-RCNN-FPN.yaml MODEL: WEIGHTS: detectron2://ImageNetPretrained/MSRA/R-50.pkl RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 # To add COCO AP evaluation against the higher-quality LVIS annotations. # DATASETS: # TEST: ("coco_2017_val", "lvis_v0.5_val_cocofied") ================================================ FILE: detectron2/projects/PointRend/configs/InstanceSegmentation/pointrend_rcnn_X_101_32x8d_FPN_3x_coco.yaml ================================================ _BASE_: Base-PointRend-RCNN-FPN.yaml MODEL: MASK_ON: True WEIGHTS: "detectron2://ImageNetPretrained/FAIR/X-101-32x8d.pkl" PIXEL_STD: [57.375, 57.120, 58.395] RESNETS: STRIDE_IN_1X1: False # this is a C2 model NUM_GROUPS: 32 WIDTH_PER_GROUP: 8 DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 # To add COCO AP evaluation against the higher-quality LVIS annotations. # DATASETS: # TEST: ("coco_2017_val", "lvis_v0.5_val_cocofied") ================================================ FILE: detectron2/projects/PointRend/configs/SemanticSegmentation/Base-PointRend-Semantic-FPN.yaml ================================================ _BASE_: "../../../../configs/Base-RCNN-FPN.yaml" MODEL: META_ARCHITECTURE: "SemanticSegmentor" BACKBONE: FREEZE_AT: 0 SEM_SEG_HEAD: NAME: "PointRendSemSegHead" POINT_HEAD: NUM_CLASSES: 54 FC_DIM: 256 NUM_FC: 3 IN_FEATURES: ["p2"] TRAIN_NUM_POINTS: 1024 SUBDIVISION_STEPS: 2 SUBDIVISION_NUM_POINTS: 8192 COARSE_SEM_SEG_HEAD_NAME: "SemSegFPNHead" COARSE_PRED_EACH_LAYER: False DATASETS: TRAIN: ("coco_2017_train_panoptic_stuffonly",) TEST: ("coco_2017_val_panoptic_stuffonly",) ================================================ FILE: detectron2/projects/PointRend/configs/SemanticSegmentation/pointrend_semantic_R_101_FPN_1x_cityscapes.yaml ================================================ _BASE_: Base-PointRend-Semantic-FPN.yaml MODEL: WEIGHTS: detectron2://ImageNetPretrained/MSRA/R-101.pkl RESNETS: DEPTH: 101 SEM_SEG_HEAD: NUM_CLASSES: 19 POINT_HEAD: NUM_CLASSES: 19 TRAIN_NUM_POINTS: 2048 SUBDIVISION_NUM_POINTS: 8192 DATASETS: TRAIN: ("cityscapes_fine_sem_seg_train",) TEST: ("cityscapes_fine_sem_seg_val",) SOLVER: BASE_LR: 0.01 STEPS: (40000, 55000) MAX_ITER: 65000 IMS_PER_BATCH: 32 INPUT: MIN_SIZE_TRAIN: (512, 768, 1024, 1280, 1536, 1792, 2048) MIN_SIZE_TRAIN_SAMPLING: "choice" MIN_SIZE_TEST: 1024 MAX_SIZE_TRAIN: 4096 MAX_SIZE_TEST: 2048 CROP: ENABLED: True TYPE: "absolute" SIZE: (512, 1024) SINGLE_CATEGORY_MAX_AREA: 0.75 COLOR_AUG_SSD: True DATALOADER: NUM_WORKERS: 10 ================================================ FILE: detectron2/projects/PointRend/point_rend/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .config import add_pointrend_config from .mask_head import PointRendMaskHead, ImplicitPointRendMaskHead from .semantic_seg import PointRendSemSegHead from .color_augmentation import ColorAugSSDTransform from . import roi_heads as _ # only registration ================================================ FILE: detectron2/projects/PointRend/point_rend/color_augmentation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import random import cv2 from fvcore.transforms.transform import Transform class ColorAugSSDTransform(Transform): """ A color related data augmentation used in Single Shot Multibox Detector (SSD). Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, Scott Reed, Cheng-Yang Fu, Alexander C. Berg. SSD: Single Shot MultiBox Detector. ECCV 2016. Implementation based on: https://github.com/weiliu89/caffe/blob /4817bf8b4200b35ada8ed0dc378dceaf38c539e4 /src/caffe/util/im_transforms.cpp https://github.com/chainer/chainercv/blob /7159616642e0be7c5b3ef380b848e16b7e99355b/chainercv /links/model/ssd/transforms.py """ def __init__( self, img_format, brightness_delta=32, contrast_low=0.5, contrast_high=1.5, saturation_low=0.5, saturation_high=1.5, hue_delta=18, ): super().__init__() assert img_format in ["BGR", "RGB"] self.is_rgb = img_format == "RGB" del img_format self._set_attributes(locals()) def apply_coords(self, coords): return coords def apply_segmentation(self, segmentation): return segmentation def apply_image(self, img, interp=None): if self.is_rgb: img = img[:, :, [2, 1, 0]] img = self.brightness(img) if random.randrange(2): img = self.contrast(img) img = self.saturation(img) img = self.hue(img) else: img = self.saturation(img) img = self.hue(img) img = self.contrast(img) if self.is_rgb: img = img[:, :, [2, 1, 0]] return img def convert(self, img, alpha=1, beta=0): img = img.astype(np.float32) * alpha + beta img = np.clip(img, 0, 255) return img.astype(np.uint8) def brightness(self, img): if random.randrange(2): return self.convert( img, beta=random.uniform(-self.brightness_delta, self.brightness_delta) ) return img def contrast(self, img): if random.randrange(2): return self.convert(img, alpha=random.uniform(self.contrast_low, self.contrast_high)) return img def saturation(self, img): if random.randrange(2): img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) img[:, :, 1] = self.convert( img[:, :, 1], alpha=random.uniform(self.saturation_low, self.saturation_high) ) return cv2.cvtColor(img, cv2.COLOR_HSV2BGR) return img def hue(self, img): if random.randrange(2): img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) img[:, :, 0] = ( img[:, :, 0].astype(int) + random.randint(-self.hue_delta, self.hue_delta) ) % 180 return cv2.cvtColor(img, cv2.COLOR_HSV2BGR) return img ================================================ FILE: detectron2/projects/PointRend/point_rend/config.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.config import CfgNode as CN def add_pointrend_config(cfg): """ Add config for PointRend. """ # We retry random cropping until no single category in semantic segmentation GT occupies more # than `SINGLE_CATEGORY_MAX_AREA` part of the crop. cfg.INPUT.CROP.SINGLE_CATEGORY_MAX_AREA = 1.0 # Color augmentatition from SSD paper for semantic segmentation model during training. cfg.INPUT.COLOR_AUG_SSD = False # Names of the input feature maps to be used by a coarse mask head. cfg.MODEL.ROI_MASK_HEAD.IN_FEATURES = ("p2",) cfg.MODEL.ROI_MASK_HEAD.FC_DIM = 1024 cfg.MODEL.ROI_MASK_HEAD.NUM_FC = 2 # The side size of a coarse mask head prediction. cfg.MODEL.ROI_MASK_HEAD.OUTPUT_SIDE_RESOLUTION = 7 # True if point head is used. cfg.MODEL.ROI_MASK_HEAD.POINT_HEAD_ON = False cfg.MODEL.POINT_HEAD = CN() cfg.MODEL.POINT_HEAD.NAME = "StandardPointHead" cfg.MODEL.POINT_HEAD.NUM_CLASSES = 80 # Names of the input feature maps to be used by a mask point head. cfg.MODEL.POINT_HEAD.IN_FEATURES = ("p2",) # Number of points sampled during training for a mask point head. cfg.MODEL.POINT_HEAD.TRAIN_NUM_POINTS = 14 * 14 # Oversampling parameter for PointRend point sampling during training. Parameter `k` in the # original paper. cfg.MODEL.POINT_HEAD.OVERSAMPLE_RATIO = 3 # Importance sampling parameter for PointRend point sampling during training. Parametr `beta` in # the original paper. cfg.MODEL.POINT_HEAD.IMPORTANCE_SAMPLE_RATIO = 0.75 # Number of subdivision steps during inference. cfg.MODEL.POINT_HEAD.SUBDIVISION_STEPS = 5 # Maximum number of points selected at each subdivision step (N). cfg.MODEL.POINT_HEAD.SUBDIVISION_NUM_POINTS = 28 * 28 cfg.MODEL.POINT_HEAD.FC_DIM = 256 cfg.MODEL.POINT_HEAD.NUM_FC = 3 cfg.MODEL.POINT_HEAD.CLS_AGNOSTIC_MASK = False # If True, then coarse prediction features are used as inout for each layer in PointRend's MLP. cfg.MODEL.POINT_HEAD.COARSE_PRED_EACH_LAYER = True cfg.MODEL.POINT_HEAD.COARSE_SEM_SEG_HEAD_NAME = "SemSegFPNHead" """ Add config for Implicit PointRend. """ cfg.MODEL.IMPLICIT_POINTREND = CN() cfg.MODEL.IMPLICIT_POINTREND.IMAGE_FEATURE_ENABLED = True cfg.MODEL.IMPLICIT_POINTREND.POS_ENC_ENABLED = True cfg.MODEL.IMPLICIT_POINTREND.PARAMS_L2_REGULARIZER = 0.00001 ================================================ FILE: detectron2/projects/PointRend/point_rend/mask_head.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import math import numpy as np from typing import Dict, List, Tuple import fvcore.nn.weight_init as weight_init import torch from torch import Tensor, nn from torch.nn import functional as F from detectron2.config import configurable from detectron2.layers import Conv2d, ShapeSpec, cat, interpolate from detectron2.modeling import ROI_MASK_HEAD_REGISTRY from detectron2.modeling.roi_heads.mask_head import mask_rcnn_inference, mask_rcnn_loss from detectron2.structures import Boxes from .point_features import ( generate_regular_grid_point_coords, get_point_coords_wrt_image, get_uncertain_point_coords_on_grid, get_uncertain_point_coords_with_randomness, point_sample, point_sample_fine_grained_features, sample_point_labels, ) from .point_head import build_point_head, roi_mask_point_loss def calculate_uncertainty(logits, classes): """ We estimate uncerainty as L1 distance between 0.0 and the logit prediction in 'logits' for the foreground class in `classes`. Args: logits (Tensor): A tensor of shape (R, C, ...) or (R, 1, ...) for class-specific or class-agnostic, where R is the total number of predicted masks in all images and C is the number of foreground classes. The values are logits. classes (list): A list of length R that contains either predicted of ground truth class for eash predicted mask. Returns: scores (Tensor): A tensor of shape (R, 1, ...) that contains uncertainty scores with the most uncertain locations having the highest uncertainty score. """ if logits.shape[1] == 1: gt_class_logits = logits.clone() else: gt_class_logits = logits[ torch.arange(logits.shape[0], device=logits.device), classes ].unsqueeze(1) return -(torch.abs(gt_class_logits)) class ConvFCHead(nn.Module): """ A mask head with fully connected layers. Given pooled features it first reduces channels and spatial dimensions with conv layers and then uses FC layers to predict coarse masks analogously to the standard box head. """ _version = 2 @configurable def __init__( self, input_shape: ShapeSpec, *, conv_dim: int, fc_dims: List[int], output_shape: Tuple[int] ): """ Args: conv_dim: the output dimension of the conv layers fc_dims: a list of N>0 integers representing the output dimensions of N FC layers output_shape: shape of the output mask prediction """ super().__init__() # fmt: off input_channels = input_shape.channels input_h = input_shape.height input_w = input_shape.width self.output_shape = output_shape # fmt: on self.conv_layers = [] if input_channels > conv_dim: self.reduce_channel_dim_conv = Conv2d( input_channels, conv_dim, kernel_size=1, stride=1, padding=0, bias=True, activation=F.relu, ) self.conv_layers.append(self.reduce_channel_dim_conv) self.reduce_spatial_dim_conv = Conv2d( conv_dim, conv_dim, kernel_size=2, stride=2, padding=0, bias=True, activation=F.relu ) self.conv_layers.append(self.reduce_spatial_dim_conv) input_dim = conv_dim * input_h * input_w input_dim //= 4 self.fcs = [] for k, fc_dim in enumerate(fc_dims): fc = nn.Linear(input_dim, fc_dim) self.add_module("fc{}".format(k + 1), fc) self.fcs.append(fc) input_dim = fc_dim output_dim = int(np.prod(self.output_shape)) self.prediction = nn.Linear(fc_dims[-1], output_dim) # use normal distribution initialization for mask prediction layer nn.init.normal_(self.prediction.weight, std=0.001) nn.init.constant_(self.prediction.bias, 0) for layer in self.conv_layers: weight_init.c2_msra_fill(layer) for layer in self.fcs: weight_init.c2_xavier_fill(layer) @classmethod def from_config(cls, cfg, input_shape): output_shape = ( cfg.MODEL.ROI_HEADS.NUM_CLASSES, cfg.MODEL.ROI_MASK_HEAD.OUTPUT_SIDE_RESOLUTION, cfg.MODEL.ROI_MASK_HEAD.OUTPUT_SIDE_RESOLUTION, ) fc_dim = cfg.MODEL.ROI_MASK_HEAD.FC_DIM num_fc = cfg.MODEL.ROI_MASK_HEAD.NUM_FC ret = dict( input_shape=input_shape, conv_dim=cfg.MODEL.ROI_MASK_HEAD.CONV_DIM, fc_dims=[fc_dim] * num_fc, output_shape=output_shape, ) return ret def forward(self, x): N = x.shape[0] for layer in self.conv_layers: x = layer(x) x = torch.flatten(x, start_dim=1) for layer in self.fcs: x = F.relu(layer(x)) output_shape = [N] + list(self.output_shape) return self.prediction(x).view(*output_shape) def _load_from_state_dict( self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs ): version = local_metadata.get("version", None) if version is None or version < 2: logger = logging.getLogger(__name__) logger.warning( "Weight format of PointRend models have changed! " "Applying automatic conversion now ..." ) for k in list(state_dict.keys()): newk = k if k.startswith(prefix + "coarse_mask_fc"): newk = k.replace(prefix + "coarse_mask_fc", prefix + "fc") if newk != k: state_dict[newk] = state_dict[k] del state_dict[k] @ROI_MASK_HEAD_REGISTRY.register() class PointRendMaskHead(nn.Module): def __init__(self, cfg, input_shape: Dict[str, ShapeSpec]): super().__init__() self._feature_scales = {k: 1.0 / v.stride for k, v in input_shape.items()} # point head self._init_point_head(cfg, input_shape) # coarse mask head self.roi_pooler_in_features = cfg.MODEL.ROI_MASK_HEAD.IN_FEATURES self.roi_pooler_size = cfg.MODEL.ROI_MASK_HEAD.POOLER_RESOLUTION self._feature_scales = {k: 1.0 / v.stride for k, v in input_shape.items()} in_channels = np.sum([input_shape[f].channels for f in self.roi_pooler_in_features]) self._init_roi_head( cfg, ShapeSpec( channels=in_channels, width=self.roi_pooler_size, height=self.roi_pooler_size, ), ) def _init_roi_head(self, cfg, input_shape): self.coarse_head = ConvFCHead(cfg, input_shape) def _init_point_head(self, cfg, input_shape): # fmt: off self.mask_point_on = cfg.MODEL.ROI_MASK_HEAD.POINT_HEAD_ON if not self.mask_point_on: return assert cfg.MODEL.ROI_HEADS.NUM_CLASSES == cfg.MODEL.POINT_HEAD.NUM_CLASSES self.mask_point_in_features = cfg.MODEL.POINT_HEAD.IN_FEATURES self.mask_point_train_num_points = cfg.MODEL.POINT_HEAD.TRAIN_NUM_POINTS self.mask_point_oversample_ratio = cfg.MODEL.POINT_HEAD.OVERSAMPLE_RATIO self.mask_point_importance_sample_ratio = cfg.MODEL.POINT_HEAD.IMPORTANCE_SAMPLE_RATIO # next three parameters are use in the adaptive subdivions inference procedure self.mask_point_subdivision_init_resolution = cfg.MODEL.ROI_MASK_HEAD.OUTPUT_SIDE_RESOLUTION self.mask_point_subdivision_steps = cfg.MODEL.POINT_HEAD.SUBDIVISION_STEPS self.mask_point_subdivision_num_points = cfg.MODEL.POINT_HEAD.SUBDIVISION_NUM_POINTS # fmt: on in_channels = int(np.sum([input_shape[f].channels for f in self.mask_point_in_features])) self.point_head = build_point_head(cfg, ShapeSpec(channels=in_channels, width=1, height=1)) # An optimization to skip unused subdivision steps: if after subdivision, all pixels on # the mask will be selected and recomputed anyway, we should just double our init_resolution while ( 4 * self.mask_point_subdivision_init_resolution**2 <= self.mask_point_subdivision_num_points ): self.mask_point_subdivision_init_resolution *= 2 self.mask_point_subdivision_steps -= 1 def forward(self, features, instances): """ Args: features (dict[str, Tensor]): a dict of image-level features instances (list[Instances]): proposals in training; detected instances in inference """ if self.training: proposal_boxes = [x.proposal_boxes for x in instances] coarse_mask = self.coarse_head(self._roi_pooler(features, proposal_boxes)) losses = {"loss_mask": mask_rcnn_loss(coarse_mask, instances)} if not self.mask_point_on: return losses point_coords, point_labels = self._sample_train_points(coarse_mask, instances) point_fine_grained_features = self._point_pooler(features, proposal_boxes, point_coords) point_logits = self._get_point_logits( point_fine_grained_features, point_coords, coarse_mask ) losses["loss_mask_point"] = roi_mask_point_loss(point_logits, instances, point_labels) return losses else: pred_boxes = [x.pred_boxes for x in instances] coarse_mask = self.coarse_head(self._roi_pooler(features, pred_boxes)) return self._subdivision_inference(features, coarse_mask, instances) def _roi_pooler(self, features: List[Tensor], boxes: List[Boxes]): """ Extract per-box feature. This is similar to RoIAlign(sampling_ratio=1) except: 1. It's implemented by point_sample 2. It pools features across all levels and concat them, while typically RoIAlign select one level for every box. However in the config we only use one level (p2) so there is no difference. Returns: Tensor of shape (R, C, pooler_size, pooler_size) where R is the total number of boxes """ features_list = [features[k] for k in self.roi_pooler_in_features] features_scales = [self._feature_scales[k] for k in self.roi_pooler_in_features] num_boxes = sum(x.tensor.size(0) for x in boxes) output_size = self.roi_pooler_size point_coords = generate_regular_grid_point_coords(num_boxes, output_size, boxes[0].device) # For regular grids of points, this function is equivalent to `len(features_list)' calls # of `ROIAlign` (with `SAMPLING_RATIO=1`), and concat the results. roi_features, _ = point_sample_fine_grained_features( features_list, features_scales, boxes, point_coords ) return roi_features.view(num_boxes, roi_features.shape[1], output_size, output_size) def _sample_train_points(self, coarse_mask, instances): assert self.training gt_classes = cat([x.gt_classes for x in instances]) with torch.no_grad(): # sample point_coords point_coords = get_uncertain_point_coords_with_randomness( coarse_mask, lambda logits: calculate_uncertainty(logits, gt_classes), self.mask_point_train_num_points, self.mask_point_oversample_ratio, self.mask_point_importance_sample_ratio, ) # sample point_labels proposal_boxes = [x.proposal_boxes for x in instances] cat_boxes = Boxes.cat(proposal_boxes) point_coords_wrt_image = get_point_coords_wrt_image(cat_boxes.tensor, point_coords) point_labels = sample_point_labels(instances, point_coords_wrt_image) return point_coords, point_labels def _point_pooler(self, features, proposal_boxes, point_coords): point_features_list = [features[k] for k in self.mask_point_in_features] point_features_scales = [self._feature_scales[k] for k in self.mask_point_in_features] # sample image-level features point_fine_grained_features, _ = point_sample_fine_grained_features( point_features_list, point_features_scales, proposal_boxes, point_coords ) return point_fine_grained_features def _get_point_logits(self, point_fine_grained_features, point_coords, coarse_mask): coarse_features = point_sample(coarse_mask, point_coords, align_corners=False) point_logits = self.point_head(point_fine_grained_features, coarse_features) return point_logits def _subdivision_inference(self, features, mask_representations, instances): assert not self.training pred_boxes = [x.pred_boxes for x in instances] pred_classes = cat([x.pred_classes for x in instances]) mask_logits = None # +1 here to include an initial step to generate the coarsest mask # prediction with init_resolution, when mask_logits is None. # We compute initial mask by sampling on a regular grid. coarse_mask # can be used as initial mask as well, but it's typically very low-res # so it will be completely overwritten during subdivision anyway. for _ in range(self.mask_point_subdivision_steps + 1): if mask_logits is None: point_coords = generate_regular_grid_point_coords( pred_classes.size(0), self.mask_point_subdivision_init_resolution, pred_boxes[0].device, ) else: mask_logits = interpolate( mask_logits, scale_factor=2, mode="bilinear", align_corners=False ) uncertainty_map = calculate_uncertainty(mask_logits, pred_classes) point_indices, point_coords = get_uncertain_point_coords_on_grid( uncertainty_map, self.mask_point_subdivision_num_points ) # Run the point head for every point in point_coords fine_grained_features = self._point_pooler(features, pred_boxes, point_coords) point_logits = self._get_point_logits( fine_grained_features, point_coords, mask_representations ) if mask_logits is None: # Create initial mask_logits using point_logits on this regular grid R, C, _ = point_logits.shape mask_logits = point_logits.reshape( R, C, self.mask_point_subdivision_init_resolution, self.mask_point_subdivision_init_resolution, ) # The subdivision code will fail with the empty list of boxes if len(pred_classes) == 0: mask_rcnn_inference(mask_logits, instances) return instances else: # Put point predictions to the right places on the upsampled grid. R, C, H, W = mask_logits.shape point_indices = point_indices.unsqueeze(1).expand(-1, C, -1) mask_logits = ( mask_logits.reshape(R, C, H * W) .scatter_(2, point_indices, point_logits) .view(R, C, H, W) ) mask_rcnn_inference(mask_logits, instances) return instances @ROI_MASK_HEAD_REGISTRY.register() class ImplicitPointRendMaskHead(PointRendMaskHead): def __init__(self, cfg, input_shape: Dict[str, ShapeSpec]): super().__init__(cfg, input_shape) def _init_roi_head(self, cfg, input_shape): assert hasattr(self, "num_params"), "Please initialize point_head first!" self.parameter_head = ConvFCHead(cfg, input_shape, output_shape=(self.num_params,)) self.regularizer = cfg.MODEL.IMPLICIT_POINTREND.PARAMS_L2_REGULARIZER def _init_point_head(self, cfg, input_shape): # fmt: off self.mask_point_on = True # always on assert cfg.MODEL.ROI_HEADS.NUM_CLASSES == cfg.MODEL.POINT_HEAD.NUM_CLASSES self.mask_point_in_features = cfg.MODEL.POINT_HEAD.IN_FEATURES self.mask_point_train_num_points = cfg.MODEL.POINT_HEAD.TRAIN_NUM_POINTS # next two parameters are use in the adaptive subdivions inference procedure self.mask_point_subdivision_steps = cfg.MODEL.POINT_HEAD.SUBDIVISION_STEPS self.mask_point_subdivision_num_points = cfg.MODEL.POINT_HEAD.SUBDIVISION_NUM_POINTS # fmt: on in_channels = int(np.sum([input_shape[f].channels for f in self.mask_point_in_features])) self.point_head = build_point_head(cfg, ShapeSpec(channels=in_channels, width=1, height=1)) self.num_params = self.point_head.num_params # inference parameters self.mask_point_subdivision_init_resolution = int( math.sqrt(self.mask_point_subdivision_num_points) ) assert ( self.mask_point_subdivision_init_resolution * self.mask_point_subdivision_init_resolution == self.mask_point_subdivision_num_points ) def forward(self, features, instances): """ Args: features (dict[str, Tensor]): a dict of image-level features instances (list[Instances]): proposals in training; detected instances in inference """ if self.training: proposal_boxes = [x.proposal_boxes for x in instances] parameters = self.parameter_head(self._roi_pooler(features, proposal_boxes)) losses = {"loss_l2": self.regularizer * (parameters**2).mean()} point_coords, point_labels = self._uniform_sample_train_points(instances) point_fine_grained_features = self._point_pooler(features, proposal_boxes, point_coords) point_logits = self._get_point_logits( point_fine_grained_features, point_coords, parameters ) losses["loss_mask_point"] = roi_mask_point_loss(point_logits, instances, point_labels) return losses else: pred_boxes = [x.pred_boxes for x in instances] parameters = self.parameter_head(self._roi_pooler(features, pred_boxes)) return self._subdivision_inference(features, parameters, instances) def _uniform_sample_train_points(self, instances): assert self.training proposal_boxes = [x.proposal_boxes for x in instances] cat_boxes = Boxes.cat(proposal_boxes) # uniform sample point_coords = torch.rand( len(cat_boxes), self.mask_point_train_num_points, 2, device=cat_boxes.tensor.device ) # sample point_labels point_coords_wrt_image = get_point_coords_wrt_image(cat_boxes.tensor, point_coords) point_labels = sample_point_labels(instances, point_coords_wrt_image) return point_coords, point_labels def _get_point_logits(self, fine_grained_features, point_coords, parameters): return self.point_head(fine_grained_features, point_coords, parameters) ================================================ FILE: detectron2/projects/PointRend/point_rend/point_features.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch from torch.nn import functional as F from detectron2.layers import cat, shapes_to_tensor from detectron2.structures import BitMasks, Boxes """ Shape shorthand in this module: N: minibatch dimension size, i.e. the number of RoIs for instance segmenation or the number of images for semantic segmenation. R: number of ROIs, combined over all images, in the minibatch P: number of points """ def point_sample(input, point_coords, **kwargs): """ A wrapper around :function:`torch.nn.functional.grid_sample` to support 3D point_coords tensors. Unlike :function:`torch.nn.functional.grid_sample` it assumes `point_coords` to lie inside [0, 1] x [0, 1] square. Args: input (Tensor): A tensor of shape (N, C, H, W) that contains features map on a H x W grid. point_coords (Tensor): A tensor of shape (N, P, 2) or (N, Hgrid, Wgrid, 2) that contains [0, 1] x [0, 1] normalized point coordinates. Returns: output (Tensor): A tensor of shape (N, C, P) or (N, C, Hgrid, Wgrid) that contains features for points in `point_coords`. The features are obtained via bilinear interplation from `input` the same way as :function:`torch.nn.functional.grid_sample`. """ add_dim = False if point_coords.dim() == 3: add_dim = True point_coords = point_coords.unsqueeze(2) output = F.grid_sample(input, 2.0 * point_coords - 1.0, **kwargs) if add_dim: output = output.squeeze(3) return output def generate_regular_grid_point_coords(R, side_size, device): """ Generate regular square grid of points in [0, 1] x [0, 1] coordinate space. Args: R (int): The number of grids to sample, one for each region. side_size (int): The side size of the regular grid. device (torch.device): Desired device of returned tensor. Returns: (Tensor): A tensor of shape (R, side_size^2, 2) that contains coordinates for the regular grids. """ aff = torch.tensor([[[0.5, 0, 0.5], [0, 0.5, 0.5]]], device=device) r = F.affine_grid(aff, torch.Size((1, 1, side_size, side_size)), align_corners=False) return r.view(1, -1, 2).expand(R, -1, -1) def get_uncertain_point_coords_with_randomness( coarse_logits, uncertainty_func, num_points, oversample_ratio, importance_sample_ratio ): """ Sample points in [0, 1] x [0, 1] coordinate space based on their uncertainty. The unceratinties are calculated for each point using 'uncertainty_func' function that takes point's logit prediction as input. See PointRend paper for details. Args: coarse_logits (Tensor): A tensor of shape (N, C, Hmask, Wmask) or (N, 1, Hmask, Wmask) for class-specific or class-agnostic prediction. uncertainty_func: A function that takes a Tensor of shape (N, C, P) or (N, 1, P) that contains logit predictions for P points and returns their uncertainties as a Tensor of shape (N, 1, P). num_points (int): The number of points P to sample. oversample_ratio (int): Oversampling parameter. importance_sample_ratio (float): Ratio of points that are sampled via importnace sampling. Returns: point_coords (Tensor): A tensor of shape (N, P, 2) that contains the coordinates of P sampled points. """ assert oversample_ratio >= 1 assert importance_sample_ratio <= 1 and importance_sample_ratio >= 0 num_boxes = coarse_logits.shape[0] num_sampled = int(num_points * oversample_ratio) point_coords = torch.rand(num_boxes, num_sampled, 2, device=coarse_logits.device) point_logits = point_sample(coarse_logits, point_coords, align_corners=False) # It is crucial to calculate uncertainty based on the sampled prediction value for the points. # Calculating uncertainties of the coarse predictions first and sampling them for points leads # to incorrect results. # To illustrate this: assume uncertainty_func(logits)=-abs(logits), a sampled point between # two coarse predictions with -1 and 1 logits has 0 logits, and therefore 0 uncertainty value. # However, if we calculate uncertainties for the coarse predictions first, # both will have -1 uncertainty, and the sampled point will get -1 uncertainty. point_uncertainties = uncertainty_func(point_logits) num_uncertain_points = int(importance_sample_ratio * num_points) num_random_points = num_points - num_uncertain_points idx = torch.topk(point_uncertainties[:, 0, :], k=num_uncertain_points, dim=1)[1] shift = num_sampled * torch.arange(num_boxes, dtype=torch.long, device=coarse_logits.device) idx += shift[:, None] point_coords = point_coords.view(-1, 2)[idx.view(-1), :].view( num_boxes, num_uncertain_points, 2 ) if num_random_points > 0: point_coords = cat( [ point_coords, torch.rand(num_boxes, num_random_points, 2, device=coarse_logits.device), ], dim=1, ) return point_coords def get_uncertain_point_coords_on_grid(uncertainty_map, num_points): """ Find `num_points` most uncertain points from `uncertainty_map` grid. Args: uncertainty_map (Tensor): A tensor of shape (N, 1, H, W) that contains uncertainty values for a set of points on a regular H x W grid. num_points (int): The number of points P to select. Returns: point_indices (Tensor): A tensor of shape (N, P) that contains indices from [0, H x W) of the most uncertain points. point_coords (Tensor): A tensor of shape (N, P, 2) that contains [0, 1] x [0, 1] normalized coordinates of the most uncertain points from the H x W grid. """ R, _, H, W = uncertainty_map.shape h_step = 1.0 / float(H) w_step = 1.0 / float(W) num_points = min(H * W, num_points) point_indices = torch.topk(uncertainty_map.view(R, H * W), k=num_points, dim=1)[1] point_coords = torch.zeros(R, num_points, 2, dtype=torch.float, device=uncertainty_map.device) point_coords[:, :, 0] = w_step / 2.0 + (point_indices % W).to(torch.float) * w_step point_coords[:, :, 1] = h_step / 2.0 + (point_indices // W).to(torch.float) * h_step return point_indices, point_coords def point_sample_fine_grained_features(features_list, feature_scales, boxes, point_coords): """ Get features from feature maps in `features_list` that correspond to specific point coordinates inside each bounding box from `boxes`. Args: features_list (list[Tensor]): A list of feature map tensors to get features from. feature_scales (list[float]): A list of scales for tensors in `features_list`. boxes (list[Boxes]): A list of I Boxes objects that contain R_1 + ... + R_I = R boxes all together. point_coords (Tensor): A tensor of shape (R, P, 2) that contains [0, 1] x [0, 1] box-normalized coordinates of the P sampled points. Returns: point_features (Tensor): A tensor of shape (R, C, P) that contains features sampled from all features maps in feature_list for P sampled points for all R boxes in `boxes`. point_coords_wrt_image (Tensor): A tensor of shape (R, P, 2) that contains image-level coordinates of P points. """ cat_boxes = Boxes.cat(boxes) num_boxes = [b.tensor.size(0) for b in boxes] point_coords_wrt_image = get_point_coords_wrt_image(cat_boxes.tensor, point_coords) split_point_coords_wrt_image = torch.split(point_coords_wrt_image, num_boxes) point_features = [] for idx_img, point_coords_wrt_image_per_image in enumerate(split_point_coords_wrt_image): point_features_per_image = [] for idx_feature, feature_map in enumerate(features_list): h, w = feature_map.shape[-2:] scale = shapes_to_tensor([w, h]) / feature_scales[idx_feature] point_coords_scaled = point_coords_wrt_image_per_image / scale.to(feature_map.device) point_features_per_image.append( point_sample( feature_map[idx_img].unsqueeze(0), point_coords_scaled.unsqueeze(0), align_corners=False, ) .squeeze(0) .transpose(1, 0) ) point_features.append(cat(point_features_per_image, dim=1)) return cat(point_features, dim=0), point_coords_wrt_image def get_point_coords_wrt_image(boxes_coords, point_coords): """ Convert box-normalized [0, 1] x [0, 1] point cooordinates to image-level coordinates. Args: boxes_coords (Tensor): A tensor of shape (R, 4) that contains bounding boxes. coordinates. point_coords (Tensor): A tensor of shape (R, P, 2) that contains [0, 1] x [0, 1] box-normalized coordinates of the P sampled points. Returns: point_coords_wrt_image (Tensor): A tensor of shape (R, P, 2) that contains image-normalized coordinates of P sampled points. """ with torch.no_grad(): point_coords_wrt_image = point_coords.clone() point_coords_wrt_image[:, :, 0] = point_coords_wrt_image[:, :, 0] * ( boxes_coords[:, None, 2] - boxes_coords[:, None, 0] ) point_coords_wrt_image[:, :, 1] = point_coords_wrt_image[:, :, 1] * ( boxes_coords[:, None, 3] - boxes_coords[:, None, 1] ) point_coords_wrt_image[:, :, 0] += boxes_coords[:, None, 0] point_coords_wrt_image[:, :, 1] += boxes_coords[:, None, 1] return point_coords_wrt_image def sample_point_labels(instances, point_coords): """ Sample point labels from ground truth mask given point_coords. Args: instances (list[Instances]): A list of N Instances, where N is the number of images in the batch. So, i_th elememt of the list contains R_i objects and R_1 + ... + R_N is equal to R. The ground-truth gt_masks in each instance will be used to compute labels. points_coords (Tensor): A tensor of shape (R, P, 2), where R is the total number of instances and P is the number of points for each instance. The coordinates are in the absolute image pixel coordinate space, i.e. [0, H] x [0, W]. Returns: Tensor: A tensor of shape (R, P) that contains the labels of P sampled points. """ with torch.no_grad(): gt_mask_logits = [] point_coords_splits = torch.split( point_coords, [len(instances_per_image) for instances_per_image in instances] ) for i, instances_per_image in enumerate(instances): if len(instances_per_image) == 0: continue assert isinstance( instances_per_image.gt_masks, BitMasks ), "Point head works with GT in 'bitmask' format. Set INPUT.MASK_FORMAT to 'bitmask'." gt_bit_masks = instances_per_image.gt_masks.tensor h, w = instances_per_image.gt_masks.image_size scale = torch.tensor([w, h], dtype=torch.float, device=gt_bit_masks.device) points_coord_grid_sample_format = point_coords_splits[i] / scale gt_mask_logits.append( point_sample( gt_bit_masks.to(torch.float32).unsqueeze(1), points_coord_grid_sample_format, align_corners=False, ).squeeze(1) ) point_labels = cat(gt_mask_logits) return point_labels ================================================ FILE: detectron2/projects/PointRend/point_rend/point_head.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import fvcore.nn.weight_init as weight_init import torch from torch import nn from torch.nn import functional as F from detectron2.layers import ShapeSpec, cat from detectron2.utils.events import get_event_storage from detectron2.utils.registry import Registry POINT_HEAD_REGISTRY = Registry("POINT_HEAD") POINT_HEAD_REGISTRY.__doc__ = """ Registry for point heads, which makes prediction for a given set of per-point features. The registered object will be called with `obj(cfg, input_shape)`. """ def roi_mask_point_loss(mask_logits, instances, point_labels): """ Compute the point-based loss for instance segmentation mask predictions given point-wise mask prediction and its corresponding point-wise labels. Args: mask_logits (Tensor): A tensor of shape (R, C, P) or (R, 1, P) for class-specific or class-agnostic, where R is the total number of predicted masks in all images, C is the number of foreground classes, and P is the number of points sampled for each mask. The values are logits. instances (list[Instances]): A list of N Instances, where N is the number of images in the batch. These instances are in 1:1 correspondence with the `mask_logits`. So, i_th elememt of the list contains R_i objects and R_1 + ... + R_N is equal to R. The ground-truth labels (class, box, mask, ...) associated with each instance are stored in fields. point_labels (Tensor): A tensor of shape (R, P), where R is the total number of predicted masks and P is the number of points for each mask. Labels with value of -1 will be ignored. Returns: point_loss (Tensor): A scalar tensor containing the loss. """ with torch.no_grad(): cls_agnostic_mask = mask_logits.size(1) == 1 total_num_masks = mask_logits.size(0) gt_classes = [] for instances_per_image in instances: if len(instances_per_image) == 0: continue if not cls_agnostic_mask: gt_classes_per_image = instances_per_image.gt_classes.to(dtype=torch.int64) gt_classes.append(gt_classes_per_image) gt_mask_logits = point_labels point_ignores = point_labels == -1 if gt_mask_logits.shape[0] == 0: return mask_logits.sum() * 0 assert gt_mask_logits.numel() > 0, gt_mask_logits.shape if cls_agnostic_mask: mask_logits = mask_logits[:, 0] else: indices = torch.arange(total_num_masks) gt_classes = cat(gt_classes, dim=0) mask_logits = mask_logits[indices, gt_classes] # Log the training accuracy (using gt classes and 0.0 threshold for the logits) mask_accurate = (mask_logits > 0.0) == gt_mask_logits.to(dtype=torch.uint8) mask_accurate = mask_accurate[~point_ignores] mask_accuracy = mask_accurate.nonzero().size(0) / max(mask_accurate.numel(), 1.0) get_event_storage().put_scalar("point/accuracy", mask_accuracy) point_loss = F.binary_cross_entropy_with_logits( mask_logits, gt_mask_logits.to(dtype=torch.float32), weight=~point_ignores, reduction="mean" ) return point_loss @POINT_HEAD_REGISTRY.register() class StandardPointHead(nn.Module): """ A point head multi-layer perceptron which we model with conv1d layers with kernel 1. The head takes both fine-grained and coarse prediction features as its input. """ def __init__(self, cfg, input_shape: ShapeSpec): """ The following attributes are parsed from config: fc_dim: the output dimension of each FC layers num_fc: the number of FC layers coarse_pred_each_layer: if True, coarse prediction features are concatenated to each layer's input """ super(StandardPointHead, self).__init__() # fmt: off num_classes = cfg.MODEL.POINT_HEAD.NUM_CLASSES fc_dim = cfg.MODEL.POINT_HEAD.FC_DIM num_fc = cfg.MODEL.POINT_HEAD.NUM_FC cls_agnostic_mask = cfg.MODEL.POINT_HEAD.CLS_AGNOSTIC_MASK self.coarse_pred_each_layer = cfg.MODEL.POINT_HEAD.COARSE_PRED_EACH_LAYER input_channels = input_shape.channels # fmt: on fc_dim_in = input_channels + num_classes self.fc_layers = [] for k in range(num_fc): fc = nn.Conv1d(fc_dim_in, fc_dim, kernel_size=1, stride=1, padding=0, bias=True) self.add_module("fc{}".format(k + 1), fc) self.fc_layers.append(fc) fc_dim_in = fc_dim fc_dim_in += num_classes if self.coarse_pred_each_layer else 0 num_mask_classes = 1 if cls_agnostic_mask else num_classes self.predictor = nn.Conv1d(fc_dim_in, num_mask_classes, kernel_size=1, stride=1, padding=0) for layer in self.fc_layers: weight_init.c2_msra_fill(layer) # use normal distribution initialization for mask prediction layer nn.init.normal_(self.predictor.weight, std=0.001) if self.predictor.bias is not None: nn.init.constant_(self.predictor.bias, 0) def forward(self, fine_grained_features, coarse_features): x = torch.cat((fine_grained_features, coarse_features), dim=1) for layer in self.fc_layers: x = F.relu(layer(x)) if self.coarse_pred_each_layer: x = cat((x, coarse_features), dim=1) return self.predictor(x) @POINT_HEAD_REGISTRY.register() class ImplicitPointHead(nn.Module): """ A point head multi-layer perceptron which we model with conv1d layers with kernel 1. The head takes both fine-grained features and instance-wise MLP parameters as its input. """ def __init__(self, cfg, input_shape: ShapeSpec): """ The following attributes are parsed from config: channels: the output dimension of each FC layers num_layers: the number of FC layers (including the final prediction layer) image_feature_enabled: if True, fine-grained image-level features are used positional_encoding_enabled: if True, positional encoding is used """ super(ImplicitPointHead, self).__init__() # fmt: off self.num_layers = cfg.MODEL.POINT_HEAD.NUM_FC + 1 self.channels = cfg.MODEL.POINT_HEAD.FC_DIM self.image_feature_enabled = cfg.MODEL.IMPLICIT_POINTREND.IMAGE_FEATURE_ENABLED self.positional_encoding_enabled = cfg.MODEL.IMPLICIT_POINTREND.POS_ENC_ENABLED self.num_classes = ( cfg.MODEL.POINT_HEAD.NUM_CLASSES if not cfg.MODEL.POINT_HEAD.CLS_AGNOSTIC_MASK else 1 ) self.in_channels = input_shape.channels # fmt: on if not self.image_feature_enabled: self.in_channels = 0 if self.positional_encoding_enabled: self.in_channels += 256 self.register_buffer("positional_encoding_gaussian_matrix", torch.randn((2, 128))) assert self.in_channels > 0 num_weight_params, num_bias_params = [], [] assert self.num_layers >= 2 for l in range(self.num_layers): if l == 0: # input layer num_weight_params.append(self.in_channels * self.channels) num_bias_params.append(self.channels) elif l == self.num_layers - 1: # output layer num_weight_params.append(self.channels * self.num_classes) num_bias_params.append(self.num_classes) else: # intermediate layer num_weight_params.append(self.channels * self.channels) num_bias_params.append(self.channels) self.num_weight_params = num_weight_params self.num_bias_params = num_bias_params self.num_params = sum(num_weight_params) + sum(num_bias_params) def forward(self, fine_grained_features, point_coords, parameters): # features: [R, channels, K] # point_coords: [R, K, 2] num_instances = fine_grained_features.size(0) num_points = fine_grained_features.size(2) if num_instances == 0: return torch.zeros((0, 1, num_points), device=fine_grained_features.device) if self.positional_encoding_enabled: # locations: [R*K, 2] locations = 2 * point_coords.reshape(num_instances * num_points, 2) - 1 locations = locations @ self.positional_encoding_gaussian_matrix.to(locations.device) locations = 2 * np.pi * locations locations = torch.cat([torch.sin(locations), torch.cos(locations)], dim=1) # locations: [R, C, K] locations = locations.reshape(num_instances, num_points, 256).permute(0, 2, 1) if not self.image_feature_enabled: fine_grained_features = locations else: fine_grained_features = torch.cat([locations, fine_grained_features], dim=1) # features [R, C, K] mask_feat = fine_grained_features.reshape(num_instances, self.in_channels, num_points) weights, biases = self._parse_params( parameters, self.in_channels, self.channels, self.num_classes, self.num_weight_params, self.num_bias_params, ) point_logits = self._dynamic_mlp(mask_feat, weights, biases, num_instances) point_logits = point_logits.reshape(-1, self.num_classes, num_points) return point_logits @staticmethod def _dynamic_mlp(features, weights, biases, num_instances): assert features.dim() == 3, features.dim() n_layers = len(weights) x = features for i, (w, b) in enumerate(zip(weights, biases)): x = torch.einsum("nck,ndc->ndk", x, w) + b if i < n_layers - 1: x = F.relu(x) return x @staticmethod def _parse_params( pred_params, in_channels, channels, num_classes, num_weight_params, num_bias_params, ): assert pred_params.dim() == 2 assert len(num_weight_params) == len(num_bias_params) assert pred_params.size(1) == sum(num_weight_params) + sum(num_bias_params) num_instances = pred_params.size(0) num_layers = len(num_weight_params) params_splits = list( torch.split_with_sizes(pred_params, num_weight_params + num_bias_params, dim=1) ) weight_splits = params_splits[:num_layers] bias_splits = params_splits[num_layers:] for l in range(num_layers): if l == 0: # input layer weight_splits[l] = weight_splits[l].reshape(num_instances, channels, in_channels) bias_splits[l] = bias_splits[l].reshape(num_instances, channels, 1) elif l < num_layers - 1: # intermediate layer weight_splits[l] = weight_splits[l].reshape(num_instances, channels, channels) bias_splits[l] = bias_splits[l].reshape(num_instances, channels, 1) else: # output layer weight_splits[l] = weight_splits[l].reshape(num_instances, num_classes, channels) bias_splits[l] = bias_splits[l].reshape(num_instances, num_classes, 1) return weight_splits, bias_splits def build_point_head(cfg, input_channels): """ Build a point head defined by `cfg.MODEL.POINT_HEAD.NAME`. """ head_name = cfg.MODEL.POINT_HEAD.NAME return POINT_HEAD_REGISTRY.get(head_name)(cfg, input_channels) ================================================ FILE: detectron2/projects/PointRend/point_rend/roi_heads.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging from detectron2.modeling import ROI_HEADS_REGISTRY, StandardROIHeads @ROI_HEADS_REGISTRY.register() class PointRendROIHeads(StandardROIHeads): """ Identical to StandardROIHeads, except for some weights conversion code to handle old models. """ _version = 2 def _load_from_state_dict( self, state_dict, prefix, local_metadata, strict, missing_keys, unexpected_keys, error_msgs ): version = local_metadata.get("version", None) if version is None or version < 2: logger = logging.getLogger(__name__) logger.warning( "Weight format of PointRend models have changed! " "Please upgrade your models. Applying automatic conversion now ..." ) for k in list(state_dict.keys()): newk = k if k.startswith(prefix + "mask_point_head"): newk = k.replace(prefix + "mask_point_head", prefix + "mask_head.point_head") if k.startswith(prefix + "mask_coarse_head"): newk = k.replace(prefix + "mask_coarse_head", prefix + "mask_head.coarse_head") if newk != k: state_dict[newk] = state_dict[k] del state_dict[k] @classmethod def _init_mask_head(cls, cfg, input_shape): if cfg.MODEL.MASK_ON and cfg.MODEL.ROI_MASK_HEAD.NAME != "PointRendMaskHead": logger = logging.getLogger(__name__) logger.warning( "Config of PointRend models have changed! " "Please upgrade your models. Applying automatic conversion now ..." ) assert cfg.MODEL.ROI_MASK_HEAD.NAME == "CoarseMaskHead" cfg.defrost() cfg.MODEL.ROI_MASK_HEAD.NAME = "PointRendMaskHead" cfg.MODEL.ROI_MASK_HEAD.POOLER_TYPE = "" cfg.freeze() return super()._init_mask_head(cfg, input_shape) ================================================ FILE: detectron2/projects/PointRend/point_rend/semantic_seg.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np from typing import Dict import torch from torch import nn from torch.nn import functional as F from detectron2.layers import ShapeSpec, cat from detectron2.modeling import SEM_SEG_HEADS_REGISTRY from .point_features import ( get_uncertain_point_coords_on_grid, get_uncertain_point_coords_with_randomness, point_sample, ) from .point_head import build_point_head def calculate_uncertainty(sem_seg_logits): """ For each location of the prediction `sem_seg_logits` we estimate uncerainty as the difference between top first and top second predicted logits. Args: mask_logits (Tensor): A tensor of shape (N, C, ...), where N is the minibatch size and C is the number of foreground classes. The values are logits. Returns: scores (Tensor): A tensor of shape (N, 1, ...) that contains uncertainty scores with the most uncertain locations having the highest uncertainty score. """ top2_scores = torch.topk(sem_seg_logits, k=2, dim=1)[0] return (top2_scores[:, 1] - top2_scores[:, 0]).unsqueeze(1) @SEM_SEG_HEADS_REGISTRY.register() class PointRendSemSegHead(nn.Module): """ A semantic segmentation head that combines a head set in `POINT_HEAD.COARSE_SEM_SEG_HEAD_NAME` and a point head set in `MODEL.POINT_HEAD.NAME`. """ def __init__(self, cfg, input_shape: Dict[str, ShapeSpec]): super().__init__() self.ignore_value = cfg.MODEL.SEM_SEG_HEAD.IGNORE_VALUE self.coarse_sem_seg_head = SEM_SEG_HEADS_REGISTRY.get( cfg.MODEL.POINT_HEAD.COARSE_SEM_SEG_HEAD_NAME )(cfg, input_shape) self._init_point_head(cfg, input_shape) def _init_point_head(self, cfg, input_shape: Dict[str, ShapeSpec]): # fmt: off assert cfg.MODEL.SEM_SEG_HEAD.NUM_CLASSES == cfg.MODEL.POINT_HEAD.NUM_CLASSES feature_channels = {k: v.channels for k, v in input_shape.items()} self.in_features = cfg.MODEL.POINT_HEAD.IN_FEATURES self.train_num_points = cfg.MODEL.POINT_HEAD.TRAIN_NUM_POINTS self.oversample_ratio = cfg.MODEL.POINT_HEAD.OVERSAMPLE_RATIO self.importance_sample_ratio = cfg.MODEL.POINT_HEAD.IMPORTANCE_SAMPLE_RATIO self.subdivision_steps = cfg.MODEL.POINT_HEAD.SUBDIVISION_STEPS self.subdivision_num_points = cfg.MODEL.POINT_HEAD.SUBDIVISION_NUM_POINTS # fmt: on in_channels = int(np.sum([feature_channels[f] for f in self.in_features])) self.point_head = build_point_head(cfg, ShapeSpec(channels=in_channels, width=1, height=1)) def forward(self, features, targets=None): coarse_sem_seg_logits = self.coarse_sem_seg_head.layers(features) if self.training: losses = self.coarse_sem_seg_head.losses(coarse_sem_seg_logits, targets) with torch.no_grad(): point_coords = get_uncertain_point_coords_with_randomness( coarse_sem_seg_logits, calculate_uncertainty, self.train_num_points, self.oversample_ratio, self.importance_sample_ratio, ) coarse_features = point_sample(coarse_sem_seg_logits, point_coords, align_corners=False) fine_grained_features = cat( [ point_sample(features[in_feature], point_coords, align_corners=False) for in_feature in self.in_features ], dim=1, ) point_logits = self.point_head(fine_grained_features, coarse_features) point_targets = ( point_sample( targets.unsqueeze(1).to(torch.float), point_coords, mode="nearest", align_corners=False, ) .squeeze(1) .to(torch.long) ) losses["loss_sem_seg_point"] = F.cross_entropy( point_logits, point_targets, reduction="mean", ignore_index=self.ignore_value ) return None, losses else: sem_seg_logits = coarse_sem_seg_logits.clone() for _ in range(self.subdivision_steps): sem_seg_logits = F.interpolate( sem_seg_logits, scale_factor=2, mode="bilinear", align_corners=False ) uncertainty_map = calculate_uncertainty(sem_seg_logits) point_indices, point_coords = get_uncertain_point_coords_on_grid( uncertainty_map, self.subdivision_num_points ) fine_grained_features = cat( [ point_sample(features[in_feature], point_coords, align_corners=False) for in_feature in self.in_features ] ) coarse_features = point_sample( coarse_sem_seg_logits, point_coords, align_corners=False ) point_logits = self.point_head(fine_grained_features, coarse_features) # put sem seg point predictions to the right places on the upsampled grid. N, C, H, W = sem_seg_logits.shape point_indices = point_indices.unsqueeze(1).expand(-1, C, -1) sem_seg_logits = ( sem_seg_logits.reshape(N, C, H * W) .scatter_(2, point_indices, point_logits) .view(N, C, H, W) ) return sem_seg_logits, {} ================================================ FILE: detectron2/projects/PointRend/train_net.py ================================================ #!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. """ PointRend Training Script. This script is a simplified version of the training script in detectron2/tools. """ import os import detectron2.data.transforms as T import detectron2.utils.comm as comm from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import get_cfg from detectron2.data import DatasetMapper, MetadataCatalog, build_detection_train_loader from detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, launch from detectron2.evaluation import ( CityscapesInstanceEvaluator, CityscapesSemSegEvaluator, COCOEvaluator, DatasetEvaluators, LVISEvaluator, SemSegEvaluator, verify_results, ) from detectron2.projects.point_rend import ColorAugSSDTransform, add_pointrend_config def build_sem_seg_train_aug(cfg): augs = [ T.ResizeShortestEdge( cfg.INPUT.MIN_SIZE_TRAIN, cfg.INPUT.MAX_SIZE_TRAIN, cfg.INPUT.MIN_SIZE_TRAIN_SAMPLING ) ] if cfg.INPUT.CROP.ENABLED: augs.append( T.RandomCrop_CategoryAreaConstraint( cfg.INPUT.CROP.TYPE, cfg.INPUT.CROP.SIZE, cfg.INPUT.CROP.SINGLE_CATEGORY_MAX_AREA, cfg.MODEL.SEM_SEG_HEAD.IGNORE_VALUE, ) ) if cfg.INPUT.COLOR_AUG_SSD: augs.append(ColorAugSSDTransform(img_format=cfg.INPUT.FORMAT)) augs.append(T.RandomFlip()) return augs class Trainer(DefaultTrainer): """ We use the "DefaultTrainer" which contains a number pre-defined logic for standard training workflow. They may not work for you, especially if you are working on a new research project. In that case you can use the cleaner "SimpleTrainer", or write your own training loop. """ @classmethod def build_evaluator(cls, cfg, dataset_name, output_folder=None): """ Create evaluator(s) for a given dataset. This uses the special metadata "evaluator_type" associated with each builtin dataset. For your own dataset, you can simply create an evaluator manually in your script and do not have to worry about the hacky if-else logic here. """ if output_folder is None: output_folder = os.path.join(cfg.OUTPUT_DIR, "inference") evaluator_list = [] evaluator_type = MetadataCatalog.get(dataset_name).evaluator_type if evaluator_type == "lvis": return LVISEvaluator(dataset_name, output_dir=output_folder) if evaluator_type == "coco": return COCOEvaluator(dataset_name, output_dir=output_folder) if evaluator_type == "sem_seg": return SemSegEvaluator( dataset_name, distributed=True, output_dir=output_folder, ) if evaluator_type == "cityscapes_instance": return CityscapesInstanceEvaluator(dataset_name) if evaluator_type == "cityscapes_sem_seg": return CityscapesSemSegEvaluator(dataset_name) if len(evaluator_list) == 0: raise NotImplementedError( "no Evaluator for the dataset {} with the type {}".format( dataset_name, evaluator_type ) ) if len(evaluator_list) == 1: return evaluator_list[0] return DatasetEvaluators(evaluator_list) @classmethod def build_train_loader(cls, cfg): if "SemanticSegmentor" in cfg.MODEL.META_ARCHITECTURE: mapper = DatasetMapper(cfg, is_train=True, augmentations=build_sem_seg_train_aug(cfg)) else: mapper = None return build_detection_train_loader(cfg, mapper=mapper) def setup(args): """ Create configs and perform basic setups. """ cfg = get_cfg() add_pointrend_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.freeze() default_setup(cfg, args) return cfg def main(args): cfg = setup(args) if args.eval_only: model = Trainer.build_model(cfg) DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load( cfg.MODEL.WEIGHTS, resume=args.resume ) res = Trainer.test(cfg, model) if comm.is_main_process(): verify_results(cfg, res) return res trainer = Trainer(cfg) trainer.resume_or_load(resume=args.resume) return trainer.train() if __name__ == "__main__": args = default_argument_parser().parse_args() print("Command Line Args:", args) launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), ) ================================================ FILE: detectron2/projects/PointSup/README.md ================================================ # Pointly-Supervised Instance Segmentation Bowen Cheng, Omkar Parkhi, Alexander Kirillov [[`arXiv`](https://arxiv.org/abs/2104.06404)] [[`Project`](https://bowenc0221.github.io/point-sup)] [[`BibTeX`](#CitingPointSup)]

## Data preparation Please follow these steps to prepare your datasets: 1. Follow official Detectron2 instruction to prepare COCO dataset. Set up `DETECTRON2_DATASETS` environment variable to the location of your Detectron2 dataset. 2. Generate 10-points annotations for COCO by running: `python tools/prepare_coco_point_annotations_without_masks.py 10` ## Training To train a model with 8 GPUs run: ```bash python train_net.py --config-file configs/mask_rcnn_R_50_FPN_3x_point_sup_point_aug_coco.yaml --num-gpus 8 ``` ## Evaluation Model evaluation can be done similarly: ```bash python train_net.py --config-file configs/mask_rcnn_R_50_FPN_3x_point_sup_point_aug_coco.yaml --eval-only MODEL.WEIGHTS /path/to/model_checkpoint ``` ## Citing Pointly-Supervised Instance Segmentation If you use PointSup, please use the following BibTeX entry. ```BibTeX @article{cheng2021pointly, title={Pointly-Supervised Instance Segmentation}, author={Bowen Cheng and Omkar Parkhi and Alexander Kirillov}, journal={arXiv}, year={2021} } ``` ================================================ FILE: detectron2/projects/PointSup/configs/implicit_pointrend_R_50_FPN_3x_point_sup_point_aug_coco.yaml ================================================ _BASE_: "../../PointRend/configs/InstanceSegmentation/implicit_pointrend_R_50_FPN_3x_coco.yaml" MODEL: ROI_MASK_HEAD: NAME: "ImplicitPointRendPointSupHead" INPUT: POINT_SUP: True SAMPLE_POINTS: 5 DATASETS: TRAIN: ("coco_2017_train_points_n10_v1_without_masks",) ================================================ FILE: detectron2/projects/PointSup/configs/mask_rcnn_R_50_FPN_3x_point_sup_coco.yaml ================================================ _BASE_: "../../../configs/Base-RCNN-FPN.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: True RESNETS: DEPTH: 50 ROI_MASK_HEAD: NAME: "MaskRCNNConvUpsamplePointSupHead" INPUT: POINT_SUP: True DATASETS: TRAIN: ("coco_2017_train_points_n10_v1_without_masks",) SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/projects/PointSup/configs/mask_rcnn_R_50_FPN_3x_point_sup_point_aug_coco.yaml ================================================ _BASE_: "mask_rcnn_R_50_FPN_3x_point_sup_coco.yaml" INPUT: SAMPLE_POINTS: 5 ================================================ FILE: detectron2/projects/PointSup/point_sup/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from . import register_point_annotations from .config import add_point_sup_config from .dataset_mapper import PointSupDatasetMapper from .mask_head import MaskRCNNConvUpsamplePointSupHead from .point_utils import get_point_coords_from_point_annotation ================================================ FILE: detectron2/projects/PointSup/point_sup/config.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved def add_point_sup_config(cfg): """ Add config for point supervision. """ # Use point annotation cfg.INPUT.POINT_SUP = False # Sample only part of points in each iteration. # Default: 0, use all available points. cfg.INPUT.SAMPLE_POINTS = 0 ================================================ FILE: detectron2/projects/PointSup/point_sup/dataset_mapper.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import copy import logging import numpy as np from typing import List, Union import torch import detectron2.data.detection_utils as utils import detectron2.data.transforms as T from detectron2.config import configurable from .detection_utils import annotations_to_instances, transform_instance_annotations __all__ = [ "PointSupDatasetMapper", ] class PointSupDatasetMapper: """ The callable currently does the following: 1. Read the image from "file_name" 2. Applies transforms to the image and annotations 3. Prepare data and annotations to Tensor and :class:`Instances` """ @configurable def __init__( self, is_train: bool, *, augmentations: List[Union[T.Augmentation, T.Transform]], image_format: str, # Extra data augmentation for point supervision sample_points: int = 0, ): """ NOTE: this interface is experimental. Args: is_train: whether it's used in training or inference augmentations: a list of augmentations or deterministic transforms to apply image_format: an image format supported by :func:`detection_utils.read_image`. sample_points: subsample points at each iteration """ # fmt: off self.is_train = is_train self.augmentations = T.AugmentationList(augmentations) self.image_format = image_format self.sample_points = sample_points # fmt: on logger = logging.getLogger(__name__) mode = "training" if is_train else "inference" logger.info(f"[DatasetMapper] Augmentations used in {mode}: {augmentations}") logger.info(f"Point Augmentations used in {mode}: sample {sample_points} points") @classmethod def from_config(cls, cfg, is_train: bool = True): augs = utils.build_augmentation(cfg, is_train) if cfg.INPUT.CROP.ENABLED and is_train: raise ValueError("Crop augmentation not supported to point supervision.") ret = { "is_train": is_train, "augmentations": augs, "image_format": cfg.INPUT.FORMAT, "sample_points": cfg.INPUT.SAMPLE_POINTS, } return ret def __call__(self, dataset_dict): """ Args: dataset_dict (dict): Metadata of one image, in Detectron2 Dataset format. Returns: dict: a format that builtin models in detectron2 accept """ dataset_dict = copy.deepcopy(dataset_dict) # it will be modified by code below image = utils.read_image(dataset_dict["file_name"], format=self.image_format) utils.check_image_size(dataset_dict, image) aug_input = T.AugInput(image) transforms = self.augmentations(aug_input) image = aug_input.image image_shape = image.shape[:2] # h, w # Pytorch's dataloader is efficient on torch.Tensor due to shared-memory, # but not efficient on large generic data structures due to the use of pickle & mp.Queue. # Therefore it's important to use torch.Tensor. dataset_dict["image"] = torch.as_tensor(np.ascontiguousarray(image.transpose(2, 0, 1))) if not self.is_train: dataset_dict.pop("annotations", None) return dataset_dict if "annotations" in dataset_dict: # Maps points from the closed interval [0, image_size - 1] on discrete # image coordinates to the half-open interval [x1, x2) on continuous image # coordinates. We use the continuous-discrete conversion from Heckbert # 1990 ("What is the coordinate of a pixel?"): d = floor(c) and c = d + 0.5, # where d is a discrete coordinate and c is a continuous coordinate. for ann in dataset_dict["annotations"]: point_coords_wrt_image = np.array(ann["point_coords"]).astype(np.float) point_coords_wrt_image = point_coords_wrt_image + 0.5 ann["point_coords"] = point_coords_wrt_image annos = [ # also need to transform point coordinates transform_instance_annotations( obj, transforms, image_shape, ) for obj in dataset_dict.pop("annotations") if obj.get("iscrowd", 0) == 0 ] instances = annotations_to_instances( annos, image_shape, sample_points=self.sample_points, ) dataset_dict["instances"] = utils.filter_empty_instances(instances) return dataset_dict ================================================ FILE: detectron2/projects/PointSup/point_sup/detection_utils.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import numpy as np import torch # fmt: off from detectron2.data.detection_utils import \ annotations_to_instances as base_annotations_to_instances from detectron2.data.detection_utils import \ transform_instance_annotations as base_transform_instance_annotations # fmt: on def annotations_to_instances(annos, image_size, sample_points=0): """ Create an :class:`Instances` object used by the models, from instance annotations in the dataset dict. Args: annos (list[dict]): a list of instance annotations in one image, each element for one instance. image_size (tuple): height, width sample_points (int): subsample points at each iteration Returns: Instances: It will contain fields "gt_boxes", "gt_classes", "gt_point_coords", "gt_point_labels", if they can be obtained from `annos`. This is the format that builtin models with point supervision expect. """ target = base_annotations_to_instances(annos, image_size) assert ("point_coords" in annos[0]) == ("point_labels" in annos[0]) if len(annos) and "point_labels" in annos[0]: point_coords = [] point_labels = [] for i, _ in enumerate(annos): # Already in the image coordinate system point_coords_wrt_image = np.array(annos[i]["point_coords"]) point_labels_wrt_image = np.array(annos[i]["point_labels"]) if sample_points > 0: random_indices = np.random.choice( point_coords_wrt_image.shape[0], sample_points, replace=point_coords_wrt_image.shape[0] < sample_points, ).astype(int) point_coords_wrt_image = point_coords_wrt_image[random_indices] point_labels_wrt_image = point_labels_wrt_image[random_indices] assert point_coords_wrt_image.shape[0] == point_labels_wrt_image.size point_coords.append(point_coords_wrt_image) point_labels.append(point_labels_wrt_image) point_coords = torch.stack([torch.from_numpy(x) for x in point_coords]) point_labels = torch.stack([torch.from_numpy(x) for x in point_labels]) target.gt_point_coords = point_coords target.gt_point_labels = point_labels return target def transform_instance_annotations( annotation, transforms, image_size, *, keypoint_hflip_indices=None ): """ Apply transforms to box, and point annotations of a single instance. It will use `transforms.apply_box` for the box, and `transforms.apply_coords` for points. Args: annotation (dict): dict of instance annotations for a single instance. It will be modified in-place. transforms (TransformList or list[Transform]): image_size (tuple): the height, width of the transformed image keypoint_hflip_indices (ndarray[int]): see `create_keypoint_hflip_indices`. Returns: dict: the same input dict with fields "bbox", "point_coords", "point_labels" transformed according to `transforms`. The "bbox_mode" field will be set to XYXY_ABS. """ annotation = base_transform_instance_annotations( annotation, transforms, image_size, keypoint_hflip_indices ) assert ("point_coords" in annotation) == ("point_labels" in annotation) if "point_coords" in annotation and "point_labels" in annotation: point_coords = annotation["point_coords"] point_labels = np.array(annotation["point_labels"]).astype(np.float) point_coords = transforms.apply_coords(point_coords) # Set all out-of-boundary points to "unlabeled" inside = (point_coords >= np.array([0, 0])) & (point_coords <= np.array(image_size[::-1])) inside = inside.all(axis=1) point_labels[~inside] = -1 annotation["point_coords"] = point_coords annotation["point_labels"] = point_labels return annotation ================================================ FILE: detectron2/projects/PointSup/point_sup/mask_head.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import numpy as np from typing import Any, List from detectron2.modeling import ROI_MASK_HEAD_REGISTRY from detectron2.modeling.roi_heads.mask_head import MaskRCNNConvUpsampleHead, mask_rcnn_inference from detectron2.projects.point_rend import ImplicitPointRendMaskHead from detectron2.projects.point_rend.point_features import point_sample from detectron2.projects.point_rend.point_head import roi_mask_point_loss from detectron2.structures import Instances from .point_utils import get_point_coords_from_point_annotation __all__ = [ "ImplicitPointRendPointSupHead", "MaskRCNNConvUpsamplePointSupHead", ] @ROI_MASK_HEAD_REGISTRY.register() class MaskRCNNConvUpsamplePointSupHead(MaskRCNNConvUpsampleHead): """ A mask head with several conv layers, plus an upsample layer (with `ConvTranspose2d`). Predictions are made with a final 1x1 conv layer. The difference with `MaskRCNNConvUpsampleHead` is that this head is trained with point supervision. Please use the `MaskRCNNConvUpsampleHead` if you want to train the model with mask supervision. """ def forward(self, x, instances: List[Instances]) -> Any: """ Args: x: input region feature(s) provided by :class:`ROIHeads`. instances (list[Instances]): contains the boxes & labels corresponding to the input features. Exact format is up to its caller to decide. Typically, this is the foreground instances in training, with "proposal_boxes" field and other gt annotations. In inference, it contains boxes that are already predicted. Returns: A dict of losses in training. The predicted "instances" in inference. """ x = self.layers(x) if self.training: N, C, H, W = x.shape assert H == W proposal_boxes = [x.proposal_boxes for x in instances] assert N == np.sum(len(x) for x in proposal_boxes) if N == 0: return {"loss_mask": x.sum() * 0} # Training with point supervision point_coords, point_labels = get_point_coords_from_point_annotation(instances) mask_logits = point_sample( x, point_coords, align_corners=False, ) return {"loss_mask": roi_mask_point_loss(mask_logits, instances, point_labels)} else: mask_rcnn_inference(x, instances) return instances @ROI_MASK_HEAD_REGISTRY.register() class ImplicitPointRendPointSupHead(ImplicitPointRendMaskHead): def _uniform_sample_train_points(self, instances): assert self.training # Please keep in mind that "gt_masks" is not used in this mask head. point_coords, point_labels = get_point_coords_from_point_annotation(instances) return point_coords, point_labels ================================================ FILE: detectron2/projects/PointSup/point_sup/point_utils.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import torch from detectron2.layers import cat def get_point_coords_from_point_annotation(instances): """ Load point coords and their corresponding labels from point annotation. Args: instances (list[Instances]): A list of N Instances, where N is the number of images in the batch. These instances are in 1:1 correspondence with the pred_mask_logits. The ground-truth labels (class, box, mask, ...) associated with each instance are stored in fields. Returns: point_coords (Tensor): A tensor of shape (N, P, 2) that contains the coordinates of P sampled points. point_labels (Tensor): A tensor of shape (N, P) that contains the labels of P sampled points. `point_labels` takes 3 possible values: - 0: the point belongs to background - 1: the point belongs to the object - -1: the point is ignored during training """ point_coords_list = [] point_labels_list = [] for instances_per_image in instances: if len(instances_per_image) == 0: continue point_coords = instances_per_image.gt_point_coords.to(torch.float32) point_labels = instances_per_image.gt_point_labels.to(torch.float32).clone() proposal_boxes_per_image = instances_per_image.proposal_boxes.tensor # Convert point coordinate system, ground truth points are in image coord. point_coords_wrt_box = get_point_coords_wrt_box(proposal_boxes_per_image, point_coords) # Ignore points that are outside predicted boxes. point_ignores = ( (point_coords_wrt_box[:, :, 0] < 0) | (point_coords_wrt_box[:, :, 0] > 1) | (point_coords_wrt_box[:, :, 1] < 0) | (point_coords_wrt_box[:, :, 1] > 1) ) point_labels[point_ignores] = -1 point_coords_list.append(point_coords_wrt_box) point_labels_list.append(point_labels) return ( cat(point_coords_list, dim=0), cat(point_labels_list, dim=0), ) def get_point_coords_wrt_box(boxes_coords, point_coords): """ Convert image-level absolute coordinates to box-normalized [0, 1] x [0, 1] point cooordinates. Args: boxes_coords (Tensor): A tensor of shape (R, 4) that contains bounding boxes. coordinates. point_coords (Tensor): A tensor of shape (R, P, 2) that contains image-normalized coordinates of P sampled points. Returns: point_coords_wrt_box (Tensor): A tensor of shape (R, P, 2) that contains [0, 1] x [0, 1] box-normalized coordinates of the P sampled points. """ with torch.no_grad(): point_coords_wrt_box = point_coords.clone() point_coords_wrt_box[:, :, 0] -= boxes_coords[:, None, 0] point_coords_wrt_box[:, :, 1] -= boxes_coords[:, None, 1] point_coords_wrt_box[:, :, 0] = point_coords_wrt_box[:, :, 0] / ( boxes_coords[:, None, 2] - boxes_coords[:, None, 0] ) point_coords_wrt_box[:, :, 1] = point_coords_wrt_box[:, :, 1] / ( boxes_coords[:, None, 3] - boxes_coords[:, None, 1] ) return point_coords_wrt_box ================================================ FILE: detectron2/projects/PointSup/point_sup/register_point_annotations.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import logging import os from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.data.datasets.builtin import _get_builtin_metadata from detectron2.data.datasets.coco import load_coco_json logger = logging.getLogger(__name__) # COCO dataset def register_coco_instances_with_points(name, metadata, json_file, image_root): """ Register a dataset in COCO's json annotation format for instance segmentation with point annotation. The point annotation json does not have "segmentation" field, instead, it has "point_coords" and "point_labels" fields. Args: name (str): the name that identifies a dataset, e.g. "coco_2014_train". metadata (dict): extra metadata associated with this dataset. You can leave it as an empty dict. json_file (str): path to the json instance annotation file. image_root (str or path-like): directory which contains all the images. """ assert isinstance(name, str), name assert isinstance(json_file, (str, os.PathLike)), json_file assert isinstance(image_root, (str, os.PathLike)), image_root # 1. register a function which returns dicts DatasetCatalog.register( name, lambda: load_coco_json(json_file, image_root, name, ["point_coords", "point_labels"]) ) # 2. Optionally, add metadata about this dataset, # since they might be useful in evaluation, visualization or logging MetadataCatalog.get(name).set( json_file=json_file, image_root=image_root, evaluator_type="coco", **metadata ) _PREDEFINED_SPLITS_COCO = {} _PREDEFINED_SPLITS_COCO["coco"] = { # point annotations without masks "coco_2017_train_points_n10_v1_without_masks": ( "coco/train2017", "coco/annotations/instances_train2017_n10_v1_without_masks.json", ), } def register_all_coco_train_points(root): for dataset_name, splits_per_dataset in _PREDEFINED_SPLITS_COCO.items(): for key, (image_root, json_file) in splits_per_dataset.items(): # Assume pre-defined datasets live in `./datasets`. register_coco_instances_with_points( key, _get_builtin_metadata(dataset_name), os.path.join(root, json_file) if "://" not in json_file else json_file, os.path.join(root, image_root), ) # True for open source; # Internally at fb, we register them elsewhere if __name__.endswith(".register_point_annotations"): _root = os.getenv("DETECTRON2_DATASETS", "datasets") register_all_coco_train_points(_root) ================================================ FILE: detectron2/projects/PointSup/tools/prepare_coco_point_annotations_without_masks.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import copy import json import numpy as np import os import sys import pycocotools.mask as mask_utils from detectron2.utils.env import seed_all_rng from detectron2.utils.file_io import PathManager def get_point_annotations(input_filename, output_filename, num_points_per_instance): with PathManager.open(input_filename, "r") as f: coco_json = json.load(f) coco_annos = coco_json.pop("annotations") coco_points_json = copy.deepcopy(coco_json) imgs = {} for img in coco_json["images"]: imgs[img["id"]] = img new_annos = [] for ann in coco_annos: # convert mask t = imgs[ann["image_id"]] h, w = t["height"], t["width"] segm = ann.pop("segmentation") if type(segm) == list: # polygon -- a single object might consist of multiple parts # we merge all parts into one mask rle code rles = mask_utils.frPyObjects(segm, h, w) rle = mask_utils.merge(rles) elif type(segm["counts"]) == list: # uncompressed RLE rle = mask_utils.frPyObjects(segm, h, w) else: # rle rle = segm mask = mask_utils.decode(rle) new_ann = copy.deepcopy(ann) # sample points in image coordinates box = ann["bbox"] point_coords_wrt_image = np.random.rand(num_points_per_instance, 2) point_coords_wrt_image[:, 0] = point_coords_wrt_image[:, 0] * box[2] point_coords_wrt_image[:, 1] = point_coords_wrt_image[:, 1] * box[3] point_coords_wrt_image[:, 0] += box[0] point_coords_wrt_image[:, 1] += box[1] # round to integer coordinates point_coords_wrt_image = np.floor(point_coords_wrt_image).astype(int) # get labels assert (point_coords_wrt_image >= 0).all(), (point_coords_wrt_image, mask.shape) assert (point_coords_wrt_image[:, 0] < w).all(), (point_coords_wrt_image, mask.shape) assert (point_coords_wrt_image[:, 1] < h).all(), (point_coords_wrt_image, mask.shape) point_labels = mask[point_coords_wrt_image[:, 1], point_coords_wrt_image[:, 0]] # store new annotations new_ann["point_coords"] = point_coords_wrt_image.tolist() new_ann["point_labels"] = point_labels.tolist() new_annos.append(new_ann) coco_points_json["annotations"] = new_annos with PathManager.open(output_filename, "w") as f: json.dump(coco_points_json, f) print("{} is modified and stored in {}.".format(input_filename, output_filename)) if __name__ == "__main__": """ Generate point-based supervision for COCO dataset. Usage: python tools/prepare_coco_point_annotations_without_masks.py \ NUM_POINTS_PER_INSTANCE NUM_VERSIONS_WITH_DIFFERENT_SEED Example to generate point-based COCO dataset with 10 points per instance: python tools/prepare_coco_point_annotations_without_masks.py 10 """ # Fix random seed seed_all_rng(12345) assert len(sys.argv) >= 2, "Please provide number of points to sample per instance" dataset_dir = os.path.join(os.getenv("DETECTRON2_DATASETS", "datasets"), "coco/annotations") num_points_per_instance = int(sys.argv[1]) if len(sys.argv) == 3: repeat = int(sys.argv[2]) else: repeat = 1 s = "instances_train2017" for version in range(repeat): print( "Start sampling {} points per instance for annotations {}.".format( num_points_per_instance, s ) ) get_point_annotations( os.path.join(dataset_dir, "{}.json".format(s)), os.path.join( dataset_dir, "{}_n{}_v{}_without_masks.json".format(s, num_points_per_instance, version + 1), ), num_points_per_instance, ) ================================================ FILE: detectron2/projects/PointSup/train_net.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. """ Point supervision Training Script. This script is a simplified version of the training script in detectron2/tools. """ import os import detectron2.utils.comm as comm from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import get_cfg from detectron2.data import MetadataCatalog, build_detection_train_loader from detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, launch from detectron2.evaluation import COCOEvaluator, DatasetEvaluators, verify_results from detectron2.projects.point_rend import add_pointrend_config from detectron2.utils.logger import setup_logger from point_sup import PointSupDatasetMapper, add_point_sup_config class Trainer(DefaultTrainer): """ We use the "DefaultTrainer" which contains pre-defined default logic for standard training workflow. They may not work for you, especially if you are working on a new research project. In that case you can write your own training loop. You can use "tools/plain_train_net.py" as an example. """ @classmethod def build_evaluator(cls, cfg, dataset_name, output_folder=None): """ Create evaluator(s) for a given dataset. This uses the special metadata "evaluator_type" associated with each builtin dataset. For your own dataset, you can simply create an evaluator manually in your script and do not have to worry about the hacky if-else logic here. """ if output_folder is None: output_folder = os.path.join(cfg.OUTPUT_DIR, "inference") evaluator_list = [] evaluator_type = MetadataCatalog.get(dataset_name).evaluator_type if evaluator_type == "coco": evaluator_list.append(COCOEvaluator(dataset_name, output_dir=output_folder)) if len(evaluator_list) == 0: raise NotImplementedError( "no Evaluator for the dataset {} with the type {}".format( dataset_name, evaluator_type ) ) elif len(evaluator_list) == 1: return evaluator_list[0] return DatasetEvaluators(evaluator_list) @classmethod def build_train_loader(cls, cfg): if cfg.INPUT.POINT_SUP: mapper = PointSupDatasetMapper(cfg, is_train=True) else: mapper = None return build_detection_train_loader(cfg, mapper=mapper) def setup(args): """ Create configs and perform basic setups. """ cfg = get_cfg() add_pointrend_config(cfg) add_point_sup_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.freeze() default_setup(cfg, args) # Setup logger for "point_sup" module setup_logger(output=cfg.OUTPUT_DIR, distributed_rank=comm.get_rank(), name="point_sup") return cfg def main(args): cfg = setup(args) if args.eval_only: model = Trainer.build_model(cfg) DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load( cfg.MODEL.WEIGHTS, resume=args.resume ) res = Trainer.test(cfg, model) if cfg.TEST.AUG.ENABLED: res.update(Trainer.test_with_TTA(cfg, model)) if comm.is_main_process(): verify_results(cfg, res) return res """ If you'd like to do anything fancier than the standard training logic, consider writing your own training loop (see plain_train_net.py) or subclassing the trainer. """ trainer = Trainer(cfg) trainer.resume_or_load(resume=args.resume) return trainer.train() if __name__ == "__main__": args = default_argument_parser().parse_args() print("Command Line Args:", args) launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), ) ================================================ FILE: detectron2/projects/README.md ================================================ Here are a few projects that are built on detectron2. They are examples of how to use detectron2 as a library, to make your projects more maintainable. ## Projects by Facebook Note that these are research projects, and therefore may not have the same level of support or stability as detectron2. + [DensePose: Dense Human Pose Estimation In The Wild](DensePose) + [Scale-Aware Trident Networks for Object Detection](TridentNet) + [TensorMask: A Foundation for Dense Object Segmentation](TensorMask) + [Mesh R-CNN](https://github.com/facebookresearch/meshrcnn) + [PointRend: Image Segmentation as Rendering](PointRend) + [Momentum Contrast for Unsupervised Visual Representation Learning](https://github.com/facebookresearch/moco/tree/master/detection) + [DETR: End-to-End Object Detection with Transformers](https://github.com/facebookresearch/detr/tree/master/d2) + [Panoptic-DeepLab: A Simple, Strong, and Fast Baseline for Bottom-Up Panoptic Segmentation](Panoptic-DeepLab) + [D2Go (Detectron2Go)](https://github.com/facebookresearch/d2go), an end-to-end production system for training and deployment for mobile platforms. + [Pointly-Supervised Instance Segmentation](PointSup) + [Unbiased Teacher for Semi-Supervised Object Detection](https://github.com/facebookresearch/unbiased-teacher) + [Rethinking "Batch" in BatchNorm](Rethinking-BatchNorm/) + [Per-Pixel Classification is Not All You Need for Semantic Segmentation](https://github.com/facebookresearch/MaskFormer) + [Exploring Plain Vision Transformer Backbones for Object Detection](ViTDet/) + [MViTv2: Improved Multiscale Vision Transformers for Classification and Detection](MViTv2/) ## External Projects External projects in the community that use detectron2: + [AdelaiDet](https://github.com/aim-uofa/adet), a detection toolbox including FCOS, BlendMask, etc. + [CenterMask](https://github.com/youngwanLEE/centermask2) + [Res2Net backbones](https://github.com/Res2Net/Res2Net-detectron2) + [VoVNet backbones](https://github.com/youngwanLEE/vovnet-detectron2) + [FsDet](https://github.com/ucbdrive/few-shot-object-detection), Few-Shot Object Detection. + [Sparse R-CNN](https://github.com/PeizeSun/SparseR-CNN) + [BCNet](https://github.com/lkeab/BCNet), a bilayer decoupling instance segmentation method. + [DD3D](https://github.com/TRI-ML/dd3d), A fully convolutional 3D detector. ================================================ FILE: detectron2/projects/Rethinking-BatchNorm/README.md ================================================ # Rethinking "Batch" in BatchNorm We provide configs that reproduce detection experiments in the paper [Rethinking "Batch" in BatchNorm](https://arxiv.org/abs/2105.07576). All configs can be trained with: ``` ../../tools/lazyconfig_train_net.py --config-file configs/X.py --num-gpus 8 ``` ## Mask R-CNN * `mask_rcnn_BNhead.py`, `mask_rcnn_BNhead_batch_stats.py`: Mask R-CNN with BatchNorm in the head. See Table 3 in the paper. * `mask_rcnn_BNhead_shuffle.py`: Mask R-CNN with cross-GPU shuffling of head inputs. See Figure 9 and Table 6 in the paper. * `mask_rcnn_SyncBNhead.py`: Mask R-CNN with cross-GPU SyncBatchNorm in the head. It matches Table 6 in the paper. ## RetinaNet * `retinanet_SyncBNhead.py`: RetinaNet with SyncBN in head, a straightforward implementation which matches row 3 of Table 5. * `retinanet_SyncBNhead_SharedTraining.py`: RetinaNet with SyncBN in head, normalizing all 5 feature levels together. Match row 1 of Table 5. The script `retinanet-eval-domain-specific.py` evaluates a checkpoint after recomputing domain-specific statistics. Running it with ``` ./retinanet-eval-domain-specific.py checkpoint.pth ``` on a model produced by the above two configs, can produce results that match row 4 and row 2 of Table 5. ================================================ FILE: detectron2/projects/Rethinking-BatchNorm/configs/mask_rcnn_BNhead.py ================================================ from detectron2.model_zoo import get_config model = get_config("common/models/mask_rcnn_fpn.py").model model.backbone.bottom_up.freeze_at = 2 model.roi_heads.box_head.conv_norm = model.roi_heads.mask_head.conv_norm = "BN" # 4conv1fc head model.roi_heads.box_head.conv_dims = [256, 256, 256, 256] model.roi_heads.box_head.fc_dims = [1024] dataloader = get_config("common/data/coco.py").dataloader lr_multiplier = get_config("common/coco_schedule.py").lr_multiplier_3x optimizer = get_config("common/optim.py").SGD train = get_config("common/train.py").train train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" train.max_iter = 270000 # 3x for batchsize = 16 ================================================ FILE: detectron2/projects/Rethinking-BatchNorm/configs/mask_rcnn_BNhead_batch_stats.py ================================================ from torch.nn import BatchNorm2d from torch.nn import functional as F class BatchNormBatchStat(BatchNorm2d): """ BN that uses batch stat in inference """ def forward(self, input): if self.training: return super().forward(input) return F.batch_norm(input, None, None, self.weight, self.bias, True, 1.0, self.eps) # After training with the base config, it's sufficient to load its model with # this config only for inference -- because the training-time behavior is identical. from .mask_rcnn_BNhead import model, dataloader, lr_multiplier, optimizer, train model.roi_heads.box_head.conv_norm = model.roi_heads.mask_head.conv_norm = BatchNormBatchStat ================================================ FILE: detectron2/projects/Rethinking-BatchNorm/configs/mask_rcnn_BNhead_shuffle.py ================================================ import math import torch import torch.distributed as dist from detectron2.modeling.roi_heads import FastRCNNConvFCHead, MaskRCNNConvUpsampleHead from detectron2.utils import comm from fvcore.nn.distributed import differentiable_all_gather def concat_all_gather(input): bs_int = input.shape[0] size_list = comm.all_gather(bs_int) max_size = max(size_list) max_shape = (max_size,) + input.shape[1:] padded_input = input.new_zeros(max_shape) padded_input[:bs_int] = input all_inputs = differentiable_all_gather(padded_input) inputs = [x[:sz] for sz, x in zip(size_list, all_inputs)] return inputs, size_list def batch_shuffle(x): # gather from all gpus batch_size_this = x.shape[0] all_xs, batch_size_all = concat_all_gather(x) all_xs_concat = torch.cat(all_xs, dim=0) total_bs = sum(batch_size_all) rank = dist.get_rank() assert batch_size_all[rank] == batch_size_this idx_range = (sum(batch_size_all[:rank]), sum(batch_size_all[: rank + 1])) # random shuffle index idx_shuffle = torch.randperm(total_bs, device=x.device) # broadcast to all gpus dist.broadcast(idx_shuffle, src=0) # index for restoring idx_unshuffle = torch.argsort(idx_shuffle) # shuffled index for this gpu splits = torch.split(idx_shuffle, math.ceil(total_bs / dist.get_world_size())) if len(splits) > rank: idx_this = splits[rank] else: idx_this = idx_shuffle.new_zeros([0]) return all_xs_concat[idx_this], idx_unshuffle[idx_range[0] : idx_range[1]] def batch_unshuffle(x, idx_unshuffle): all_x, _ = concat_all_gather(x) x_gather = torch.cat(all_x, dim=0) return x_gather[idx_unshuffle] def wrap_shuffle(module_type, method): def new_method(self, x): if self.training: x, idx = batch_shuffle(x) x = getattr(module_type, method)(self, x) if self.training: x = batch_unshuffle(x, idx) return x return type(module_type.__name__ + "WithShuffle", (module_type,), {method: new_method}) from .mask_rcnn_BNhead import model, dataloader, lr_multiplier, optimizer, train model.roi_heads.box_head._target_ = wrap_shuffle(FastRCNNConvFCHead, "forward") model.roi_heads.mask_head._target_ = wrap_shuffle(MaskRCNNConvUpsampleHead, "layers") ================================================ FILE: detectron2/projects/Rethinking-BatchNorm/configs/mask_rcnn_SyncBNhead.py ================================================ from .mask_rcnn_BNhead import model, dataloader, lr_multiplier, optimizer, train model.roi_heads.box_head.conv_norm = model.roi_heads.mask_head.conv_norm = "SyncBN" ================================================ FILE: detectron2/projects/Rethinking-BatchNorm/configs/retinanet_SyncBNhead.py ================================================ from detectron2.model_zoo import get_config from torch import nn model = get_config("common/models/retinanet.py").model model.backbone.bottom_up.freeze_at = 2 # The head will overwrite string "SyncBN" to use domain-specific BN, so we # provide a class here to use shared BN in training. model.head.norm = nn.SyncBatchNorm2d dataloader = get_config("common/data/coco.py").dataloader lr_multiplier = get_config("common/coco_schedule.py").lr_multiplier_3x optimizer = get_config("common/optim.py").SGD train = get_config("common/train.py").train optimizer.lr = 0.01 train.init_checkpoint = "detectron2://ImageNetPretrained/MSRA/R-50.pkl" train.max_iter = 270000 # 3x for batchsize = 16 ================================================ FILE: detectron2/projects/Rethinking-BatchNorm/configs/retinanet_SyncBNhead_SharedTraining.py ================================================ from typing import List import torch from torch import Tensor, nn from detectron2.modeling.meta_arch.retinanet import RetinaNetHead def apply_sequential(inputs, modules): for mod in modules: if isinstance(mod, (nn.BatchNorm2d, nn.SyncBatchNorm)): # for BN layer, normalize all inputs together shapes = [i.shape for i in inputs] spatial_sizes = [s[2] * s[3] for s in shapes] x = [i.flatten(2) for i in inputs] x = torch.cat(x, dim=2).unsqueeze(3) x = mod(x).split(spatial_sizes, dim=2) inputs = [i.view(s) for s, i in zip(shapes, x)] else: inputs = [mod(i) for i in inputs] return inputs class RetinaNetHead_SharedTrainingBN(RetinaNetHead): def forward(self, features: List[Tensor]): logits = apply_sequential(features, list(self.cls_subnet) + [self.cls_score]) bbox_reg = apply_sequential(features, list(self.bbox_subnet) + [self.bbox_pred]) return logits, bbox_reg from .retinanet_SyncBNhead import model, dataloader, lr_multiplier, optimizer, train model.head._target_ = RetinaNetHead_SharedTrainingBN ================================================ FILE: detectron2/projects/Rethinking-BatchNorm/retinanet-eval-domain-specific.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. import sys import torch from fvcore.nn.precise_bn import update_bn_stats from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import LazyConfig, instantiate from detectron2.evaluation import inference_on_dataset from detectron2.layers import CycleBatchNormList from detectron2.utils.events import EventStorage from detectron2.utils.logger import setup_logger logger = setup_logger() setup_logger(name="fvcore") if __name__ == "__main__": checkpoint = sys.argv[1] cfg = LazyConfig.load_rel("./configs/retinanet_SyncBNhead.py") model = cfg.model model.head.norm = lambda c: CycleBatchNormList(len(model.head_in_features), num_features=c) model = instantiate(model) model.cuda() DetectionCheckpointer(model).load(checkpoint) cfg.dataloader.train.total_batch_size = 8 logger.info("Running PreciseBN ...") with EventStorage(), torch.no_grad(): update_bn_stats(model, instantiate(cfg.dataloader.train), 500) logger.info("Running evaluation ...") inference_on_dataset( model, instantiate(cfg.dataloader.test), instantiate(cfg.dataloader.evaluator) ) ================================================ FILE: detectron2/projects/TensorMask/README.md ================================================ # TensorMask in Detectron2 **A Foundation for Dense Object Segmentation** Xinlei Chen, Ross Girshick, Kaiming He, Piotr Dollár [[`arXiv`](https://arxiv.org/abs/1903.12174)] [[`BibTeX`](#CitingTensorMask)]
In this repository, we release code for TensorMask in Detectron2. TensorMask is a dense sliding-window instance segmentation framework that, for the first time, achieves results close to the well-developed Mask R-CNN framework -- both qualitatively and quantitatively. It establishes a conceptually complementary direction for object instance segmentation research. ## Installation First install Detectron2 following the [documentation](https://detectron2.readthedocs.io/tutorials/install.html) and [setup the dataset](../../datasets). Then compile the TensorMask-specific op (`swap_align2nat`): ```bash pip install -e /path/to/detectron2/projects/TensorMask ``` ## Training To train a model, run: ```bash python /path/to/detectron2/projects/TensorMask/train_net.py --config-file ``` For example, to launch TensorMask BiPyramid training (1x schedule) with ResNet-50 backbone on 8 GPUs, one should execute: ```bash python /path/to/detectron2/projects/TensorMask/train_net.py --config-file configs/tensormask_R_50_FPN_1x.yaml --num-gpus 8 ``` ## Evaluation Model evaluation can be done similarly (6x schedule with scale augmentation): ```bash python /path/to/detectron2/projects/TensorMask/train_net.py --config-file configs/tensormask_R_50_FPN_6x.yaml --eval-only MODEL.WEIGHTS /path/to/model_checkpoint ``` # Pretrained Models | Backbone | lr sched | AP box | AP mask | download | | -------- | -------- | -- | --- | -------- | | R50 | 1x | 37.6 | 32.4 | model \|  metrics | | R50 | 6x | 41.4 | 35.8 | model \|  metrics | ## Citing TensorMask If you use TensorMask, please use the following BibTeX entry. ``` @InProceedings{chen2019tensormask, title={Tensormask: A Foundation for Dense Object Segmentation}, author={Chen, Xinlei and Girshick, Ross and He, Kaiming and Doll{\'a}r, Piotr}, journal={The International Conference on Computer Vision (ICCV)}, year={2019} } ``` ================================================ FILE: detectron2/projects/TensorMask/configs/Base-TensorMask.yaml ================================================ MODEL: META_ARCHITECTURE: "TensorMask" MASK_ON: True BACKBONE: NAME: "build_retinanet_resnet_fpn_backbone" RESNETS: OUT_FEATURES: ["res2", "res3", "res4", "res5"] ANCHOR_GENERATOR: SIZES: [[44, 60], [88, 120], [176, 240], [352, 480], [704, 960], [1408, 1920]] ASPECT_RATIOS: [[1.0]] FPN: IN_FEATURES: ["res2", "res3", "res4", "res5"] FUSE_TYPE: "avg" TENSOR_MASK: ALIGNED_ON: True BIPYRAMID_ON: True DATASETS: TRAIN: ("coco_2017_train",) TEST: ("coco_2017_val",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.02 STEPS: (60000, 80000) MAX_ITER: 90000 VERSION: 2 ================================================ FILE: detectron2/projects/TensorMask/configs/tensormask_R_50_FPN_1x.yaml ================================================ _BASE_: "Base-TensorMask.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 ================================================ FILE: detectron2/projects/TensorMask/configs/tensormask_R_50_FPN_6x.yaml ================================================ _BASE_: "Base-TensorMask.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" RESNETS: DEPTH: 50 SOLVER: STEPS: (480000, 520000) MAX_ITER: 540000 INPUT: MIN_SIZE_TRAIN_SAMPLING: "range" MIN_SIZE_TRAIN: (640, 800) ================================================ FILE: detectron2/projects/TensorMask/setup.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. import glob import os from setuptools import find_packages, setup import torch from torch.utils.cpp_extension import CUDA_HOME, CppExtension, CUDAExtension def get_extensions(): this_dir = os.path.dirname(os.path.abspath(__file__)) extensions_dir = os.path.join(this_dir, "tensormask", "layers", "csrc") main_source = os.path.join(extensions_dir, "vision.cpp") sources = glob.glob(os.path.join(extensions_dir, "**", "*.cpp")) source_cuda = glob.glob(os.path.join(extensions_dir, "**", "*.cu")) + glob.glob( os.path.join(extensions_dir, "*.cu") ) sources = [main_source] + sources extension = CppExtension extra_compile_args = {"cxx": []} define_macros = [] if (torch.cuda.is_available() and CUDA_HOME is not None) or os.getenv("FORCE_CUDA", "0") == "1": extension = CUDAExtension sources += source_cuda define_macros += [("WITH_CUDA", None)] extra_compile_args["nvcc"] = [ "-DCUDA_HAS_FP16=1", "-D__CUDA_NO_HALF_OPERATORS__", "-D__CUDA_NO_HALF_CONVERSIONS__", "-D__CUDA_NO_HALF2_OPERATORS__", ] # It's better if pytorch can do this by default .. CC = os.environ.get("CC", None) if CC is not None: extra_compile_args["nvcc"].append("-ccbin={}".format(CC)) sources = [os.path.join(extensions_dir, s) for s in sources] include_dirs = [extensions_dir] ext_modules = [ extension( "tensormask._C", sources, include_dirs=include_dirs, define_macros=define_macros, extra_compile_args=extra_compile_args, ) ] return ext_modules setup( name="tensormask", version="0.1", author="FAIR", packages=find_packages(exclude=("configs", "tests")), python_requires=">=3.7", ext_modules=get_extensions(), cmdclass={"build_ext": torch.utils.cpp_extension.BuildExtension}, ) ================================================ FILE: detectron2/projects/TensorMask/tensormask/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .config import add_tensormask_config from .arch import TensorMask ================================================ FILE: detectron2/projects/TensorMask/tensormask/arch.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import math from typing import List import torch import torch.nn.functional as F from fvcore.nn import sigmoid_focal_loss_star_jit, smooth_l1_loss from torch import nn from detectron2.layers import ShapeSpec, batched_nms, cat, paste_masks_in_image from detectron2.modeling.anchor_generator import DefaultAnchorGenerator from detectron2.modeling.backbone import build_backbone from detectron2.modeling.box_regression import Box2BoxTransform from detectron2.modeling.meta_arch.build import META_ARCH_REGISTRY from detectron2.modeling.meta_arch.retinanet import permute_to_N_HWA_K from detectron2.structures import Boxes, ImageList, Instances from tensormask.layers import SwapAlign2Nat __all__ = ["TensorMask"] def permute_all_cls_and_box_to_N_HWA_K_and_concat(pred_logits, pred_anchor_deltas, num_classes=80): """ Rearrange the tensor layout from the network output, i.e.: list[Tensor]: #lvl tensors of shape (N, A x K, Hi, Wi) to per-image predictions, i.e.: Tensor: of shape (N x sum(Hi x Wi x A), K) """ # for each feature level, permute the outputs to make them be in the # same format as the labels. pred_logits_flattened = [permute_to_N_HWA_K(x, num_classes) for x in pred_logits] pred_anchor_deltas_flattened = [permute_to_N_HWA_K(x, 4) for x in pred_anchor_deltas] # concatenate on the first dimension (representing the feature levels), to # take into account the way the labels were generated (with all feature maps # being concatenated as well) pred_logits = cat(pred_logits_flattened, dim=1).view(-1, num_classes) pred_anchor_deltas = cat(pred_anchor_deltas_flattened, dim=1).view(-1, 4) return pred_logits, pred_anchor_deltas def _assignment_rule( gt_boxes, anchor_boxes, unit_lengths, min_anchor_size, scale_thresh=2.0, spatial_thresh=1.0, uniqueness_on=True, ): """ Given two lists of boxes of N ground truth boxes and M anchor boxes, compute the assignment between the two, following the assignment rules in https://arxiv.org/abs/1903.12174. The box order must be (xmin, ymin, xmax, ymax), so please make sure to convert to BoxMode.XYXY_ABS before calling this function. Args: gt_boxes, anchor_boxes (Boxes): two Boxes. Contains N & M boxes/anchors, respectively. unit_lengths (Tensor): Contains the unit lengths of M anchor boxes. min_anchor_size (float): Minimum size of the anchor, in pixels scale_thresh (float): The `scale` threshold: the maximum size of the anchor should not be greater than scale_thresh x max(h, w) of the ground truth box. spatial_thresh (float): The `spatial` threshold: the l2 distance between the center of the anchor and the ground truth box should not be greater than spatial_thresh x u where u is the unit length. Returns: matches (Tensor[int64]): a vector of length M, where matches[i] is a matched ground-truth index in [0, N) match_labels (Tensor[int8]): a vector of length M, where pred_labels[i] indicates whether a prediction is a true or false positive or ignored """ gt_boxes, anchor_boxes = gt_boxes.tensor, anchor_boxes.tensor N = gt_boxes.shape[0] M = anchor_boxes.shape[0] if N == 0 or M == 0: return ( gt_boxes.new_full((N,), 0, dtype=torch.int64), gt_boxes.new_full((N,), -1, dtype=torch.int8), ) # Containment rule lt = torch.min(gt_boxes[:, None, :2], anchor_boxes[:, :2]) # [N,M,2] rb = torch.max(gt_boxes[:, None, 2:], anchor_boxes[:, 2:]) # [N,M,2] union = cat([lt, rb], dim=2) # [N,M,4] dummy_gt_boxes = torch.zeros_like(gt_boxes) anchor = dummy_gt_boxes[:, None, :] + anchor_boxes[:, :] # [N,M,4] contain_matrix = torch.all(union == anchor, dim=2) # [N,M] # Centrality rule, scale gt_size_lower = torch.max(gt_boxes[:, 2:] - gt_boxes[:, :2], dim=1)[0] # [N] gt_size_upper = gt_size_lower * scale_thresh # [N] # Fall back for small objects gt_size_upper[gt_size_upper < min_anchor_size] = min_anchor_size # Due to sampling of locations, the anchor sizes are deducted with sampling strides anchor_size = ( torch.max(anchor_boxes[:, 2:] - anchor_boxes[:, :2], dim=1)[0] - unit_lengths ) # [M] size_diff_upper = gt_size_upper[:, None] - anchor_size # [N,M] scale_matrix = size_diff_upper >= 0 # [N,M] # Centrality rule, spatial gt_center = (gt_boxes[:, 2:] + gt_boxes[:, :2]) / 2 # [N,2] anchor_center = (anchor_boxes[:, 2:] + anchor_boxes[:, :2]) / 2 # [M,2] offset_center = gt_center[:, None, :] - anchor_center[:, :] # [N,M,2] offset_center /= unit_lengths[:, None] # [N,M,2] spatial_square = spatial_thresh * spatial_thresh spatial_matrix = torch.sum(offset_center * offset_center, dim=2) <= spatial_square assign_matrix = (contain_matrix & scale_matrix & spatial_matrix).int() # assign_matrix is N (gt) x M (predicted) # Max over gt elements (dim 0) to find best gt candidate for each prediction matched_vals, matches = assign_matrix.max(dim=0) match_labels = matches.new_full(matches.size(), 1, dtype=torch.int8) match_labels[matched_vals == 0] = 0 match_labels[matched_vals == 1] = 1 # find all the elements that match to ground truths multiple times not_unique_idxs = assign_matrix.sum(dim=0) > 1 if uniqueness_on: match_labels[not_unique_idxs] = 0 else: match_labels[not_unique_idxs] = -1 return matches, match_labels # TODO make the paste_mask function in d2 core support mask list def _paste_mask_lists_in_image(masks, boxes, image_shape, threshold=0.5): """ Paste a list of masks that are of various resolutions (e.g., 28 x 28) into an image. The location, height, and width for pasting each mask is determined by their corresponding bounding boxes in boxes. Args: masks (list(Tensor)): A list of Tensor of shape (1, Hmask_i, Wmask_i). Values are in [0, 1]. The list length, Bimg, is the number of detected object instances in the image. boxes (Boxes): A Boxes of length Bimg. boxes.tensor[i] and masks[i] correspond to the same object instance. image_shape (tuple): height, width threshold (float): A threshold in [0, 1] for converting the (soft) masks to binary masks. Returns: img_masks (Tensor): A tensor of shape (Bimg, Himage, Wimage), where Bimg is the number of detected object instances and Himage, Wimage are the image width and height. img_masks[i] is a binary mask for object instance i. """ if len(masks) == 0: return torch.empty((0, 1) + image_shape, dtype=torch.uint8) # Loop over masks groups. Each group has the same mask prediction size. img_masks = [] ind_masks = [] mask_sizes = torch.tensor([m.shape[-1] for m in masks]) unique_sizes = torch.unique(mask_sizes) for msize in unique_sizes.tolist(): cur_ind = torch.where(mask_sizes == msize)[0] ind_masks.append(cur_ind) cur_masks = cat([masks[i] for i in cur_ind]) cur_boxes = boxes[cur_ind] img_masks.append(paste_masks_in_image(cur_masks, cur_boxes, image_shape, threshold)) img_masks = cat(img_masks) ind_masks = cat(ind_masks) img_masks_out = torch.empty_like(img_masks) img_masks_out[ind_masks, :, :] = img_masks return img_masks_out def _postprocess(results, result_mask_info, output_height, output_width, mask_threshold=0.5): """ Post-process the output boxes for TensorMask. The input images are often resized when entering an object detector. As a result, we often need the outputs of the detector in a different resolution from its inputs. This function will postprocess the raw outputs of TensorMask to produce outputs according to the desired output resolution. Args: results (Instances): the raw outputs from the detector. `results.image_size` contains the input image resolution the detector sees. This object might be modified in-place. Note that it does not contain the field `pred_masks`, which is provided by another input `result_masks`. result_mask_info (list[Tensor], Boxes): a pair of two items for mask related results. The first item is a list of #detection tensors, each is the predicted masks. The second item is the anchors corresponding to the predicted masks. output_height, output_width: the desired output resolution. Returns: Instances: the postprocessed output from the model, based on the output resolution """ scale_x, scale_y = (output_width / results.image_size[1], output_height / results.image_size[0]) results = Instances((output_height, output_width), **results.get_fields()) output_boxes = results.pred_boxes output_boxes.tensor[:, 0::2] *= scale_x output_boxes.tensor[:, 1::2] *= scale_y output_boxes.clip(results.image_size) inds_nonempty = output_boxes.nonempty() results = results[inds_nonempty] result_masks, result_anchors = result_mask_info if result_masks: result_anchors.tensor[:, 0::2] *= scale_x result_anchors.tensor[:, 1::2] *= scale_y result_masks = [x for (i, x) in zip(inds_nonempty.tolist(), result_masks) if i] results.pred_masks = _paste_mask_lists_in_image( result_masks, result_anchors[inds_nonempty], results.image_size, threshold=mask_threshold, ) return results class TensorMaskAnchorGenerator(DefaultAnchorGenerator): """ For a set of image sizes and feature maps, computes a set of anchors for TensorMask. It also computes the unit lengths and indexes for each anchor box. """ def grid_anchors_with_unit_lengths_and_indexes(self, grid_sizes): anchors = [] unit_lengths = [] indexes = [] for lvl, (size, stride, base_anchors) in enumerate( zip(grid_sizes, self.strides, self.cell_anchors) ): grid_height, grid_width = size device = base_anchors.device shifts_x = torch.arange( 0, grid_width * stride, step=stride, dtype=torch.float32, device=device ) shifts_y = torch.arange( 0, grid_height * stride, step=stride, dtype=torch.float32, device=device ) shift_y, shift_x = torch.meshgrid(shifts_y, shifts_x) shifts = torch.stack((shift_x, shift_y, shift_x, shift_y), dim=2) # Stack anchors in shapes of (HWA, 4) cur_anchor = (shifts[:, :, None, :] + base_anchors.view(1, 1, -1, 4)).view(-1, 4) anchors.append(cur_anchor) unit_lengths.append( torch.full((cur_anchor.shape[0],), stride, dtype=torch.float32, device=device) ) # create mask indexes using mesh grid shifts_l = torch.full((1,), lvl, dtype=torch.int64, device=device) shifts_i = torch.zeros((1,), dtype=torch.int64, device=device) shifts_h = torch.arange(0, grid_height, dtype=torch.int64, device=device) shifts_w = torch.arange(0, grid_width, dtype=torch.int64, device=device) shifts_a = torch.arange(0, base_anchors.shape[0], dtype=torch.int64, device=device) grids = torch.meshgrid(shifts_l, shifts_i, shifts_h, shifts_w, shifts_a) indexes.append(torch.stack(grids, dim=5).view(-1, 5)) return anchors, unit_lengths, indexes def forward(self, features): """ Returns: list[list[Boxes]]: a list of #image elements. Each is a list of #feature level Boxes. The Boxes contains anchors of this image on the specific feature level. list[list[Tensor]]: a list of #image elements. Each is a list of #feature level tensors. The tensor contains strides, or unit lengths for the anchors. list[list[Tensor]]: a list of #image elements. Each is a list of #feature level tensors. The Tensor contains indexes for the anchors, with the last dimension meaning (L, N, H, W, A), where L is level, I is image (not set yet), H is height, W is width, and A is anchor. """ num_images = len(features[0]) grid_sizes = [feature_map.shape[-2:] for feature_map in features] anchors_list, lengths_list, indexes_list = self.grid_anchors_with_unit_lengths_and_indexes( grid_sizes ) # Convert anchors from Tensor to Boxes anchors_per_im = [Boxes(x) for x in anchors_list] # TODO it can be simplified to not return duplicated information for # each image, just like detectron2's own AnchorGenerator anchors = [copy.deepcopy(anchors_per_im) for _ in range(num_images)] unit_lengths = [copy.deepcopy(lengths_list) for _ in range(num_images)] indexes = [copy.deepcopy(indexes_list) for _ in range(num_images)] return anchors, unit_lengths, indexes @META_ARCH_REGISTRY.register() class TensorMask(nn.Module): """ TensorMask model. Creates FPN backbone, anchors and a head for classification and box regression. Calculates and applies proper losses to class, box, and masks. """ def __init__(self, cfg): super().__init__() # fmt: off self.num_classes = cfg.MODEL.TENSOR_MASK.NUM_CLASSES self.in_features = cfg.MODEL.TENSOR_MASK.IN_FEATURES self.anchor_sizes = cfg.MODEL.ANCHOR_GENERATOR.SIZES self.num_levels = len(cfg.MODEL.ANCHOR_GENERATOR.SIZES) # Loss parameters: self.focal_loss_alpha = cfg.MODEL.TENSOR_MASK.FOCAL_LOSS_ALPHA self.focal_loss_gamma = cfg.MODEL.TENSOR_MASK.FOCAL_LOSS_GAMMA # Inference parameters: self.score_threshold = cfg.MODEL.TENSOR_MASK.SCORE_THRESH_TEST self.topk_candidates = cfg.MODEL.TENSOR_MASK.TOPK_CANDIDATES_TEST self.nms_threshold = cfg.MODEL.TENSOR_MASK.NMS_THRESH_TEST self.detections_im = cfg.TEST.DETECTIONS_PER_IMAGE # Mask parameters: self.mask_on = cfg.MODEL.MASK_ON self.mask_loss_weight = cfg.MODEL.TENSOR_MASK.MASK_LOSS_WEIGHT self.mask_pos_weight = torch.tensor(cfg.MODEL.TENSOR_MASK.POSITIVE_WEIGHT, dtype=torch.float32) self.bipyramid_on = cfg.MODEL.TENSOR_MASK.BIPYRAMID_ON # fmt: on # build the backbone self.backbone = build_backbone(cfg) backbone_shape = self.backbone.output_shape() feature_shapes = [backbone_shape[f] for f in self.in_features] feature_strides = [x.stride for x in feature_shapes] # build anchors self.anchor_generator = TensorMaskAnchorGenerator(cfg, feature_shapes) self.num_anchors = self.anchor_generator.num_cell_anchors[0] anchors_min_level = cfg.MODEL.ANCHOR_GENERATOR.SIZES[0] self.mask_sizes = [size // feature_strides[0] for size in anchors_min_level] self.min_anchor_size = min(anchors_min_level) - feature_strides[0] # head of the TensorMask self.head = TensorMaskHead( cfg, self.num_levels, self.num_anchors, self.mask_sizes, feature_shapes ) # box transform self.box2box_transform = Box2BoxTransform(weights=cfg.MODEL.TENSOR_MASK.BBOX_REG_WEIGHTS) self.register_buffer("pixel_mean", torch.tensor(cfg.MODEL.PIXEL_MEAN).view(-1, 1, 1), False) self.register_buffer("pixel_std", torch.tensor(cfg.MODEL.PIXEL_STD).view(-1, 1, 1), False) @property def device(self): return self.pixel_mean.device def forward(self, batched_inputs): """ Args: batched_inputs: a list, batched outputs of :class:`DetectionTransform` . Each item in the list contains the inputs for one image. For now, each item in the list is a dict that contains: image: Tensor, image in (C, H, W) format. instances: Instances Other information that's included in the original dicts, such as: "height", "width" (int): the output resolution of the model, used in inference. See :meth:`postprocess` for details. Returns: losses (dict[str: Tensor]): mapping from a named loss to a tensor storing the loss. Used during training only. """ images = self.preprocess_image(batched_inputs) if "instances" in batched_inputs[0]: gt_instances = [x["instances"].to(self.device) for x in batched_inputs] else: gt_instances = None features = self.backbone(images.tensor) features = [features[f] for f in self.in_features] # apply the TensorMask head pred_logits, pred_deltas, pred_masks = self.head(features) # generate anchors based on features, is it image specific? anchors, unit_lengths, indexes = self.anchor_generator(features) if self.training: # get ground truths for class labels and box targets, it will label each anchor gt_class_info, gt_delta_info, gt_mask_info, num_fg = self.get_ground_truth( anchors, unit_lengths, indexes, gt_instances ) # compute the loss return self.losses( gt_class_info, gt_delta_info, gt_mask_info, num_fg, pred_logits, pred_deltas, pred_masks, ) else: # do inference to get the output results = self.inference(pred_logits, pred_deltas, pred_masks, anchors, indexes, images) processed_results = [] for results_im, input_im, image_size in zip( results, batched_inputs, images.image_sizes ): height = input_im.get("height", image_size[0]) width = input_im.get("width", image_size[1]) # this is to do post-processing with the image size result_box, result_mask = results_im r = _postprocess(result_box, result_mask, height, width) processed_results.append({"instances": r}) return processed_results def losses( self, gt_class_info, gt_delta_info, gt_mask_info, num_fg, pred_logits, pred_deltas, pred_masks, ): """ Args: For `gt_class_info`, `gt_delta_info`, `gt_mask_info` and `num_fg` parameters, see :meth:`TensorMask.get_ground_truth`. For `pred_logits`, `pred_deltas` and `pred_masks`, see :meth:`TensorMaskHead.forward`. Returns: losses (dict[str: Tensor]): mapping from a named loss to a scalar tensor storing the loss. Used during training only. The potential dict keys are: "loss_cls", "loss_box_reg" and "loss_mask". """ gt_classes_target, gt_valid_inds = gt_class_info gt_deltas, gt_fg_inds = gt_delta_info gt_masks, gt_mask_inds = gt_mask_info loss_normalizer = torch.tensor(max(1, num_fg), dtype=torch.float32, device=self.device) # classification and regression pred_logits, pred_deltas = permute_all_cls_and_box_to_N_HWA_K_and_concat( pred_logits, pred_deltas, self.num_classes ) loss_cls = ( sigmoid_focal_loss_star_jit( pred_logits[gt_valid_inds], gt_classes_target[gt_valid_inds], alpha=self.focal_loss_alpha, gamma=self.focal_loss_gamma, reduction="sum", ) / loss_normalizer ) if num_fg == 0: loss_box_reg = pred_deltas.sum() * 0 else: loss_box_reg = ( smooth_l1_loss(pred_deltas[gt_fg_inds], gt_deltas, beta=0.0, reduction="sum") / loss_normalizer ) losses = {"loss_cls": loss_cls, "loss_box_reg": loss_box_reg} # mask prediction if self.mask_on: loss_mask = 0 for lvl in range(self.num_levels): cur_level_factor = 2**lvl if self.bipyramid_on else 1 for anc in range(self.num_anchors): cur_gt_mask_inds = gt_mask_inds[lvl][anc] if cur_gt_mask_inds is None: loss_mask += pred_masks[lvl][anc][0, 0, 0, 0] * 0 else: cur_mask_size = self.mask_sizes[anc] * cur_level_factor # TODO maybe there are numerical issues when mask sizes are large cur_size_divider = torch.tensor( self.mask_loss_weight / (cur_mask_size**2), dtype=torch.float32, device=self.device, ) cur_pred_masks = pred_masks[lvl][anc][ cur_gt_mask_inds[:, 0], # N :, # V x U cur_gt_mask_inds[:, 1], # H cur_gt_mask_inds[:, 2], # W ] loss_mask += F.binary_cross_entropy_with_logits( cur_pred_masks.view(-1, cur_mask_size, cur_mask_size), # V, U gt_masks[lvl][anc].to(dtype=torch.float32), reduction="sum", weight=cur_size_divider, pos_weight=self.mask_pos_weight, ) losses["loss_mask"] = loss_mask / loss_normalizer return losses @torch.no_grad() def get_ground_truth(self, anchors, unit_lengths, indexes, targets): """ Args: anchors (list[list[Boxes]]): a list of N=#image elements. Each is a list of #feature level Boxes. The Boxes contains anchors of this image on the specific feature level. unit_lengths (list[list[Tensor]]): a list of N=#image elements. Each is a list of #feature level Tensor. The tensor contains unit lengths for anchors of this image on the specific feature level. indexes (list[list[Tensor]]): a list of N=#image elements. Each is a list of #feature level Tensor. The tensor contains the 5D index of each anchor, the second dimension means (L, N, H, W, A), where L is level, I is image, H is height, W is width, and A is anchor. targets (list[Instances]): a list of N `Instances`s. The i-th `Instances` contains the ground-truth per-instance annotations for the i-th input image. Specify `targets` during training only. Returns: gt_class_info (Tensor, Tensor): A pair of two tensors for classification. The first one is an integer tensor of shape (R, #classes) storing ground-truth labels for each anchor. R is the total number of anchors in the batch. The second one is an integer tensor of shape (R,), to indicate which anchors are valid for loss computation, which anchors are not. gt_delta_info (Tensor, Tensor): A pair of two tensors for boxes. The first one, of shape (F, 4). F=#foreground anchors. The last dimension represents ground-truth box2box transform targets (dx, dy, dw, dh) that map each anchor to its matched ground-truth box. Only foreground anchors have values in this tensor. Could be `None` if F=0. The second one, of shape (R,), is an integer tensor indicating which anchors are foreground ones used for box regression. Could be `None` if F=0. gt_mask_info (list[list[Tensor]], list[list[Tensor]]): A pair of two lists for masks. The first one is a list of P=#feature level elements. Each is a list of A=#anchor tensors. Each tensor contains the ground truth masks of the same size and for the same feature level. Could be `None`. The second one is a list of P=#feature level elements. Each is a list of A=#anchor tensors. Each tensor contains the location of the ground truth masks of the same size and for the same feature level. The second dimension means (N, H, W), where N is image, H is height, and W is width. Could be `None`. num_fg (int): F=#foreground anchors, used later for loss normalization. """ gt_classes = [] gt_deltas = [] gt_masks = [[[] for _ in range(self.num_anchors)] for _ in range(self.num_levels)] gt_mask_inds = [[[] for _ in range(self.num_anchors)] for _ in range(self.num_levels)] anchors = [Boxes.cat(anchors_i) for anchors_i in anchors] unit_lengths = [cat(unit_lengths_i) for unit_lengths_i in unit_lengths] indexes = [cat(indexes_i) for indexes_i in indexes] num_fg = 0 for i, (anchors_im, unit_lengths_im, indexes_im, targets_im) in enumerate( zip(anchors, unit_lengths, indexes, targets) ): # Initialize all gt_classes_i = torch.full_like( unit_lengths_im, self.num_classes, dtype=torch.int64, device=self.device ) # Ground truth classes has_gt = len(targets_im) > 0 if has_gt: # Compute the pairwise matrix gt_matched_inds, anchor_labels = _assignment_rule( targets_im.gt_boxes, anchors_im, unit_lengths_im, self.min_anchor_size ) # Find the foreground instances fg_inds = anchor_labels == 1 fg_anchors = anchors_im[fg_inds] num_fg += len(fg_anchors) # Find the ground truths for foreground instances gt_fg_matched_inds = gt_matched_inds[fg_inds] # Assign labels for foreground instances gt_classes_i[fg_inds] = targets_im.gt_classes[gt_fg_matched_inds] # Anchors with label -1 are ignored, others are left as negative gt_classes_i[anchor_labels == -1] = -1 # Boxes # Ground truth box regression, only for foregrounds matched_gt_boxes = targets_im[gt_fg_matched_inds].gt_boxes # Compute box regression offsets for foregrounds only gt_deltas_i = self.box2box_transform.get_deltas( fg_anchors.tensor, matched_gt_boxes.tensor ) gt_deltas.append(gt_deltas_i) # Masks if self.mask_on: # Compute masks for each level and each anchor matched_indexes = indexes_im[fg_inds, :] for lvl in range(self.num_levels): ids_lvl = matched_indexes[:, 0] == lvl if torch.any(ids_lvl): cur_level_factor = 2**lvl if self.bipyramid_on else 1 for anc in range(self.num_anchors): ids_lvl_anchor = ids_lvl & (matched_indexes[:, 4] == anc) if torch.any(ids_lvl_anchor): gt_masks[lvl][anc].append( targets_im[ gt_fg_matched_inds[ids_lvl_anchor] ].gt_masks.crop_and_resize( fg_anchors[ids_lvl_anchor].tensor, self.mask_sizes[anc] * cur_level_factor, ) ) # Select (N, H, W) dimensions gt_mask_inds_lvl_anc = matched_indexes[ids_lvl_anchor, 1:4] # Set the image index to the current image gt_mask_inds_lvl_anc[:, 0] = i gt_mask_inds[lvl][anc].append(gt_mask_inds_lvl_anc) gt_classes.append(gt_classes_i) # Classes and boxes gt_classes = cat(gt_classes) gt_valid_inds = gt_classes >= 0 gt_fg_inds = gt_valid_inds & (gt_classes < self.num_classes) gt_classes_target = torch.zeros( (gt_classes.shape[0], self.num_classes), dtype=torch.float32, device=self.device ) gt_classes_target[gt_fg_inds, gt_classes[gt_fg_inds]] = 1 gt_deltas = cat(gt_deltas) if gt_deltas else None # Masks gt_masks = [[cat(mla) if mla else None for mla in ml] for ml in gt_masks] gt_mask_inds = [[cat(ila) if ila else None for ila in il] for il in gt_mask_inds] return ( (gt_classes_target, gt_valid_inds), (gt_deltas, gt_fg_inds), (gt_masks, gt_mask_inds), num_fg, ) def inference(self, pred_logits, pred_deltas, pred_masks, anchors, indexes, images): """ Arguments: pred_logits, pred_deltas, pred_masks: Same as the output of: meth:`TensorMaskHead.forward` anchors, indexes: Same as the input of meth:`TensorMask.get_ground_truth` images (ImageList): the input images Returns: results (List[Instances]): a list of #images elements. """ assert len(anchors) == len(images) results = [] pred_logits = [permute_to_N_HWA_K(x, self.num_classes) for x in pred_logits] pred_deltas = [permute_to_N_HWA_K(x, 4) for x in pred_deltas] pred_logits = cat(pred_logits, dim=1) pred_deltas = cat(pred_deltas, dim=1) for img_idx, (anchors_im, indexes_im) in enumerate(zip(anchors, indexes)): # Get the size of the current image image_size = images.image_sizes[img_idx] logits_im = pred_logits[img_idx] deltas_im = pred_deltas[img_idx] if self.mask_on: masks_im = [[mla[img_idx] for mla in ml] for ml in pred_masks] else: masks_im = [None] * self.num_levels results_im = self.inference_single_image( logits_im, deltas_im, masks_im, Boxes.cat(anchors_im), cat(indexes_im), tuple(image_size), ) results.append(results_im) return results def inference_single_image( self, pred_logits, pred_deltas, pred_masks, anchors, indexes, image_size ): """ Single-image inference. Return bounding-box detection results by thresholding on scores and applying non-maximum suppression (NMS). Arguments: pred_logits (list[Tensor]): list of #feature levels. Each entry contains tensor of size (AxHxW, K) pred_deltas (list[Tensor]): Same shape as 'pred_logits' except that K becomes 4. pred_masks (list[list[Tensor]]): List of #feature levels, each is a list of #anchors. Each entry contains tensor of size (M_i*M_i, H, W). `None` if mask_on=False. anchors (list[Boxes]): list of #feature levels. Each entry contains a Boxes object, which contains all the anchors for that image in that feature level. image_size (tuple(H, W)): a tuple of the image height and width. Returns: Same as `inference`, but for only one image. """ pred_logits = pred_logits.flatten().sigmoid_() # We get top locations across all levels to accelerate the inference speed, # which does not seem to affect the accuracy. # First select values above the threshold logits_top_idxs = torch.where(pred_logits > self.score_threshold)[0] # Then get the top values num_topk = min(self.topk_candidates, logits_top_idxs.shape[0]) pred_prob, topk_idxs = pred_logits[logits_top_idxs].sort(descending=True) # Keep top k scoring values pred_prob = pred_prob[:num_topk] # Keep top k values top_idxs = logits_top_idxs[topk_idxs[:num_topk]] # class index cls_idxs = top_idxs % self.num_classes # HWA index top_idxs //= self.num_classes # predict boxes pred_boxes = self.box2box_transform.apply_deltas( pred_deltas[top_idxs], anchors[top_idxs].tensor ) # apply nms keep = batched_nms(pred_boxes, pred_prob, cls_idxs, self.nms_threshold) # pick the top ones keep = keep[: self.detections_im] results = Instances(image_size) results.pred_boxes = Boxes(pred_boxes[keep]) results.scores = pred_prob[keep] results.pred_classes = cls_idxs[keep] # deal with masks result_masks, result_anchors = [], None if self.mask_on: # index and anchors, useful for masks top_indexes = indexes[top_idxs] top_anchors = anchors[top_idxs] result_indexes = top_indexes[keep] result_anchors = top_anchors[keep] # Get masks and do sigmoid for lvl, _, h, w, anc in result_indexes.tolist(): cur_size = self.mask_sizes[anc] * (2**lvl if self.bipyramid_on else 1) result_masks.append( torch.sigmoid(pred_masks[lvl][anc][:, h, w].view(1, cur_size, cur_size)) ) return results, (result_masks, result_anchors) def preprocess_image(self, batched_inputs): """ Normalize, pad and batch the input images. """ images = [x["image"].to(self.device) for x in batched_inputs] images = [(x - self.pixel_mean) / self.pixel_std for x in images] images = ImageList.from_tensors(images, self.backbone.size_divisibility) return images class TensorMaskHead(nn.Module): def __init__(self, cfg, num_levels, num_anchors, mask_sizes, input_shape: List[ShapeSpec]): """ TensorMask head. """ super().__init__() # fmt: off self.in_features = cfg.MODEL.TENSOR_MASK.IN_FEATURES in_channels = input_shape[0].channels num_classes = cfg.MODEL.TENSOR_MASK.NUM_CLASSES cls_channels = cfg.MODEL.TENSOR_MASK.CLS_CHANNELS num_convs = cfg.MODEL.TENSOR_MASK.NUM_CONVS # box parameters bbox_channels = cfg.MODEL.TENSOR_MASK.BBOX_CHANNELS # mask parameters self.mask_on = cfg.MODEL.MASK_ON self.mask_sizes = mask_sizes mask_channels = cfg.MODEL.TENSOR_MASK.MASK_CHANNELS self.align_on = cfg.MODEL.TENSOR_MASK.ALIGNED_ON self.bipyramid_on = cfg.MODEL.TENSOR_MASK.BIPYRAMID_ON # fmt: on # class subnet cls_subnet = [] cur_channels = in_channels for _ in range(num_convs): cls_subnet.append( nn.Conv2d(cur_channels, cls_channels, kernel_size=3, stride=1, padding=1) ) cur_channels = cls_channels cls_subnet.append(nn.ReLU()) self.cls_subnet = nn.Sequential(*cls_subnet) self.cls_score = nn.Conv2d( cur_channels, num_anchors * num_classes, kernel_size=3, stride=1, padding=1 ) modules_list = [self.cls_subnet, self.cls_score] # box subnet bbox_subnet = [] cur_channels = in_channels for _ in range(num_convs): bbox_subnet.append( nn.Conv2d(cur_channels, bbox_channels, kernel_size=3, stride=1, padding=1) ) cur_channels = bbox_channels bbox_subnet.append(nn.ReLU()) self.bbox_subnet = nn.Sequential(*bbox_subnet) self.bbox_pred = nn.Conv2d( cur_channels, num_anchors * 4, kernel_size=3, stride=1, padding=1 ) modules_list.extend([self.bbox_subnet, self.bbox_pred]) # mask subnet if self.mask_on: mask_subnet = [] cur_channels = in_channels for _ in range(num_convs): mask_subnet.append( nn.Conv2d(cur_channels, mask_channels, kernel_size=3, stride=1, padding=1) ) cur_channels = mask_channels mask_subnet.append(nn.ReLU()) self.mask_subnet = nn.Sequential(*mask_subnet) modules_list.append(self.mask_subnet) for mask_size in self.mask_sizes: cur_mask_module = "mask_pred_%02d" % mask_size self.add_module( cur_mask_module, nn.Conv2d( cur_channels, mask_size * mask_size, kernel_size=1, stride=1, padding=0 ), ) modules_list.append(getattr(self, cur_mask_module)) if self.align_on: if self.bipyramid_on: for lvl in range(num_levels): cur_mask_module = "align2nat_%02d" % lvl lambda_val = 2**lvl setattr(self, cur_mask_module, SwapAlign2Nat(lambda_val)) # Also the fusing layer, stay at the same channel size mask_fuse = [ nn.Conv2d(cur_channels, cur_channels, kernel_size=3, stride=1, padding=1), nn.ReLU(), ] self.mask_fuse = nn.Sequential(*mask_fuse) modules_list.append(self.mask_fuse) else: self.align2nat = SwapAlign2Nat(1) # Initialization for modules in modules_list: for layer in modules.modules(): if isinstance(layer, nn.Conv2d): torch.nn.init.normal_(layer.weight, mean=0, std=0.01) torch.nn.init.constant_(layer.bias, 0) # Use prior in model initialization to improve stability bias_value = -(math.log((1 - 0.01) / 0.01)) torch.nn.init.constant_(self.cls_score.bias, bias_value) def forward(self, features): """ Arguments: features (list[Tensor]): FPN feature map tensors in high to low resolution. Each tensor in the list correspond to different feature levels. Returns: pred_logits (list[Tensor]): #lvl tensors, each has shape (N, AxK, Hi, Wi). The tensor predicts the classification probability at each spatial position for each of the A anchors and K object classes. pred_deltas (list[Tensor]): #lvl tensors, each has shape (N, Ax4, Hi, Wi). The tensor predicts 4-vector (dx,dy,dw,dh) box regression values for every anchor. These values are the relative offset between the anchor and the ground truth box. pred_masks (list(list[Tensor])): #lvl list of tensors, each is a list of A tensors of shape (N, M_{i,a}, Hi, Wi). The tensor predicts a dense set of M_ixM_i masks at every location. """ pred_logits = [self.cls_score(self.cls_subnet(x)) for x in features] pred_deltas = [self.bbox_pred(self.bbox_subnet(x)) for x in features] pred_masks = None if self.mask_on: mask_feats = [self.mask_subnet(x) for x in features] if self.bipyramid_on: mask_feat_high_res = mask_feats[0] H, W = mask_feat_high_res.shape[-2:] mask_feats_up = [] for lvl, mask_feat in enumerate(mask_feats): lambda_val = 2.0**lvl mask_feat_up = mask_feat if lvl > 0: mask_feat_up = F.interpolate( mask_feat, scale_factor=lambda_val, mode="bilinear", align_corners=False ) mask_feats_up.append( self.mask_fuse(mask_feat_up[:, :, :H, :W] + mask_feat_high_res) ) mask_feats = mask_feats_up pred_masks = [] for lvl, mask_feat in enumerate(mask_feats): cur_masks = [] for mask_size in self.mask_sizes: cur_mask_module = getattr(self, "mask_pred_%02d" % mask_size) cur_mask = cur_mask_module(mask_feat) if self.align_on: if self.bipyramid_on: cur_mask_module = getattr(self, "align2nat_%02d" % lvl) cur_mask = cur_mask_module(cur_mask) else: cur_mask = self.align2nat(cur_mask) cur_masks.append(cur_mask) pred_masks.append(cur_masks) return pred_logits, pred_deltas, pred_masks ================================================ FILE: detectron2/projects/TensorMask/tensormask/config.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.config import CfgNode as CN def add_tensormask_config(cfg): """ Add config for TensorMask. """ cfg.MODEL.TENSOR_MASK = CN() # Anchor parameters cfg.MODEL.TENSOR_MASK.IN_FEATURES = ["p2", "p3", "p4", "p5", "p6", "p7"] # Convolutions to use in the towers cfg.MODEL.TENSOR_MASK.NUM_CONVS = 4 # Number of foreground classes. cfg.MODEL.TENSOR_MASK.NUM_CLASSES = 80 # Channel size for the classification tower cfg.MODEL.TENSOR_MASK.CLS_CHANNELS = 256 cfg.MODEL.TENSOR_MASK.SCORE_THRESH_TEST = 0.05 # Only the top (1000 * #levels) candidate boxes across all levels are # considered jointly during test (to improve speed) cfg.MODEL.TENSOR_MASK.TOPK_CANDIDATES_TEST = 6000 cfg.MODEL.TENSOR_MASK.NMS_THRESH_TEST = 0.5 # Box parameters # Channel size for the box tower cfg.MODEL.TENSOR_MASK.BBOX_CHANNELS = 128 # Weights on (dx, dy, dw, dh) cfg.MODEL.TENSOR_MASK.BBOX_REG_WEIGHTS = (1.5, 1.5, 0.75, 0.75) # Loss parameters cfg.MODEL.TENSOR_MASK.FOCAL_LOSS_GAMMA = 3.0 cfg.MODEL.TENSOR_MASK.FOCAL_LOSS_ALPHA = 0.3 # Mask parameters # Channel size for the mask tower cfg.MODEL.TENSOR_MASK.MASK_CHANNELS = 128 # Mask loss weight cfg.MODEL.TENSOR_MASK.MASK_LOSS_WEIGHT = 2.0 # weight on positive pixels within the mask cfg.MODEL.TENSOR_MASK.POSITIVE_WEIGHT = 1.5 # Whether to predict in the aligned representation cfg.MODEL.TENSOR_MASK.ALIGNED_ON = False # Whether to use the bipyramid architecture cfg.MODEL.TENSOR_MASK.BIPYRAMID_ON = False ================================================ FILE: detectron2/projects/TensorMask/tensormask/layers/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .swap_align2nat import SwapAlign2Nat, swap_align2nat __all__ = [k for k in globals().keys() if not k.startswith("_")] ================================================ FILE: detectron2/projects/TensorMask/tensormask/layers/csrc/SwapAlign2Nat/SwapAlign2Nat.h ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #pragma once #include namespace tensormask { #if defined(WITH_CUDA) || defined(WITH_HIP) at::Tensor SwapAlign2Nat_forward_cuda( const at::Tensor& X, const int lambda_val, const float pad_val); at::Tensor SwapAlign2Nat_backward_cuda( const at::Tensor& gY, const int lambda_val, const int batch_size, const int channel, const int height, const int width); #endif inline at::Tensor SwapAlign2Nat_forward( const at::Tensor& X, const int lambda_val, const float pad_val) { if (X.type().is_cuda()) { #if defined(WITH_CUDA) || defined(WITH_HIP) return SwapAlign2Nat_forward_cuda(X, lambda_val, pad_val); #else AT_ERROR("Not compiled with GPU support"); #endif } AT_ERROR("Not implemented on the CPU"); } inline at::Tensor SwapAlign2Nat_backward( const at::Tensor& gY, const int lambda_val, const int batch_size, const int channel, const int height, const int width) { if (gY.type().is_cuda()) { #if defined(WITH_CUDA) || defined(WITH_HIP) return SwapAlign2Nat_backward_cuda( gY, lambda_val, batch_size, channel, height, width); #else AT_ERROR("Not compiled with GPU support"); #endif } AT_ERROR("Not implemented on the CPU"); } } // namespace tensormask ================================================ FILE: detectron2/projects/TensorMask/tensormask/layers/csrc/SwapAlign2Nat/SwapAlign2Nat_cuda.cu ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #include #include #include #include // TODO make it in a common file #define CUDA_1D_KERNEL_LOOP(i, n) \ for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < n; \ i += blockDim.x * gridDim.x) template __device__ inline T get_pixel_val( const T* tensor, const int idx, const int H, const int W, const int y, const int x, const int V, const int U, const int v, const int u, const T pad_val) { if ((y < 0) || (y >= H) || (x < 0) || (x >= W) || (v < 0) || (v >= V) || (u < 0) || (u >= U)) { return pad_val; } else { return tensor[(((idx * V + v) * U + u) * H + y) * W + x]; } } template __device__ inline void add_pixel_val( T* tensor, const T val, const int idx, const int H, const int W, const int y, const int x, const int V, const int U, const int v, const int u) { if ((val == 0.) || (y < 0) || (y >= H) || (x < 0) || (x >= W) || (v < 0) || (v >= V) || (u < 0) || (u >= U)) { return; } else { atomicAdd(tensor + ((((idx * V + v) * U + u) * H + y) * W + x), val); } } template __global__ void SwapAlign2NatForwardFeat( const int nthreads, const T* bottom_data, const int Vout, const int Uout, const float hVout, const float hUout, const int Vin, const int Uin, const float lambda, const int Hin, const int Win, const int Hout, const int Wout, const T pad_val, T* top_data) { CUDA_1D_KERNEL_LOOP(index, nthreads) { int idx = index; const int x = idx % Wout; idx /= Wout; const int y = idx % Hout; idx /= Hout; const int u = idx % Uout; idx /= Uout; const int v = idx % Vout; idx /= Vout; const float ox = x * lambda + u - hUout + 0.5; const int xf = static_cast(floor(ox)); const int xc = static_cast(ceil(ox)); const float xwc = ox - xf; const float xwf = 1. - xwc; const float oy = y * lambda + v - hVout + 0.5; const int yf = static_cast(floor(oy)); const int yc = static_cast(ceil(oy)); const float ywc = oy - yf; const float ywf = 1. - ywc; const float ou = (u + 0.5) / lambda - 0.5; const int uf = static_cast(floor(ou)); const int uc = static_cast(ceil(ou)); const float uwc = ou - uf; const float uwf = 1. - uwc; const float ov = (v + 0.5) / lambda - 0.5; const int vf = static_cast(floor(ov)); const int vc = static_cast(ceil(ov)); const float vwc = ov - vf; const float vwf = 1. - vwc; T val = ywf * xwf * vwf * uwf * get_pixel_val( bottom_data, idx, Hin, Win, yf, xf, Vin, Uin, vf, uf, pad_val) + ywf * xwf * vwf * uwc * get_pixel_val( bottom_data, idx, Hin, Win, yf, xf, Vin, Uin, vf, uc, pad_val) + ywf * xwf * vwc * uwf * get_pixel_val( bottom_data, idx, Hin, Win, yf, xf, Vin, Uin, vc, uf, pad_val) + ywf * xwf * vwc * uwc * get_pixel_val( bottom_data, idx, Hin, Win, yf, xf, Vin, Uin, vc, uc, pad_val) + ywf * xwc * vwf * uwf * get_pixel_val( bottom_data, idx, Hin, Win, yf, xc, Vin, Uin, vf, uf, pad_val) + ywf * xwc * vwf * uwc * get_pixel_val( bottom_data, idx, Hin, Win, yf, xc, Vin, Uin, vf, uc, pad_val) + ywf * xwc * vwc * uwf * get_pixel_val( bottom_data, idx, Hin, Win, yf, xc, Vin, Uin, vc, uf, pad_val) + ywf * xwc * vwc * uwc * get_pixel_val( bottom_data, idx, Hin, Win, yf, xc, Vin, Uin, vc, uc, pad_val) + ywc * xwf * vwf * uwf * get_pixel_val( bottom_data, idx, Hin, Win, yc, xf, Vin, Uin, vf, uf, pad_val) + ywc * xwf * vwf * uwc * get_pixel_val( bottom_data, idx, Hin, Win, yc, xf, Vin, Uin, vf, uc, pad_val) + ywc * xwf * vwc * uwf * get_pixel_val( bottom_data, idx, Hin, Win, yc, xf, Vin, Uin, vc, uf, pad_val) + ywc * xwf * vwc * uwc * get_pixel_val( bottom_data, idx, Hin, Win, yc, xf, Vin, Uin, vc, uc, pad_val) + ywc * xwc * vwf * uwf * get_pixel_val( bottom_data, idx, Hin, Win, yc, xc, Vin, Uin, vf, uf, pad_val) + ywc * xwc * vwf * uwc * get_pixel_val( bottom_data, idx, Hin, Win, yc, xc, Vin, Uin, vf, uc, pad_val) + ywc * xwc * vwc * uwf * get_pixel_val( bottom_data, idx, Hin, Win, yc, xc, Vin, Uin, vc, uf, pad_val) + ywc * xwc * vwc * uwc * get_pixel_val( bottom_data, idx, Hin, Win, yc, xc, Vin, Uin, vc, uc, pad_val); top_data[index] = val; } } template __global__ void SwapAlign2NatBackwardFeat( const int nthreads, const T* top_diff, const int Vout, const int Uout, const float hVout, const float hUout, const int Vin, const int Uin, const float lambda, const int Hin, const int Win, const int Hout, const int Wout, T* bottom_diff) { CUDA_1D_KERNEL_LOOP(index, nthreads) { int idx = index; const int x = idx % Wout; idx /= Wout; const int y = idx % Hout; idx /= Hout; const int u = idx % Uout; idx /= Uout; const int v = idx % Vout; idx /= Vout; const float ox = x * lambda + u - hUout + 0.5; const int xf = static_cast(floor(ox)); const int xc = static_cast(ceil(ox)); const float xwc = ox - xf; const float xwf = 1. - xwc; const float oy = y * lambda + v - hVout + 0.5; const int yf = static_cast(floor(oy)); const int yc = static_cast(ceil(oy)); const float ywc = oy - yf; const float ywf = 1. - ywc; const float ou = (u + 0.5) / lambda - 0.5; const int uf = static_cast(floor(ou)); const int uc = static_cast(ceil(ou)); const float uwc = ou - uf; const float uwf = 1. - uwc; const float ov = (v + 0.5) / lambda - 0.5; const int vf = static_cast(floor(ov)); const int vc = static_cast(ceil(ov)); const float vwc = ov - vf; const float vwf = 1. - vwc; const T grad = top_diff[index]; add_pixel_val( bottom_diff, ywf * xwf * vwf * uwf * grad, idx, Hin, Win, yf, xf, Vin, Uin, vf, uf); add_pixel_val( bottom_diff, ywf * xwf * vwf * uwc * grad, idx, Hin, Win, yf, xf, Vin, Uin, vf, uc); add_pixel_val( bottom_diff, ywf * xwf * vwc * uwf * grad, idx, Hin, Win, yf, xf, Vin, Uin, vc, uf); add_pixel_val( bottom_diff, ywf * xwf * vwc * uwc * grad, idx, Hin, Win, yf, xf, Vin, Uin, vc, uc); add_pixel_val( bottom_diff, ywf * xwc * vwf * uwf * grad, idx, Hin, Win, yf, xc, Vin, Uin, vf, uf); add_pixel_val( bottom_diff, ywf * xwc * vwf * uwc * grad, idx, Hin, Win, yf, xc, Vin, Uin, vf, uc); add_pixel_val( bottom_diff, ywf * xwc * vwc * uwf * grad, idx, Hin, Win, yf, xc, Vin, Uin, vc, uf); add_pixel_val( bottom_diff, ywf * xwc * vwc * uwc * grad, idx, Hin, Win, yf, xc, Vin, Uin, vc, uc); add_pixel_val( bottom_diff, ywc * xwf * vwf * uwf * grad, idx, Hin, Win, yc, xf, Vin, Uin, vf, uf); add_pixel_val( bottom_diff, ywc * xwf * vwf * uwc * grad, idx, Hin, Win, yc, xf, Vin, Uin, vf, uc); add_pixel_val( bottom_diff, ywc * xwf * vwc * uwf * grad, idx, Hin, Win, yc, xf, Vin, Uin, vc, uf); add_pixel_val( bottom_diff, ywc * xwf * vwc * uwc * grad, idx, Hin, Win, yc, xf, Vin, Uin, vc, uc); add_pixel_val( bottom_diff, ywc * xwc * vwf * uwf * grad, idx, Hin, Win, yc, xc, Vin, Uin, vf, uf); add_pixel_val( bottom_diff, ywc * xwc * vwf * uwc * grad, idx, Hin, Win, yc, xc, Vin, Uin, vf, uc); add_pixel_val( bottom_diff, ywc * xwc * vwc * uwf * grad, idx, Hin, Win, yc, xc, Vin, Uin, vc, uf); add_pixel_val( bottom_diff, ywc * xwc * vwc * uwc * grad, idx, Hin, Win, yc, xc, Vin, Uin, vc, uc); } } namespace tensormask { at::Tensor SwapAlign2Nat_forward_cuda( const at::Tensor& X, const int lambda_val, const float pad_val) { AT_ASSERTM(X.device().is_cuda(), "input must be a CUDA tensor"); AT_ASSERTM(X.ndimension() == 4, "input must be a 4D tensor"); AT_ASSERTM(lambda_val >= 1, "lambda should be greater or equal to 1"); const int N = X.size(0); const int C = X.size(1); const int Vin = static_cast(sqrt(static_cast(C))); const int Uin = C / Vin; AT_ASSERTM( C == Vin * Uin && Vin == Uin, "#channels should be a square number"); const int Vout = lambda_val * Vin; const int Uout = lambda_val * Uin; const int Hin = X.size(2); const int Win = X.size(3); const float lambda = static_cast(lambda_val); const int Hout = static_cast(ceil(Hin / lambda)); const int Wout = static_cast(ceil(Win / lambda)); const float hVout = Vout / 2.; const float hUout = Uout / 2.; at::cuda::CUDAGuard device_guard(X.device()); at::Tensor Y = at::empty({N, Vout * Uout, Hout, Wout}, X.options()); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); dim3 grid(std::min(at::cuda::ATenCeilDiv(Y.numel(), 512L), 4096L)); dim3 block(512); if (Y.numel() == 0) { AT_CUDA_CHECK(cudaGetLastError()); return Y; } auto X_ = X.contiguous(); AT_DISPATCH_FLOATING_TYPES(X.scalar_type(), "SwapAlign2Nat_forward", [&] { SwapAlign2NatForwardFeat<<>>( Y.numel(), X_.data_ptr(), Vout, Uout, hVout, hUout, Vin, Uin, lambda, Hin, Win, Hout, Wout, pad_val, Y.data_ptr()); }); cudaDeviceSynchronize(); AT_CUDA_CHECK(cudaGetLastError()); return Y; } at::Tensor SwapAlign2Nat_backward_cuda( const at::Tensor& gY, const int lambda_val, const int batch_size, const int channel, const int height, const int width) { AT_ASSERTM(gY.device().is_cuda(), "input gradient must be a CUDA tensor"); AT_ASSERTM(gY.ndimension() == 4, "input gradient must be a 4D tensor"); AT_ASSERTM(lambda_val >= 1, "lambda should be greater or equal to 1"); const int Vin = static_cast(sqrt(static_cast(channel))); const int Uin = channel / Vin; const int Vout = lambda_val * Vin; const int Uout = lambda_val * Uin; const float hVout = Vout / 2.; const float hUout = Uout / 2.; const int Hout = gY.size(2); const int Wout = gY.size(3); at::cuda::CUDAGuard device_guard(gY.device()); at::Tensor gX = at::zeros({batch_size, channel, height, width}, gY.options()); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); dim3 grid(std::min(at::cuda::ATenCeilDiv(gY.numel(), 512L), 4096L)); dim3 block(512); // handle possibly empty gradients if (gY.numel() == 0) { AT_CUDA_CHECK(cudaGetLastError()); return gX; } auto gY_ = gY.contiguous(); AT_DISPATCH_FLOATING_TYPES(gY.scalar_type(), "SwapAlign2Nat_backward", [&] { SwapAlign2NatBackwardFeat<<>>( gY.numel(), gY_.data_ptr(), Vout, Uout, hVout, hUout, Vin, Uin, static_cast(lambda_val), height, width, Hout, Wout, gX.data_ptr()); }); AT_CUDA_CHECK(cudaGetLastError()); return gX; } } // namespace tensormask ================================================ FILE: detectron2/projects/TensorMask/tensormask/layers/csrc/vision.cpp ================================================ // Copyright (c) Facebook, Inc. and its affiliates. #include #include "SwapAlign2Nat/SwapAlign2Nat.h" namespace tensormask { PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { m.def( "swap_align2nat_forward", &SwapAlign2Nat_forward, "SwapAlign2Nat_forward"); m.def( "swap_align2nat_backward", &SwapAlign2Nat_backward, "SwapAlign2Nat_backward"); } } // namespace tensormask ================================================ FILE: detectron2/projects/TensorMask/tensormask/layers/swap_align2nat.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from torch import nn from torch.autograd import Function from torch.autograd.function import once_differentiable from tensormask import _C class _SwapAlign2Nat(Function): @staticmethod def forward(ctx, X, lambda_val, pad_val): ctx.lambda_val = lambda_val ctx.input_shape = X.size() Y = _C.swap_align2nat_forward(X, lambda_val, pad_val) return Y @staticmethod @once_differentiable def backward(ctx, gY): lambda_val = ctx.lambda_val bs, ch, h, w = ctx.input_shape gX = _C.swap_align2nat_backward(gY, lambda_val, bs, ch, h, w) return gX, None, None swap_align2nat = _SwapAlign2Nat.apply class SwapAlign2Nat(nn.Module): """ The op `SwapAlign2Nat` described in https://arxiv.org/abs/1903.12174. Given an input tensor that predicts masks of shape (N, C=VxU, H, W), apply the op, it will return masks of shape (N, V'xU', H', W') where the unit lengths of (V, U) and (H, W) are swapped, and the mask representation is transformed from aligned to natural. Args: lambda_val (int): the relative unit length ratio between (V, U) and (H, W), as we always have larger unit lengths for (V, U) than (H, W), lambda_val is always >= 1. pad_val (float): padding value for the values falling outside of the input tensor, default set to -6 as sigmoid(-6) is ~0, indicating that is no masks outside of the tensor. """ def __init__(self, lambda_val, pad_val=-6.0): super(SwapAlign2Nat, self).__init__() self.lambda_val = lambda_val self.pad_val = pad_val def forward(self, X): return swap_align2nat(X, self.lambda_val, self.pad_val) def __repr__(self): tmpstr = self.__class__.__name__ + "(" tmpstr += "lambda_val=" + str(self.lambda_val) tmpstr += ", pad_val=" + str(self.pad_val) tmpstr += ")" return tmpstr ================================================ FILE: detectron2/projects/TensorMask/tests/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. ================================================ FILE: detectron2/projects/TensorMask/tests/test_swap_align2nat.py ================================================ #!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. import unittest import torch from torch.autograd import gradcheck from tensormask.layers.swap_align2nat import SwapAlign2Nat class SwapAlign2NatTest(unittest.TestCase): @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_swap_align2nat_gradcheck_cuda(self): dtype = torch.float64 device = torch.device("cuda") m = SwapAlign2Nat(2).to(dtype=dtype, device=device) x = torch.rand(2, 4, 10, 10, dtype=dtype, device=device, requires_grad=True) self.assertTrue(gradcheck(m, x), "gradcheck failed for SwapAlign2Nat CUDA") def _swap_align2nat(self, tensor, lambda_val): """ The basic setup for testing Swap_Align """ op = SwapAlign2Nat(lambda_val, pad_val=0.0) input = torch.from_numpy(tensor[None, :, :, :].astype("float32")) output = op.forward(input.cuda()).cpu().numpy() return output[0] if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/projects/TensorMask/train_net.py ================================================ #!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. """ TensorMask Training Script. This script is a simplified version of the training script in detectron2/tools. """ import os import detectron2.utils.comm as comm from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import get_cfg from detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, launch from detectron2.evaluation import COCOEvaluator, verify_results from tensormask import add_tensormask_config class Trainer(DefaultTrainer): @classmethod def build_evaluator(cls, cfg, dataset_name, output_folder=None): if output_folder is None: output_folder = os.path.join(cfg.OUTPUT_DIR, "inference") return COCOEvaluator(dataset_name, output_dir=output_folder) def setup(args): """ Create configs and perform basic setups. """ cfg = get_cfg() add_tensormask_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.freeze() default_setup(cfg, args) return cfg def main(args): cfg = setup(args) if args.eval_only: model = Trainer.build_model(cfg) DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load( cfg.MODEL.WEIGHTS, resume=args.resume ) res = Trainer.test(cfg, model) if comm.is_main_process(): verify_results(cfg, res) return res trainer = Trainer(cfg) trainer.resume_or_load(resume=args.resume) return trainer.train() if __name__ == "__main__": args = default_argument_parser().parse_args() print("Command Line Args:", args) launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), ) ================================================ FILE: detectron2/projects/TridentNet/README.md ================================================ # TridentNet in Detectron2 **Scale-Aware Trident Networks for Object Detection** Yanghao Li\*, Yuntao Chen\*, Naiyan Wang, Zhaoxiang Zhang [[`TridentNet`](https://github.com/TuSimple/simpledet/tree/master/models/tridentnet)] [[`arXiv`](https://arxiv.org/abs/1901.01892)] [[`BibTeX`](#CitingTridentNet)]
In this repository, we implement TridentNet-Fast in Detectron2. Trident Network (TridentNet) aims to generate scale-specific feature maps with a uniform representational power. We construct a parallel multi-branch architecture in which each branch shares the same transformation parameters but with different receptive fields. TridentNet-Fast is a fast approximation version of TridentNet that could achieve significant improvements without any additional parameters and computational cost. ## Training To train a model, run ```bash python /path/to/detectron2/projects/TridentNet/train_net.py --config-file ``` For example, to launch end-to-end TridentNet training with ResNet-50 backbone on 8 GPUs, one should execute: ```bash python /path/to/detectron2/projects/TridentNet/train_net.py --config-file configs/tridentnet_fast_R_50_C4_1x.yaml --num-gpus 8 ``` ## Evaluation Model evaluation can be done similarly: ```bash python /path/to/detectron2/projects/TridentNet/train_net.py --config-file configs/tridentnet_fast_R_50_C4_1x.yaml --eval-only MODEL.WEIGHTS model.pth ``` ## Results on MS-COCO in Detectron2 |Model|Backbone|Head|lr sched|AP|AP50|AP75|APs|APm|APl|download| |-----|--------|----|--------|--|----|----|---|---|---|--------| |Faster|R50-C4|C5-512ROI|1X|35.7|56.1|38.0|19.2|40.9|48.7|model \| metrics| |TridentFast|R50-C4|C5-128ROI|1X|38.0|58.1|40.8|19.5|42.2|54.6|model \| metrics| |Faster|R50-C4|C5-512ROI|3X|38.4|58.7|41.3|20.7|42.7|53.1|model \| metrics| |TridentFast|R50-C4|C5-128ROI|3X|40.6|60.8|43.6|23.4|44.7|57.1|model \| metrics| |Faster|R101-C4|C5-512ROI|3X|41.1|61.4|44.0|22.2|45.5|55.9|model \| metrics| |TridentFast|R101-C4|C5-128ROI|3X|43.6|63.4|47.0|24.3|47.8|60.0|model \| metrics| ## Citing TridentNet If you use TridentNet, please use the following BibTeX entry. ``` @InProceedings{li2019scale, title={Scale-Aware Trident Networks for Object Detection}, author={Li, Yanghao and Chen, Yuntao and Wang, Naiyan and Zhang, Zhaoxiang}, journal={The International Conference on Computer Vision (ICCV)}, year={2019} } ``` ================================================ FILE: detectron2/projects/TridentNet/configs/Base-TridentNet-Fast-C4.yaml ================================================ MODEL: META_ARCHITECTURE: "GeneralizedRCNN" BACKBONE: NAME: "build_trident_resnet_backbone" ROI_HEADS: NAME: "TridentRes5ROIHeads" POSITIVE_FRACTION: 0.5 BATCH_SIZE_PER_IMAGE: 128 PROPOSAL_APPEND_GT: False PROPOSAL_GENERATOR: NAME: "TridentRPN" RPN: POST_NMS_TOPK_TRAIN: 500 TRIDENT: NUM_BRANCH: 3 BRANCH_DILATIONS: [1, 2, 3] TEST_BRANCH_IDX: 1 TRIDENT_STAGE: "res4" DATASETS: TRAIN: ("coco_2017_train",) TEST: ("coco_2017_val",) SOLVER: IMS_PER_BATCH: 16 BASE_LR: 0.02 STEPS: (60000, 80000) MAX_ITER: 90000 INPUT: MIN_SIZE_TRAIN: (640, 672, 704, 736, 768, 800) VERSION: 2 ================================================ FILE: detectron2/projects/TridentNet/configs/tridentnet_fast_R_101_C4_3x.yaml ================================================ _BASE_: "Base-TridentNet-Fast-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-101.pkl" MASK_ON: False RESNETS: DEPTH: 101 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/projects/TridentNet/configs/tridentnet_fast_R_50_C4_1x.yaml ================================================ _BASE_: "Base-TridentNet-Fast-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 ================================================ FILE: detectron2/projects/TridentNet/configs/tridentnet_fast_R_50_C4_3x.yaml ================================================ _BASE_: "Base-TridentNet-Fast-C4.yaml" MODEL: WEIGHTS: "detectron2://ImageNetPretrained/MSRA/R-50.pkl" MASK_ON: False RESNETS: DEPTH: 50 SOLVER: STEPS: (210000, 250000) MAX_ITER: 270000 ================================================ FILE: detectron2/projects/TridentNet/train_net.py ================================================ #!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. """ TridentNet Training Script. This script is a simplified version of the training script in detectron2/tools. """ import os from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import get_cfg from detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, launch from detectron2.evaluation import COCOEvaluator from tridentnet import add_tridentnet_config class Trainer(DefaultTrainer): @classmethod def build_evaluator(cls, cfg, dataset_name, output_folder=None): if output_folder is None: output_folder = os.path.join(cfg.OUTPUT_DIR, "inference") return COCOEvaluator(dataset_name, output_dir=output_folder) def setup(args): """ Create configs and perform basic setups. """ cfg = get_cfg() add_tridentnet_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.freeze() default_setup(cfg, args) return cfg def main(args): cfg = setup(args) if args.eval_only: model = Trainer.build_model(cfg) DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load( cfg.MODEL.WEIGHTS, resume=args.resume ) res = Trainer.test(cfg, model) return res trainer = Trainer(cfg) trainer.resume_or_load(resume=args.resume) return trainer.train() if __name__ == "__main__": args = default_argument_parser().parse_args() print("Command Line Args:", args) launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), ) ================================================ FILE: detectron2/projects/TridentNet/tridentnet/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .config import add_tridentnet_config from .trident_backbone import ( TridentBottleneckBlock, build_trident_resnet_backbone, make_trident_stage, ) from .trident_rpn import TridentRPN from .trident_rcnn import TridentRes5ROIHeads, TridentStandardROIHeads ================================================ FILE: detectron2/projects/TridentNet/tridentnet/config.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.config import CfgNode as CN def add_tridentnet_config(cfg): """ Add config for tridentnet. """ _C = cfg _C.MODEL.TRIDENT = CN() # Number of branches for TridentNet. _C.MODEL.TRIDENT.NUM_BRANCH = 3 # Specify the dilations for each branch. _C.MODEL.TRIDENT.BRANCH_DILATIONS = [1, 2, 3] # Specify the stage for applying trident blocks. Default stage is Res4 according to the # TridentNet paper. _C.MODEL.TRIDENT.TRIDENT_STAGE = "res4" # Specify the test branch index TridentNet Fast inference: # - use -1 to aggregate results of all branches during inference. # - otherwise, only using specified branch for fast inference. Recommended setting is # to use the middle branch. _C.MODEL.TRIDENT.TEST_BRANCH_IDX = 1 ================================================ FILE: detectron2/projects/TridentNet/tridentnet/trident_backbone.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import fvcore.nn.weight_init as weight_init import torch import torch.nn.functional as F from detectron2.layers import Conv2d, FrozenBatchNorm2d, get_norm from detectron2.modeling import BACKBONE_REGISTRY, ResNet, ResNetBlockBase from detectron2.modeling.backbone.resnet import BasicStem, BottleneckBlock, DeformBottleneckBlock from .trident_conv import TridentConv __all__ = ["TridentBottleneckBlock", "make_trident_stage", "build_trident_resnet_backbone"] class TridentBottleneckBlock(ResNetBlockBase): def __init__( self, in_channels, out_channels, *, bottleneck_channels, stride=1, num_groups=1, norm="BN", stride_in_1x1=False, num_branch=3, dilations=(1, 2, 3), concat_output=False, test_branch_idx=-1, ): """ Args: num_branch (int): the number of branches in TridentNet. dilations (tuple): the dilations of multiple branches in TridentNet. concat_output (bool): if concatenate outputs of multiple branches in TridentNet. Use 'True' for the last trident block. """ super().__init__(in_channels, out_channels, stride) assert num_branch == len(dilations) self.num_branch = num_branch self.concat_output = concat_output self.test_branch_idx = test_branch_idx if in_channels != out_channels: self.shortcut = Conv2d( in_channels, out_channels, kernel_size=1, stride=stride, bias=False, norm=get_norm(norm, out_channels), ) else: self.shortcut = None stride_1x1, stride_3x3 = (stride, 1) if stride_in_1x1 else (1, stride) self.conv1 = Conv2d( in_channels, bottleneck_channels, kernel_size=1, stride=stride_1x1, bias=False, norm=get_norm(norm, bottleneck_channels), ) self.conv2 = TridentConv( bottleneck_channels, bottleneck_channels, kernel_size=3, stride=stride_3x3, paddings=dilations, bias=False, groups=num_groups, dilations=dilations, num_branch=num_branch, test_branch_idx=test_branch_idx, norm=get_norm(norm, bottleneck_channels), ) self.conv3 = Conv2d( bottleneck_channels, out_channels, kernel_size=1, bias=False, norm=get_norm(norm, out_channels), ) for layer in [self.conv1, self.conv2, self.conv3, self.shortcut]: if layer is not None: # shortcut can be None weight_init.c2_msra_fill(layer) def forward(self, x): num_branch = self.num_branch if self.training or self.test_branch_idx == -1 else 1 if not isinstance(x, list): x = [x] * num_branch out = [self.conv1(b) for b in x] out = [F.relu_(b) for b in out] out = self.conv2(out) out = [F.relu_(b) for b in out] out = [self.conv3(b) for b in out] if self.shortcut is not None: shortcut = [self.shortcut(b) for b in x] else: shortcut = x out = [out_b + shortcut_b for out_b, shortcut_b in zip(out, shortcut)] out = [F.relu_(b) for b in out] if self.concat_output: out = torch.cat(out) return out def make_trident_stage(block_class, num_blocks, **kwargs): """ Create a resnet stage by creating many blocks for TridentNet. """ concat_output = [False] * (num_blocks - 1) + [True] kwargs["concat_output_per_block"] = concat_output return ResNet.make_stage(block_class, num_blocks, **kwargs) @BACKBONE_REGISTRY.register() def build_trident_resnet_backbone(cfg, input_shape): """ Create a ResNet instance from config for TridentNet. Returns: ResNet: a :class:`ResNet` instance. """ # need registration of new blocks/stems? norm = cfg.MODEL.RESNETS.NORM stem = BasicStem( in_channels=input_shape.channels, out_channels=cfg.MODEL.RESNETS.STEM_OUT_CHANNELS, norm=norm, ) freeze_at = cfg.MODEL.BACKBONE.FREEZE_AT if freeze_at >= 1: for p in stem.parameters(): p.requires_grad = False stem = FrozenBatchNorm2d.convert_frozen_batchnorm(stem) # fmt: off out_features = cfg.MODEL.RESNETS.OUT_FEATURES depth = cfg.MODEL.RESNETS.DEPTH num_groups = cfg.MODEL.RESNETS.NUM_GROUPS width_per_group = cfg.MODEL.RESNETS.WIDTH_PER_GROUP bottleneck_channels = num_groups * width_per_group in_channels = cfg.MODEL.RESNETS.STEM_OUT_CHANNELS out_channels = cfg.MODEL.RESNETS.RES2_OUT_CHANNELS stride_in_1x1 = cfg.MODEL.RESNETS.STRIDE_IN_1X1 res5_dilation = cfg.MODEL.RESNETS.RES5_DILATION deform_on_per_stage = cfg.MODEL.RESNETS.DEFORM_ON_PER_STAGE deform_modulated = cfg.MODEL.RESNETS.DEFORM_MODULATED deform_num_groups = cfg.MODEL.RESNETS.DEFORM_NUM_GROUPS num_branch = cfg.MODEL.TRIDENT.NUM_BRANCH branch_dilations = cfg.MODEL.TRIDENT.BRANCH_DILATIONS trident_stage = cfg.MODEL.TRIDENT.TRIDENT_STAGE test_branch_idx = cfg.MODEL.TRIDENT.TEST_BRANCH_IDX # fmt: on assert res5_dilation in {1, 2}, "res5_dilation cannot be {}.".format(res5_dilation) num_blocks_per_stage = {50: [3, 4, 6, 3], 101: [3, 4, 23, 3], 152: [3, 8, 36, 3]}[depth] stages = [] res_stage_idx = {"res2": 2, "res3": 3, "res4": 4, "res5": 5} out_stage_idx = [res_stage_idx[f] for f in out_features] trident_stage_idx = res_stage_idx[trident_stage] max_stage_idx = max(out_stage_idx) for idx, stage_idx in enumerate(range(2, max_stage_idx + 1)): dilation = res5_dilation if stage_idx == 5 else 1 first_stride = 1 if idx == 0 or (stage_idx == 5 and dilation == 2) else 2 stage_kargs = { "num_blocks": num_blocks_per_stage[idx], "stride_per_block": [first_stride] + [1] * (num_blocks_per_stage[idx] - 1), "in_channels": in_channels, "bottleneck_channels": bottleneck_channels, "out_channels": out_channels, "num_groups": num_groups, "norm": norm, "stride_in_1x1": stride_in_1x1, "dilation": dilation, } if stage_idx == trident_stage_idx: assert not deform_on_per_stage[ idx ], "Not support deformable conv in Trident blocks yet." stage_kargs["block_class"] = TridentBottleneckBlock stage_kargs["num_branch"] = num_branch stage_kargs["dilations"] = branch_dilations stage_kargs["test_branch_idx"] = test_branch_idx stage_kargs.pop("dilation") elif deform_on_per_stage[idx]: stage_kargs["block_class"] = DeformBottleneckBlock stage_kargs["deform_modulated"] = deform_modulated stage_kargs["deform_num_groups"] = deform_num_groups else: stage_kargs["block_class"] = BottleneckBlock blocks = ( make_trident_stage(**stage_kargs) if stage_idx == trident_stage_idx else ResNet.make_stage(**stage_kargs) ) in_channels = out_channels out_channels *= 2 bottleneck_channels *= 2 if freeze_at >= stage_idx: for block in blocks: block.freeze() stages.append(blocks) return ResNet(stem, stages, out_features=out_features) ================================================ FILE: detectron2/projects/TridentNet/tridentnet/trident_conv.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch from torch import nn from torch.nn import functional as F from torch.nn.modules.utils import _pair from detectron2.layers.wrappers import _NewEmptyTensorOp class TridentConv(nn.Module): def __init__( self, in_channels, out_channels, kernel_size, stride=1, paddings=0, dilations=1, groups=1, num_branch=1, test_branch_idx=-1, bias=False, norm=None, activation=None, ): super(TridentConv, self).__init__() self.in_channels = in_channels self.out_channels = out_channels self.kernel_size = _pair(kernel_size) self.num_branch = num_branch self.stride = _pair(stride) self.groups = groups self.with_bias = bias if isinstance(paddings, int): paddings = [paddings] * self.num_branch if isinstance(dilations, int): dilations = [dilations] * self.num_branch self.paddings = [_pair(padding) for padding in paddings] self.dilations = [_pair(dilation) for dilation in dilations] self.test_branch_idx = test_branch_idx self.norm = norm self.activation = activation assert len({self.num_branch, len(self.paddings), len(self.dilations)}) == 1 self.weight = nn.Parameter( torch.Tensor(out_channels, in_channels // groups, *self.kernel_size) ) if bias: self.bias = nn.Parameter(torch.Tensor(out_channels)) else: self.bias = None nn.init.kaiming_uniform_(self.weight, nonlinearity="relu") if self.bias is not None: nn.init.constant_(self.bias, 0) def forward(self, inputs): num_branch = self.num_branch if self.training or self.test_branch_idx == -1 else 1 assert len(inputs) == num_branch if inputs[0].numel() == 0: output_shape = [ (i + 2 * p - (di * (k - 1) + 1)) // s + 1 for i, p, di, k, s in zip( inputs[0].shape[-2:], self.padding, self.dilation, self.kernel_size, self.stride ) ] output_shape = [input[0].shape[0], self.weight.shape[0]] + output_shape return [_NewEmptyTensorOp.apply(input, output_shape) for input in inputs] if self.training or self.test_branch_idx == -1: outputs = [ F.conv2d(input, self.weight, self.bias, self.stride, padding, dilation, self.groups) for input, dilation, padding in zip(inputs, self.dilations, self.paddings) ] else: outputs = [ F.conv2d( inputs[0], self.weight, self.bias, self.stride, self.paddings[self.test_branch_idx], self.dilations[self.test_branch_idx], self.groups, ) ] if self.norm is not None: outputs = [self.norm(x) for x in outputs] if self.activation is not None: outputs = [self.activation(x) for x in outputs] return outputs def extra_repr(self): tmpstr = "in_channels=" + str(self.in_channels) tmpstr += ", out_channels=" + str(self.out_channels) tmpstr += ", kernel_size=" + str(self.kernel_size) tmpstr += ", num_branch=" + str(self.num_branch) tmpstr += ", test_branch_idx=" + str(self.test_branch_idx) tmpstr += ", stride=" + str(self.stride) tmpstr += ", paddings=" + str(self.paddings) tmpstr += ", dilations=" + str(self.dilations) tmpstr += ", groups=" + str(self.groups) tmpstr += ", bias=" + str(self.with_bias) return tmpstr ================================================ FILE: detectron2/projects/TridentNet/tridentnet/trident_rcnn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.layers import batched_nms from detectron2.modeling import ROI_HEADS_REGISTRY, StandardROIHeads from detectron2.modeling.roi_heads.roi_heads import Res5ROIHeads from detectron2.structures import Instances def merge_branch_instances(instances, num_branch, nms_thresh, topk_per_image): """ Merge detection results from different branches of TridentNet. Return detection results by applying non-maximum suppression (NMS) on bounding boxes and keep the unsuppressed boxes and other instances (e.g mask) if any. Args: instances (list[Instances]): A list of N * num_branch instances that store detection results. Contain N images and each image has num_branch instances. num_branch (int): Number of branches used for merging detection results for each image. nms_thresh (float): The threshold to use for box non-maximum suppression. Value in [0, 1]. topk_per_image (int): The number of top scoring detections to return. Set < 0 to return all detections. Returns: results: (list[Instances]): A list of N instances, one for each image in the batch, that stores the topk most confidence detections after merging results from multiple branches. """ if num_branch == 1: return instances batch_size = len(instances) // num_branch results = [] for i in range(batch_size): instance = Instances.cat([instances[i + batch_size * j] for j in range(num_branch)]) # Apply per-class NMS keep = batched_nms( instance.pred_boxes.tensor, instance.scores, instance.pred_classes, nms_thresh ) keep = keep[:topk_per_image] result = instance[keep] results.append(result) return results @ROI_HEADS_REGISTRY.register() class TridentRes5ROIHeads(Res5ROIHeads): """ The TridentNet ROIHeads in a typical "C4" R-CNN model. See :class:`Res5ROIHeads`. """ def __init__(self, cfg, input_shape): super().__init__(cfg, input_shape) self.num_branch = cfg.MODEL.TRIDENT.NUM_BRANCH self.trident_fast = cfg.MODEL.TRIDENT.TEST_BRANCH_IDX != -1 def forward(self, images, features, proposals, targets=None): """ See :class:`Res5ROIHeads.forward`. """ num_branch = self.num_branch if self.training or not self.trident_fast else 1 all_targets = targets * num_branch if targets is not None else None pred_instances, losses = super().forward(images, features, proposals, all_targets) del images, all_targets, targets if self.training: return pred_instances, losses else: pred_instances = merge_branch_instances( pred_instances, num_branch, self.box_predictor.test_nms_thresh, self.box_predictor.test_topk_per_image, ) return pred_instances, {} @ROI_HEADS_REGISTRY.register() class TridentStandardROIHeads(StandardROIHeads): """ The `StandardROIHeads` for TridentNet. See :class:`StandardROIHeads`. """ def __init__(self, cfg, input_shape): super(TridentStandardROIHeads, self).__init__(cfg, input_shape) self.num_branch = cfg.MODEL.TRIDENT.NUM_BRANCH self.trident_fast = cfg.MODEL.TRIDENT.TEST_BRANCH_IDX != -1 def forward(self, images, features, proposals, targets=None): """ See :class:`Res5ROIHeads.forward`. """ # Use 1 branch if using trident_fast during inference. num_branch = self.num_branch if self.training or not self.trident_fast else 1 # Duplicate targets for all branches in TridentNet. all_targets = targets * num_branch if targets is not None else None pred_instances, losses = super().forward(images, features, proposals, all_targets) del images, all_targets, targets if self.training: return pred_instances, losses else: pred_instances = merge_branch_instances( pred_instances, num_branch, self.box_predictor.test_nms_thresh, self.box_predictor.test_topk_per_image, ) return pred_instances, {} ================================================ FILE: detectron2/projects/TridentNet/tridentnet/trident_rpn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch from detectron2.modeling import PROPOSAL_GENERATOR_REGISTRY from detectron2.modeling.proposal_generator.rpn import RPN from detectron2.structures import ImageList @PROPOSAL_GENERATOR_REGISTRY.register() class TridentRPN(RPN): """ Trident RPN subnetwork. """ def __init__(self, cfg, input_shape): super(TridentRPN, self).__init__(cfg, input_shape) self.num_branch = cfg.MODEL.TRIDENT.NUM_BRANCH self.trident_fast = cfg.MODEL.TRIDENT.TEST_BRANCH_IDX != -1 def forward(self, images, features, gt_instances=None): """ See :class:`RPN.forward`. """ num_branch = self.num_branch if self.training or not self.trident_fast else 1 # Duplicate images and gt_instances for all branches in TridentNet. all_images = ImageList( torch.cat([images.tensor] * num_branch), images.image_sizes * num_branch ) all_gt_instances = gt_instances * num_branch if gt_instances is not None else None return super(TridentRPN, self).forward(all_images, features, all_gt_instances) ================================================ FILE: detectron2/projects/ViTDet/README.md ================================================ # ViTDet: Exploring Plain Vision Transformer Backbones for Object Detection Yanghao Li, Hanzi Mao, Ross Girshick†, Kaiming He† [[`arXiv`](https://arxiv.org/abs/2203.16527)] [[`BibTeX`](#CitingViTDet)] In this repository, we provide configs and models in Detectron2 for ViTDet as well as MViTv2 and Swin backbones with our implementation and settings as described in [ViTDet](https://arxiv.org/abs/2203.16527) paper. ## Pretrained Models ### COCO #### Mask R-CNN
Name pre-train train
time
(s/im)
inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
model id download
ViTDet, ViT-B IN1K, MAE 0.314 0.079 10.9 51.6 45.9 325346929 model
ViTDet, ViT-L IN1K, MAE 0.603 0.125 20.9 55.5 49.2 325599698 model
ViTDet, ViT-H IN1K, MAE 1.098 0.178 31.5 56.7 50.2 329145471 model
#### Cascade Mask R-CNN
Name pre-train train
time
(s/im)
inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
model id download
Swin-B IN21K, sup 0.389 0.077 8.7 53.9 46.2 342979038 model
Swin-L IN21K, sup 0.508 0.097 12.6 55.0 47.2 342979186 model
MViTv2-B IN21K, sup 0.475 0.090 8.9 55.6 48.1 325820315 model
MViTv2-L IN21K, sup 0.844 0.157 19.7 55.7 48.3 325607715 model
MViTv2-H IN21K, sup 1.655 0.285 18.4* 55.9 48.3 326187358 model
ViTDet, ViT-B IN1K, MAE 0.362 0.089 12.3 54.0 46.7 325358525 model
ViTDet, ViT-L IN1K, MAE 0.643 0.142 22.3 57.6 50.0 328021305 model
ViTDet, ViT-H IN1K, MAE 1.137 0.196 32.9 58.7 51.0 328730692 model
### LVIS #### Mask R-CNN
Name pre-train train
time
(s/im)
inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
model id download
ViTDet, ViT-B IN1K, MAE 0.317 0.085 14.4 40.2 38.2 329225748 model
ViTDet, ViT-L IN1K, MAE 0.576 0.137 24.7 46.1 43.6 329211570 model
ViTDet, ViT-H IN1K, MAE 1.059 0.186 35.3 49.1 46.0 332434656 model
#### Cascade Mask R-CNN
Name pre-train train
time
(s/im)
inference
time
(s/im)
train
mem
(GB)
box
AP
mask
AP
model id download
Swin-B IN21K, sup 0.368 0.090 11.5 44.0 39.6 329222304 model
Swin-L IN21K, sup 0.486 0.105 13.8 46.0 41.4 329222724 model
MViTv2-B IN21K, sup 0.475 0.100 11.8 46.3 42.0 329477206 model
MViTv2-L IN21K, sup 0.844 0.172 21.0 49.4 44.2 329661552 model
MViTv2-H IN21K, sup 1.661 0.290 21.3* 49.5 44.1 330445165 model
ViTDet, ViT-B IN1K, MAE 0.356 0.099 15.2 43.0 38.9 329226874 model
ViTDet, ViT-L IN1K, MAE 0.629 0.150 24.9 49.2 44.5 329042206 model
ViTDet, ViT-H IN1K, MAE 1.100 0.204 35.5 51.5 46.6 332552778 model
Note: Unlike the system-level comparisons in the paper, these models use a lower resolution (1024 instead of 1280) and standard NMS (instead of soft NMS). As a result, they have slightly lower box and mask AP. We observed higher variance on LVIS evalution results compared to COCO. For example, the standard deviations of box AP and mask AP were 0.30% (compared to 0.10% on COCO) when we trained ViTDet, ViT-B five times with varying random seeds. The above models were trained and measured on 8-node with 64 NVIDIA A100 GPUs in total. *: Activation checkpointing is used. ## Training All configs can be trained with: ``` ../../tools/lazyconfig_train_net.py --config-file configs/path/to/config.py ``` By default, we use 64 GPUs with batch size as 64 for training. ## Evaluation Model evaluation can be done similarly: ``` ../../tools/lazyconfig_train_net.py --config-file configs/path/to/config.py --eval-only train.init_checkpoint=/path/to/model_checkpoint ``` ## Citing ViTDet If you use ViTDet, please use the following BibTeX entry. ```BibTeX @article{li2022exploring, title={Exploring plain vision transformer backbones for object detection}, author={Li, Yanghao and Mao, Hanzi and Girshick, Ross and He, Kaiming}, journal={arXiv preprint arXiv:2203.16527}, year={2022} } ``` ================================================ FILE: detectron2/projects/ViTDet/configs/COCO/cascade_mask_rcnn_mvitv2_b_in21k_100ep.py ================================================ from functools import partial import torch.nn as nn from fvcore.common.param_scheduler import MultiStepParamScheduler from detectron2 import model_zoo from detectron2.config import LazyCall as L from detectron2.solver import WarmupParamScheduler from detectron2.modeling import MViT from detectron2.layers import ShapeSpec from detectron2.modeling.box_regression import Box2BoxTransform from detectron2.modeling.matcher import Matcher from detectron2.modeling.roi_heads import ( FastRCNNOutputLayers, FastRCNNConvFCHead, CascadeROIHeads, ) from ..common.coco_loader_lsj import dataloader model = model_zoo.get_config("common/models/mask_rcnn_fpn.py").model constants = model_zoo.get_config("common/data/constants.py").constants model.pixel_mean = constants.imagenet_rgb256_mean model.pixel_std = constants.imagenet_rgb256_std model.input_format = "RGB" model.backbone.bottom_up = L(MViT)( embed_dim=96, depth=24, num_heads=1, last_block_indexes=(1, 4, 20, 23), residual_pooling=True, drop_path_rate=0.4, norm_layer=partial(nn.LayerNorm, eps=1e-6), out_features=("scale2", "scale3", "scale4", "scale5"), ) model.backbone.in_features = "${.bottom_up.out_features}" model.backbone.square_pad = 1024 # New heads and LN model.backbone.norm = "LN" # Use LN in FPN model.roi_heads.box_head.conv_norm = model.roi_heads.mask_head.conv_norm = "LN" # 2conv in RPN: model.proposal_generator.head.conv_dims = [-1, -1] # arguments that don't exist for Cascade R-CNN [model.roi_heads.pop(k) for k in ["box_head", "box_predictor", "proposal_matcher"]] model.roi_heads.update( _target_=CascadeROIHeads, box_heads=[ L(FastRCNNConvFCHead)( input_shape=ShapeSpec(channels=256, height=7, width=7), conv_dims=[256, 256, 256, 256], fc_dims=[1024], conv_norm="LN", ) for _ in range(3) ], box_predictors=[ L(FastRCNNOutputLayers)( input_shape=ShapeSpec(channels=1024), test_score_thresh=0.05, box2box_transform=L(Box2BoxTransform)(weights=(w1, w1, w2, w2)), cls_agnostic_bbox_reg=True, num_classes="${...num_classes}", ) for (w1, w2) in [(10, 5), (20, 10), (30, 15)] ], proposal_matchers=[ L(Matcher)(thresholds=[th], labels=[0, 1], allow_low_quality_matches=False) for th in [0.5, 0.6, 0.7] ], ) # Initialization and trainer settings train = model_zoo.get_config("common/train.py").train train.amp.enabled = True train.ddp.fp16_compression = True train.init_checkpoint = "detectron2://ImageNetPretrained/mvitv2/MViTv2_B_in21k.pyth" # Schedule # 100 ep = 184375 iters * 64 images/iter / 118000 images/ep train.max_iter = 184375 lr_multiplier = L(WarmupParamScheduler)( scheduler=L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], milestones=[163889, 177546], num_updates=train.max_iter, ), warmup_length=250 / train.max_iter, warmup_factor=0.001, ) optimizer = model_zoo.get_config("common/optim.py").AdamW optimizer.params.overrides = {"pos_embed": {"weight_decay": 0.0}} optimizer.lr = 8e-5 ================================================ FILE: detectron2/projects/ViTDet/configs/COCO/cascade_mask_rcnn_mvitv2_h_in21k_36ep.py ================================================ from fvcore.common.param_scheduler import MultiStepParamScheduler from detectron2.config import LazyCall as L from detectron2.solver import WarmupParamScheduler from .cascade_mask_rcnn_mvitv2_b_in21k_100ep import ( dataloader, lr_multiplier, model, train, optimizer, ) model.backbone.bottom_up.embed_dim = 192 model.backbone.bottom_up.depth = 80 model.backbone.bottom_up.num_heads = 3 model.backbone.bottom_up.last_block_indexes = (3, 11, 71, 79) model.backbone.bottom_up.drop_path_rate = 0.6 model.backbone.bottom_up.use_act_checkpoint = True train.init_checkpoint = "detectron2://ImageNetPretrained/mvitv2/MViTv2_H_in21k.pyth" # 36 epochs train.max_iter = 67500 lr_multiplier = L(WarmupParamScheduler)( scheduler=L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], milestones=[ 52500, 62500, 67500, ], ), warmup_length=250 / train.max_iter, warmup_factor=0.001, ) optimizer.lr = 1.6e-4 ================================================ FILE: detectron2/projects/ViTDet/configs/COCO/cascade_mask_rcnn_mvitv2_l_in21k_50ep.py ================================================ from .cascade_mask_rcnn_mvitv2_b_in21k_100ep import ( dataloader, lr_multiplier, model, train, optimizer, ) model.backbone.bottom_up.embed_dim = 144 model.backbone.bottom_up.depth = 48 model.backbone.bottom_up.num_heads = 2 model.backbone.bottom_up.last_block_indexes = (1, 7, 43, 47) model.backbone.bottom_up.drop_path_rate = 0.5 train.init_checkpoint = "detectron2://ImageNetPretrained/mvitv2/MViTv2_L_in21k.pyth" train.max_iter = train.max_iter // 2 # 100ep -> 50ep lr_multiplier.scheduler.milestones = [ milestone // 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/projects/ViTDet/configs/COCO/cascade_mask_rcnn_swin_b_in21k_50ep.py ================================================ from fvcore.common.param_scheduler import MultiStepParamScheduler from detectron2 import model_zoo from detectron2.config import LazyCall as L from detectron2.solver import WarmupParamScheduler from detectron2.modeling import SwinTransformer from ..common.coco_loader_lsj import dataloader from .cascade_mask_rcnn_mvitv2_b_in21k_100ep import model model.backbone.bottom_up = L(SwinTransformer)( depths=[2, 2, 18, 2], drop_path_rate=0.4, embed_dim=128, num_heads=[4, 8, 16, 32], ) model.backbone.in_features = ("p0", "p1", "p2", "p3") model.backbone.square_pad = 1024 # Initialization and trainer settings train = model_zoo.get_config("common/train.py").train train.amp.enabled = True train.ddp.fp16_compression = True train.init_checkpoint = "detectron2://ImageNetPretrained/swin/swin_base_patch4_window7_224_22k.pth" # Schedule # 100 ep = 184375 iters * 64 images/iter / 118000 images/ep train.max_iter = 184375 lr_multiplier = L(WarmupParamScheduler)( scheduler=L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], milestones=[163889, 177546], num_updates=train.max_iter, ), warmup_length=250 / train.max_iter, warmup_factor=0.001, ) # Rescale schedule train.max_iter = train.max_iter // 2 # 100ep -> 50ep lr_multiplier.scheduler.milestones = [ milestone // 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter optimizer = model_zoo.get_config("common/optim.py").AdamW optimizer.lr = 4e-5 optimizer.weight_decay = 0.05 optimizer.params.overrides = {"relative_position_bias_table": {"weight_decay": 0.0}} ================================================ FILE: detectron2/projects/ViTDet/configs/COCO/cascade_mask_rcnn_swin_l_in21k_50ep.py ================================================ from .cascade_mask_rcnn_swin_b_in21k_50ep import ( dataloader, lr_multiplier, model, train, optimizer, ) model.backbone.bottom_up.depths = [2, 2, 18, 2] model.backbone.bottom_up.drop_path_rate = 0.4 model.backbone.bottom_up.embed_dim = 192 model.backbone.bottom_up.num_heads = [6, 12, 24, 48] train.init_checkpoint = "detectron2://ImageNetPretrained/swin/swin_large_patch4_window7_224_22k.pth" ================================================ FILE: detectron2/projects/ViTDet/configs/COCO/cascade_mask_rcnn_vitdet_b_100ep.py ================================================ from detectron2.config import LazyCall as L from detectron2.layers import ShapeSpec from detectron2.modeling.box_regression import Box2BoxTransform from detectron2.modeling.matcher import Matcher from detectron2.modeling.roi_heads import ( FastRCNNOutputLayers, FastRCNNConvFCHead, CascadeROIHeads, ) from .mask_rcnn_vitdet_b_100ep import ( dataloader, lr_multiplier, model, train, optimizer, get_vit_lr_decay_rate, ) # arguments that don't exist for Cascade R-CNN [model.roi_heads.pop(k) for k in ["box_head", "box_predictor", "proposal_matcher"]] model.roi_heads.update( _target_=CascadeROIHeads, box_heads=[ L(FastRCNNConvFCHead)( input_shape=ShapeSpec(channels=256, height=7, width=7), conv_dims=[256, 256, 256, 256], fc_dims=[1024], conv_norm="LN", ) for _ in range(3) ], box_predictors=[ L(FastRCNNOutputLayers)( input_shape=ShapeSpec(channels=1024), test_score_thresh=0.05, box2box_transform=L(Box2BoxTransform)(weights=(w1, w1, w2, w2)), cls_agnostic_bbox_reg=True, num_classes="${...num_classes}", ) for (w1, w2) in [(10, 5), (20, 10), (30, 15)] ], proposal_matchers=[ L(Matcher)(thresholds=[th], labels=[0, 1], allow_low_quality_matches=False) for th in [0.5, 0.6, 0.7] ], ) ================================================ FILE: detectron2/projects/ViTDet/configs/COCO/cascade_mask_rcnn_vitdet_h_75ep.py ================================================ from functools import partial from .cascade_mask_rcnn_vitdet_b_100ep import ( dataloader, lr_multiplier, model, train, optimizer, get_vit_lr_decay_rate, ) train.init_checkpoint = "detectron2://ImageNetPretrained/MAE/mae_pretrain_vit_huge_p14to16.pth" model.backbone.net.embed_dim = 1280 model.backbone.net.depth = 32 model.backbone.net.num_heads = 16 model.backbone.net.drop_path_rate = 0.5 # 7, 15, 23, 31 for global attention model.backbone.net.window_block_indexes = ( list(range(0, 7)) + list(range(8, 15)) + list(range(16, 23)) + list(range(24, 31)) ) optimizer.params.lr_factor_func = partial(get_vit_lr_decay_rate, lr_decay_rate=0.9, num_layers=32) optimizer.params.overrides = {} optimizer.params.weight_decay_norm = None train.max_iter = train.max_iter * 3 // 4 # 100ep -> 75ep lr_multiplier.scheduler.milestones = [ milestone * 3 // 4 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/projects/ViTDet/configs/COCO/cascade_mask_rcnn_vitdet_l_100ep.py ================================================ from functools import partial from .cascade_mask_rcnn_vitdet_b_100ep import ( dataloader, lr_multiplier, model, train, optimizer, get_vit_lr_decay_rate, ) train.init_checkpoint = "detectron2://ImageNetPretrained/MAE/mae_pretrain_vit_large.pth" model.backbone.net.embed_dim = 1024 model.backbone.net.depth = 24 model.backbone.net.num_heads = 16 model.backbone.net.drop_path_rate = 0.4 # 5, 11, 17, 23 for global attention model.backbone.net.window_block_indexes = ( list(range(0, 5)) + list(range(6, 11)) + list(range(12, 17)) + list(range(18, 23)) ) optimizer.params.lr_factor_func = partial(get_vit_lr_decay_rate, lr_decay_rate=0.8, num_layers=24) ================================================ FILE: detectron2/projects/ViTDet/configs/COCO/mask_rcnn_vitdet_b_100ep.py ================================================ from functools import partial from fvcore.common.param_scheduler import MultiStepParamScheduler from detectron2 import model_zoo from detectron2.config import LazyCall as L from detectron2.solver import WarmupParamScheduler from detectron2.modeling.backbone.vit import get_vit_lr_decay_rate from ..common.coco_loader_lsj import dataloader model = model_zoo.get_config("common/models/mask_rcnn_vitdet.py").model # Initialization and trainer settings train = model_zoo.get_config("common/train.py").train train.amp.enabled = True train.ddp.fp16_compression = True train.init_checkpoint = "detectron2://ImageNetPretrained/MAE/mae_pretrain_vit_base.pth" # Schedule # 100 ep = 184375 iters * 64 images/iter / 118000 images/ep train.max_iter = 184375 lr_multiplier = L(WarmupParamScheduler)( scheduler=L(MultiStepParamScheduler)( values=[1.0, 0.1, 0.01], milestones=[163889, 177546], num_updates=train.max_iter, ), warmup_length=250 / train.max_iter, warmup_factor=0.001, ) # Optimizer optimizer = model_zoo.get_config("common/optim.py").AdamW optimizer.params.lr_factor_func = partial(get_vit_lr_decay_rate, num_layers=12, lr_decay_rate=0.7) optimizer.params.overrides = {"pos_embed": {"weight_decay": 0.0}} ================================================ FILE: detectron2/projects/ViTDet/configs/COCO/mask_rcnn_vitdet_h_75ep.py ================================================ from functools import partial from .mask_rcnn_vitdet_b_100ep import ( dataloader, lr_multiplier, model, train, optimizer, get_vit_lr_decay_rate, ) train.init_checkpoint = "detectron2://ImageNetPretrained/MAE/mae_pretrain_vit_huge_p14to16.pth" model.backbone.net.embed_dim = 1280 model.backbone.net.depth = 32 model.backbone.net.num_heads = 16 model.backbone.net.drop_path_rate = 0.5 # 7, 15, 23, 31 for global attention model.backbone.net.window_block_indexes = ( list(range(0, 7)) + list(range(8, 15)) + list(range(16, 23)) + list(range(24, 31)) ) optimizer.params.lr_factor_func = partial(get_vit_lr_decay_rate, lr_decay_rate=0.9, num_layers=32) optimizer.params.overrides = {} optimizer.params.weight_decay_norm = None train.max_iter = train.max_iter * 3 // 4 # 100ep -> 75ep lr_multiplier.scheduler.milestones = [ milestone * 3 // 4 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter ================================================ FILE: detectron2/projects/ViTDet/configs/COCO/mask_rcnn_vitdet_l_100ep.py ================================================ from functools import partial from .mask_rcnn_vitdet_b_100ep import ( dataloader, lr_multiplier, model, train, optimizer, get_vit_lr_decay_rate, ) train.init_checkpoint = "detectron2://ImageNetPretrained/MAE/mae_pretrain_vit_large.pth" model.backbone.net.embed_dim = 1024 model.backbone.net.depth = 24 model.backbone.net.num_heads = 16 model.backbone.net.drop_path_rate = 0.4 # 5, 11, 17, 23 for global attention model.backbone.net.window_block_indexes = ( list(range(0, 5)) + list(range(6, 11)) + list(range(12, 17)) + list(range(18, 23)) ) optimizer.params.lr_factor_func = partial(get_vit_lr_decay_rate, lr_decay_rate=0.8, num_layers=24) ================================================ FILE: detectron2/projects/ViTDet/configs/LVIS/cascade_mask_rcnn_mvitv2_b_in21k_100ep.py ================================================ from functools import partial import torch.nn as nn from detectron2.config import LazyCall as L from detectron2.data.detection_utils import get_fed_loss_cls_weights from detectron2.data.samplers import RepeatFactorTrainingSampler from detectron2.evaluation.lvis_evaluation import LVISEvaluator from ..COCO.cascade_mask_rcnn_mvitv2_b_in21k_100ep import ( dataloader, model, train, lr_multiplier, optimizer, ) dataloader.train.dataset.names = "lvis_v1_train" dataloader.train.sampler = L(RepeatFactorTrainingSampler)( repeat_factors=L(RepeatFactorTrainingSampler.repeat_factors_from_category_frequency)( dataset_dicts="${dataloader.train.dataset}", repeat_thresh=0.001 ) ) dataloader.test.dataset.names = "lvis_v1_val" dataloader.evaluator = L(LVISEvaluator)( dataset_name="${..test.dataset.names}", max_dets_per_image=300, ) model.roi_heads.num_classes = 1203 for i in range(3): model.roi_heads.box_predictors[i].test_score_thresh = 0.02 model.roi_heads.box_predictors[i].test_topk_per_image = 300 model.roi_heads.box_predictors[i].use_sigmoid_ce = True model.roi_heads.box_predictors[i].use_fed_loss = True model.roi_heads.box_predictors[i].get_fed_loss_cls_weights = lambda: get_fed_loss_cls_weights( dataloader.train.dataset.names, 0.5 ) # Schedule # 100 ep = 156250 iters * 64 images/iter / 100000 images/ep train.max_iter = 156250 train.eval_period = 30000 lr_multiplier.scheduler.milestones = [138889, 150463] lr_multiplier.scheduler.num_updates = train.max_iter lr_multiplier.warmup_length = 250 / train.max_iter optimizer.lr = 1e-4 ================================================ FILE: detectron2/projects/ViTDet/configs/LVIS/cascade_mask_rcnn_mvitv2_h_in21k_50ep.py ================================================ from .cascade_mask_rcnn_mvitv2_b_in21k_100ep import ( dataloader, lr_multiplier, model, train, optimizer, ) model.backbone.bottom_up.embed_dim = 192 model.backbone.bottom_up.depth = 80 model.backbone.bottom_up.num_heads = 3 model.backbone.bottom_up.last_block_indexes = (3, 11, 71, 79) model.backbone.bottom_up.drop_path_rate = 0.6 model.backbone.bottom_up.use_act_checkpoint = True train.init_checkpoint = "detectron2://ImageNetPretrained/mvitv2/MViTv2_H_in21k.pyth" train.max_iter = train.max_iter // 2 # 100ep -> 50ep lr_multiplier.scheduler.milestones = [ milestone // 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter lr_multiplier.warmup_length = 250 / train.max_iter optimizer.lr = 2e-5 ================================================ FILE: detectron2/projects/ViTDet/configs/LVIS/cascade_mask_rcnn_mvitv2_l_in21k_50ep.py ================================================ from .cascade_mask_rcnn_mvitv2_b_in21k_100ep import ( dataloader, lr_multiplier, model, train, optimizer, ) model.backbone.bottom_up.embed_dim = 144 model.backbone.bottom_up.depth = 48 model.backbone.bottom_up.num_heads = 2 model.backbone.bottom_up.last_block_indexes = (1, 7, 43, 47) model.backbone.bottom_up.drop_path_rate = 0.5 train.init_checkpoint = "detectron2://ImageNetPretrained/mvitv2/MViTv2_L_in21k.pyth" train.max_iter = train.max_iter // 2 # 100ep -> 50ep lr_multiplier.scheduler.milestones = [ milestone // 2 for milestone in lr_multiplier.scheduler.milestones ] lr_multiplier.scheduler.num_updates = train.max_iter lr_multiplier.warmup_length = 250 / train.max_iter optimizer.lr = 4e-5 ================================================ FILE: detectron2/projects/ViTDet/configs/LVIS/cascade_mask_rcnn_swin_b_in21k_50ep.py ================================================ from detectron2.config.lazy import LazyCall as L from detectron2.data.detection_utils import get_fed_loss_cls_weights from detectron2.data.samplers import RepeatFactorTrainingSampler from detectron2.evaluation.lvis_evaluation import LVISEvaluator from ..COCO.cascade_mask_rcnn_swin_b_in21k_50ep import ( dataloader, model, train, lr_multiplier, optimizer, ) dataloader.train.dataset.names = "lvis_v1_train" dataloader.train.sampler = L(RepeatFactorTrainingSampler)( repeat_factors=L(RepeatFactorTrainingSampler.repeat_factors_from_category_frequency)( dataset_dicts="${dataloader.train.dataset}", repeat_thresh=0.001 ) ) dataloader.test.dataset.names = "lvis_v1_val" dataloader.evaluator = L(LVISEvaluator)( dataset_name="${..test.dataset.names}", max_dets_per_image=300, ) model.backbone.bottom_up.drop_path_rate = 0.3 model.roi_heads.num_classes = 1203 for i in range(3): model.roi_heads.box_predictors[i].test_score_thresh = 0.02 model.roi_heads.box_predictors[i].test_topk_per_image = 300 model.roi_heads.box_predictors[i].use_sigmoid_ce = True model.roi_heads.box_predictors[i].use_fed_loss = True model.roi_heads.box_predictors[i].get_fed_loss_cls_weights = lambda: get_fed_loss_cls_weights( dataloader.train.dataset.names, 0.5 ) # Schedule # 100 ep = 156250 iters * 64 images/iter / 100000 images/ep # 100 ep -> 50 ep as the model achieves better performance with 50 epochs train.max_iter = 156250 // 2 train.eval_period = 30000 lr_multiplier.scheduler.milestones = [milestone // 2 for milestone in [138889, 150463]] lr_multiplier.scheduler.num_updates = train.max_iter lr_multiplier.warmup_length = 250 / train.max_iter # Optimized hyperparams optimizer.lr = 1e-4 ================================================ FILE: detectron2/projects/ViTDet/configs/LVIS/cascade_mask_rcnn_swin_l_in21k_50ep.py ================================================ from .cascade_mask_rcnn_swin_b_in21k_50ep import ( dataloader, lr_multiplier, model, train, optimizer, ) model.backbone.bottom_up.embed_dim = 192 model.backbone.bottom_up.num_heads = [6, 12, 24, 48] train.init_checkpoint = "detectron2://ImageNetPretrained/swin/swin_large_patch4_window7_224_22k.pth" ================================================ FILE: detectron2/projects/ViTDet/configs/LVIS/cascade_mask_rcnn_vitdet_b_100ep.py ================================================ from detectron2.config import LazyCall as L from detectron2.data.detection_utils import get_fed_loss_cls_weights from detectron2.layers import ShapeSpec from detectron2.modeling.box_regression import Box2BoxTransform from detectron2.modeling.matcher import Matcher from detectron2.modeling.roi_heads import FastRCNNOutputLayers, FastRCNNConvFCHead, CascadeROIHeads from .mask_rcnn_vitdet_b_100ep import ( dataloader, lr_multiplier, model, optimizer, train, ) # arguments that don't exist for Cascade R-CNN [model.roi_heads.pop(k) for k in ["box_head", "box_predictor", "proposal_matcher"]] model.roi_heads.update( _target_=CascadeROIHeads, num_classes=1203, box_heads=[ L(FastRCNNConvFCHead)( input_shape=ShapeSpec(channels=256, height=7, width=7), conv_dims=[256, 256, 256, 256], fc_dims=[1024], conv_norm="LN", ) for _ in range(3) ], box_predictors=[ L(FastRCNNOutputLayers)( input_shape=ShapeSpec(channels=1024), box2box_transform=L(Box2BoxTransform)(weights=(w1, w1, w2, w2)), num_classes="${...num_classes}", test_score_thresh=0.02, test_topk_per_image=300, cls_agnostic_bbox_reg=True, use_sigmoid_ce=True, use_fed_loss=True, get_fed_loss_cls_weights=lambda: get_fed_loss_cls_weights( dataloader.train.dataset.names, 0.5 ), ) for (w1, w2) in [(10, 5), (20, 10), (30, 15)] ], proposal_matchers=[ L(Matcher)(thresholds=[th], labels=[0, 1], allow_low_quality_matches=False) for th in [0.5, 0.6, 0.7] ], ) ================================================ FILE: detectron2/projects/ViTDet/configs/LVIS/cascade_mask_rcnn_vitdet_h_100ep.py ================================================ from detectron2.config import LazyCall as L from detectron2.data.detection_utils import get_fed_loss_cls_weights from detectron2.layers import ShapeSpec from detectron2.modeling.box_regression import Box2BoxTransform from detectron2.modeling.matcher import Matcher from detectron2.modeling.roi_heads import FastRCNNOutputLayers, FastRCNNConvFCHead, CascadeROIHeads from .mask_rcnn_vitdet_h_100ep import ( dataloader, lr_multiplier, model, optimizer, train, ) # arguments that don't exist for Cascade R-CNN [model.roi_heads.pop(k) for k in ["box_head", "box_predictor", "proposal_matcher"]] model.roi_heads.update( _target_=CascadeROIHeads, num_classes=1203, box_heads=[ L(FastRCNNConvFCHead)( input_shape=ShapeSpec(channels=256, height=7, width=7), conv_dims=[256, 256, 256, 256], fc_dims=[1024], conv_norm="LN", ) for _ in range(3) ], box_predictors=[ L(FastRCNNOutputLayers)( input_shape=ShapeSpec(channels=1024), box2box_transform=L(Box2BoxTransform)(weights=(w1, w1, w2, w2)), num_classes="${...num_classes}", test_score_thresh=0.02, test_topk_per_image=300, cls_agnostic_bbox_reg=True, use_sigmoid_ce=True, use_fed_loss=True, get_fed_loss_cls_weights=lambda: get_fed_loss_cls_weights( dataloader.train.dataset.names, 0.5 ), ) for (w1, w2) in [(10, 5), (20, 10), (30, 15)] ], proposal_matchers=[ L(Matcher)(thresholds=[th], labels=[0, 1], allow_low_quality_matches=False) for th in [0.5, 0.6, 0.7] ], ) ================================================ FILE: detectron2/projects/ViTDet/configs/LVIS/cascade_mask_rcnn_vitdet_l_100ep.py ================================================ from detectron2.config import LazyCall as L from detectron2.data.detection_utils import get_fed_loss_cls_weights from detectron2.layers import ShapeSpec from detectron2.modeling.box_regression import Box2BoxTransform from detectron2.modeling.matcher import Matcher from detectron2.modeling.roi_heads import FastRCNNOutputLayers, FastRCNNConvFCHead, CascadeROIHeads from .mask_rcnn_vitdet_l_100ep import ( dataloader, lr_multiplier, model, optimizer, train, ) # arguments that don't exist for Cascade R-CNN [model.roi_heads.pop(k) for k in ["box_head", "box_predictor", "proposal_matcher"]] model.roi_heads.update( _target_=CascadeROIHeads, num_classes=1203, box_heads=[ L(FastRCNNConvFCHead)( input_shape=ShapeSpec(channels=256, height=7, width=7), conv_dims=[256, 256, 256, 256], fc_dims=[1024], conv_norm="LN", ) for _ in range(3) ], box_predictors=[ L(FastRCNNOutputLayers)( input_shape=ShapeSpec(channels=1024), box2box_transform=L(Box2BoxTransform)(weights=(w1, w1, w2, w2)), num_classes="${...num_classes}", test_score_thresh=0.02, test_topk_per_image=300, cls_agnostic_bbox_reg=True, use_sigmoid_ce=True, use_fed_loss=True, get_fed_loss_cls_weights=lambda: get_fed_loss_cls_weights( dataloader.train.dataset.names, 0.5 ), ) for (w1, w2) in [(10, 5), (20, 10), (30, 15)] ], proposal_matchers=[ L(Matcher)(thresholds=[th], labels=[0, 1], allow_low_quality_matches=False) for th in [0.5, 0.6, 0.7] ], ) ================================================ FILE: detectron2/projects/ViTDet/configs/LVIS/mask_rcnn_vitdet_b_100ep.py ================================================ from detectron2.config import LazyCall as L from detectron2.data.samplers import RepeatFactorTrainingSampler from detectron2.evaluation.lvis_evaluation import LVISEvaluator from detectron2.data.detection_utils import get_fed_loss_cls_weights from ..COCO.mask_rcnn_vitdet_b_100ep import ( dataloader, model, train, lr_multiplier, optimizer, ) dataloader.train.dataset.names = "lvis_v1_train" dataloader.train.sampler = L(RepeatFactorTrainingSampler)( repeat_factors=L(RepeatFactorTrainingSampler.repeat_factors_from_category_frequency)( dataset_dicts="${dataloader.train.dataset}", repeat_thresh=0.001 ) ) dataloader.test.dataset.names = "lvis_v1_val" dataloader.evaluator = L(LVISEvaluator)( dataset_name="${..test.dataset.names}", max_dets_per_image=300, ) model.roi_heads.num_classes = 1203 model.roi_heads.box_predictor.test_score_thresh = 0.02 model.roi_heads.box_predictor.test_topk_per_image = 300 model.roi_heads.box_predictor.use_sigmoid_ce = True model.roi_heads.box_predictor.use_fed_loss = True model.roi_heads.box_predictor.get_fed_loss_cls_weights = lambda: get_fed_loss_cls_weights( dataloader.train.dataset.names, 0.5 ) # Schedule # 100 ep = 156250 iters * 64 images/iter / 100000 images/ep train.max_iter = 156250 train.eval_period = 30000 lr_multiplier.scheduler.milestones = [138889, 150463] lr_multiplier.scheduler.num_updates = train.max_iter lr_multiplier.warmup_length = 250 / train.max_iter optimizer.lr = 2e-4 ================================================ FILE: detectron2/projects/ViTDet/configs/LVIS/mask_rcnn_vitdet_h_100ep.py ================================================ from functools import partial from detectron2.modeling.backbone.vit import get_vit_lr_decay_rate from .mask_rcnn_vitdet_b_100ep import ( dataloader, lr_multiplier, model, train, optimizer, ) train.init_checkpoint = "detectron2://ImageNetPretrained/MAE/mae_pretrain_vit_huge_p14to16.pth" model.backbone.net.embed_dim = 1280 model.backbone.net.depth = 32 model.backbone.net.num_heads = 16 model.backbone.net.drop_path_rate = 0.4 # 7, 15, 23, 31 for global attention model.backbone.net.window_block_indexes = ( list(range(0, 7)) + list(range(8, 15)) + list(range(16, 23)) + list(range(24, 31)) ) optimizer.lr = 1e-4 optimizer.params.lr_factor_func = partial(get_vit_lr_decay_rate, lr_decay_rate=0.9, num_layers=32) optimizer.params.overrides = {} optimizer.params.weight_decay_norm = None ================================================ FILE: detectron2/projects/ViTDet/configs/LVIS/mask_rcnn_vitdet_l_100ep.py ================================================ from functools import partial from detectron2.modeling.backbone.vit import get_vit_lr_decay_rate from .mask_rcnn_vitdet_b_100ep import ( dataloader, lr_multiplier, model, train, optimizer, ) train.init_checkpoint = "detectron2://ImageNetPretrained/MAE/mae_pretrain_vit_large.pth" model.backbone.net.embed_dim = 1024 model.backbone.net.depth = 24 model.backbone.net.num_heads = 16 model.backbone.net.drop_path_rate = 0.4 # 5, 11, 17, 23 for global attention model.backbone.net.window_block_indexes = ( list(range(0, 5)) + list(range(6, 11)) + list(range(12, 17)) + list(range(18, 23)) ) optimizer.params.lr_factor_func = partial(get_vit_lr_decay_rate, lr_decay_rate=0.8, num_layers=24) ================================================ FILE: detectron2/projects/ViTDet/configs/common/coco_loader_lsj.py ================================================ import detectron2.data.transforms as T from detectron2 import model_zoo from detectron2.config import LazyCall as L # Data using LSJ image_size = 1024 dataloader = model_zoo.get_config("common/data/coco.py").dataloader dataloader.train.mapper.augmentations = [ L(T.RandomFlip)(horizontal=True), # flip first L(T.ResizeScale)( min_scale=0.1, max_scale=2.0, target_height=image_size, target_width=image_size ), L(T.FixedSizeCrop)(crop_size=(image_size, image_size), pad=False), ] dataloader.train.mapper.image_format = "RGB" dataloader.train.total_batch_size = 64 # recompute boxes due to cropping dataloader.train.mapper.recompute_boxes = True dataloader.test.mapper.augmentations = [ L(T.ResizeShortestEdge)(short_edge_length=image_size, max_size=image_size), ] ================================================ FILE: detectron2/setup.cfg ================================================ [isort] line_length=100 multi_line_output=3 include_trailing_comma=True known_standard_library=numpy,setuptools,mock skip=./datasets,docs skip_glob=*/__init__.py,**/configs/**,**/tests/config/** known_myself=detectron2 known_third_party=fvcore,matplotlib,cv2,torch,torchvision,PIL,pycocotools,yacs,termcolor,cityscapesscripts,tabulate,tqdm,scipy,lvis,psutil,pkg_resources,caffe2,onnx,panopticapi,black,isort,av,iopath,omegaconf,hydra,yaml,pydoc,submitit,cloudpickle,packaging no_lines_before=STDLIB,THIRDPARTY sections=FUTURE,STDLIB,THIRDPARTY,myself,FIRSTPARTY,LOCALFOLDER default_section=FIRSTPARTY [mypy] python_version=3.7 ignore_missing_imports = True warn_unused_configs = True disallow_untyped_defs = True check_untyped_defs = True warn_unused_ignores = True warn_redundant_casts = True show_column_numbers = True follow_imports = silent allow_redefinition = True ; Require all functions to be annotated disallow_incomplete_defs = True ================================================ FILE: detectron2/setup.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. import glob import os import shutil from os import path from setuptools import find_packages, setup from typing import List import torch from torch.utils.cpp_extension import CUDA_HOME, CppExtension, CUDAExtension torch_ver = [int(x) for x in torch.__version__.split(".")[:2]] assert torch_ver >= [1, 8], "Requires PyTorch >= 1.8" def get_version(): init_py_path = path.join(path.abspath(path.dirname(__file__)), "detectron2", "__init__.py") init_py = open(init_py_path, "r").readlines() version_line = [l.strip() for l in init_py if l.startswith("__version__")][0] version = version_line.split("=")[-1].strip().strip("'\"") # The following is used to build release packages. # Users should never use it. suffix = os.getenv("D2_VERSION_SUFFIX", "") version = version + suffix if os.getenv("BUILD_NIGHTLY", "0") == "1": from datetime import datetime date_str = datetime.today().strftime("%y%m%d") version = version + ".dev" + date_str new_init_py = [l for l in init_py if not l.startswith("__version__")] new_init_py.append('__version__ = "{}"\n'.format(version)) with open(init_py_path, "w") as f: f.write("".join(new_init_py)) return version def get_extensions(): this_dir = path.dirname(path.abspath(__file__)) extensions_dir = path.join(this_dir, "detectron2", "layers", "csrc") main_source = path.join(extensions_dir, "vision.cpp") sources = glob.glob(path.join(extensions_dir, "**", "*.cpp")) from torch.utils.cpp_extension import ROCM_HOME is_rocm_pytorch = ( True if ((torch.version.hip is not None) and (ROCM_HOME is not None)) else False ) if is_rocm_pytorch: assert torch_ver >= [1, 8], "ROCM support requires PyTorch >= 1.8!" # common code between cuda and rocm platforms, for hipify version [1,0,0] and later. source_cuda = glob.glob(path.join(extensions_dir, "**", "*.cu")) + glob.glob( path.join(extensions_dir, "*.cu") ) sources = [main_source] + sources extension = CppExtension extra_compile_args = {"cxx": []} define_macros = [] if (torch.cuda.is_available() and ((CUDA_HOME is not None) or is_rocm_pytorch)) or os.getenv( "FORCE_CUDA", "0" ) == "1": extension = CUDAExtension sources += source_cuda if not is_rocm_pytorch: define_macros += [("WITH_CUDA", None)] extra_compile_args["nvcc"] = [ "-O3", "-DCUDA_HAS_FP16=1", "-D__CUDA_NO_HALF_OPERATORS__", "-D__CUDA_NO_HALF_CONVERSIONS__", "-D__CUDA_NO_HALF2_OPERATORS__", ] else: define_macros += [("WITH_HIP", None)] extra_compile_args["nvcc"] = [] if torch_ver < [1, 7]: # supported by https://github.com/pytorch/pytorch/pull/43931 CC = os.environ.get("CC", None) if CC is not None: extra_compile_args["nvcc"].append("-ccbin={}".format(CC)) include_dirs = [extensions_dir] ext_modules = [ extension( "detectron2._C", sources, include_dirs=include_dirs, define_macros=define_macros, extra_compile_args=extra_compile_args, ) ] return ext_modules def get_model_zoo_configs() -> List[str]: """ Return a list of configs to include in package for model zoo. Copy over these configs inside detectron2/model_zoo. """ # Use absolute paths while symlinking. source_configs_dir = path.join(path.dirname(path.realpath(__file__)), "configs") destination = path.join( path.dirname(path.realpath(__file__)), "detectron2", "model_zoo", "configs" ) # Symlink the config directory inside package to have a cleaner pip install. # Remove stale symlink/directory from a previous build. if path.exists(source_configs_dir): if path.islink(destination): os.unlink(destination) elif path.isdir(destination): shutil.rmtree(destination) if not path.exists(destination): try: os.symlink(source_configs_dir, destination) except OSError: # Fall back to copying if symlink fails: ex. on Windows. shutil.copytree(source_configs_dir, destination) config_paths = glob.glob("configs/**/*.yaml", recursive=True) + glob.glob( "configs/**/*.py", recursive=True ) return config_paths # For projects that are relative small and provide features that are very close # to detectron2's core functionalities, we install them under detectron2.projects PROJECTS = { "detectron2.projects.point_rend": "projects/PointRend/point_rend", "detectron2.projects.deeplab": "projects/DeepLab/deeplab", "detectron2.projects.panoptic_deeplab": "projects/Panoptic-DeepLab/panoptic_deeplab", } setup( name="detectron2", version=get_version(), author="FAIR", url="https://github.com/facebookresearch/detectron2", description="Detectron2 is FAIR's next-generation research " "platform for object detection and segmentation.", packages=find_packages(exclude=("configs", "tests*")) + list(PROJECTS.keys()), package_dir=PROJECTS, package_data={"detectron2.model_zoo": get_model_zoo_configs()}, python_requires=">=3.7", install_requires=[ # These dependencies are not pure-python. # In general, avoid adding more dependencies like them because they are not # guaranteed to be installable by `pip install` on all platforms. # To tell if a package is pure-python, go to https://pypi.org/project/{name}/#files "Pillow>=7.1", # or use pillow-simd for better performance "matplotlib", # TODO move it to optional after we add opencv visualization "pycocotools>=2.0.2", # corresponds to https://github.com/ppwwyyxx/cocoapi # Do not add opencv here. Just like pytorch, user should install # opencv themselves, preferrably by OS's package manager, or by # choosing the proper pypi package name at https://github.com/skvark/opencv-python # The following are pure-python dependencies that should be easily installable "termcolor>=1.1", "yacs>=0.1.8", "tabulate", "cloudpickle", "tqdm>4.29.0", "tensorboard", # Lock version of fvcore/iopath because they may have breaking changes # NOTE: when updating fvcore/iopath version, make sure fvcore depends # on compatible version of iopath. "fvcore>=0.1.5,<0.1.6", # required like this to make it pip installable "iopath>=0.1.7,<0.1.10", "future", # used by caffe2 "pydot", # used to save caffe2 SVGs "dataclasses; python_version<'3.7'", "omegaconf>=2.1", "hydra-core>=1.1", "black==22.3.0", "timm", "fairscale", "packaging", # If a new dependency is required at import time (in addition to runtime), it # probably needs to exist in docs/requirements.txt, or as a mock in docs/conf.py ], extras_require={ # optional dependencies, required by some features "all": [ "scipy>1.5.1", "shapely", "pygments>=2.2", "psutil", "panopticapi @ https://github.com/cocodataset/panopticapi/archive/master.zip", ], # dev dependencies. Install them by `pip install 'detectron2[dev]'` "dev": [ "flake8==3.8.1", "isort==4.3.21", "flake8-bugbear", "flake8-comprehensions", ], }, ext_modules=get_extensions(), cmdclass={"build_ext": torch.utils.cpp_extension.BuildExtension}, ) ================================================ FILE: detectron2/tests/README.md ================================================ ## Unit Tests To run the unittests, do: ``` cd detectron2 python -m unittest discover -v -s ./tests ``` There are also end-to-end inference & training tests, in [dev/run_*_tests.sh](../dev). ================================================ FILE: detectron2/tests/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. ================================================ FILE: detectron2/tests/config/dir1/dir1_a.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. dir1a_str = "base_a_1" dir1a_dict = {"a": 1, "b": 2} ================================================ FILE: detectron2/tests/config/dir1/dir1_b.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.config import LazyConfig # equivalent to relative import dir1a_str, dir1a_dict = LazyConfig.load_rel("dir1_a.py", ("dir1a_str", "dir1a_dict")) dir1b_str = dir1a_str + "_from_b" dir1b_dict = dir1a_dict # Every import is a reload: not modified by other config files assert dir1a_dict.a == 1 ================================================ FILE: detectron2/tests/config/root_cfg.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from itertools import count from detectron2.config import LazyCall as L from .dir1.dir1_a import dir1a_dict, dir1a_str dir1a_dict.a = "modified" # modification above won't affect future imports from .dir1.dir1_b import dir1b_dict, dir1b_str lazyobj = L(count)(x=dir1a_str, y=dir1b_str) ================================================ FILE: detectron2/tests/config/test_instantiate_config.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import os import tempfile import unittest import yaml from omegaconf import OmegaConf from omegaconf import __version__ as oc_version from dataclasses import dataclass from detectron2.config import LazyConfig, instantiate, LazyCall as L from detectron2.layers import ShapeSpec from detectron2.utils.testing import reload_lazy_config OC_VERSION = tuple(int(x) for x in oc_version.split(".")[:2]) class TestClass: def __init__(self, int_arg, list_arg=None, dict_arg=None, extra_arg=None): self.int_arg = int_arg self.list_arg = list_arg self.dict_arg = dict_arg self.extra_arg = extra_arg def __call__(self, call_arg): return call_arg + self.int_arg @unittest.skipIf(OC_VERSION < (2, 1), "omegaconf version too old") class TestConstruction(unittest.TestCase): def test_basic_construct(self): cfg = L(TestClass)( int_arg=3, list_arg=[10], dict_arg={}, extra_arg=L(TestClass)(int_arg=4, list_arg="${..list_arg}"), ) for x in [cfg, reload_lazy_config(cfg)]: obj = instantiate(x) self.assertIsInstance(obj, TestClass) self.assertEqual(obj.int_arg, 3) self.assertEqual(obj.extra_arg.int_arg, 4) self.assertEqual(obj.extra_arg.list_arg, obj.list_arg) # Test interpolation x.extra_arg.list_arg = [5] obj = instantiate(x) self.assertIsInstance(obj, TestClass) self.assertEqual(obj.extra_arg.list_arg, [5]) def test_instantiate_other_obj(self): # do nothing for other obj self.assertEqual(instantiate(5), 5) x = [3, 4, 5] self.assertEqual(instantiate(x), x) x = TestClass(1) self.assertIs(instantiate(x), x) x = {"xx": "yy"} self.assertIs(instantiate(x), x) def test_instantiate_lazy_target(self): # _target_ is result of instantiate objconf = L(L(len)(int_arg=3))(call_arg=4) objconf._target_._target_ = TestClass self.assertEqual(instantiate(objconf), 7) def test_instantiate_list(self): lst = [1, 2, L(TestClass)(int_arg=1)] x = L(TestClass)(int_arg=lst) # list as an argument should be recursively instantiated x = instantiate(x).int_arg self.assertEqual(x[:2], [1, 2]) self.assertIsInstance(x[2], TestClass) self.assertEqual(x[2].int_arg, 1) def test_instantiate_dataclass(self): cfg = L(ShapeSpec)(channels=1, width=3) # Test original cfg as well as serialization for x in [cfg, reload_lazy_config(cfg)]: obj = instantiate(x) self.assertIsInstance(obj, ShapeSpec) self.assertEqual(obj.channels, 1) self.assertEqual(obj.height, None) def test_instantiate_dataclass_as_subconfig(self): cfg = L(TestClass)(int_arg=1, extra_arg=ShapeSpec(channels=1, width=3)) # Test original cfg as well as serialization for x in [cfg, reload_lazy_config(cfg)]: obj = instantiate(x) self.assertIsInstance(obj.extra_arg, ShapeSpec) self.assertEqual(obj.extra_arg.channels, 1) self.assertEqual(obj.extra_arg.height, None) def test_bad_lazycall(self): with self.assertRaises(Exception): L(3) def test_interpolation(self): cfg = L(TestClass)(int_arg=3, extra_arg="${int_arg}") cfg.int_arg = 4 obj = instantiate(cfg) self.assertEqual(obj.extra_arg, 4) # Test that interpolation still works after serialization cfg = reload_lazy_config(cfg) cfg.int_arg = 5 obj = instantiate(cfg) self.assertEqual(obj.extra_arg, 5) ================================================ FILE: detectron2/tests/config/test_lazy_config.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import os import unittest import tempfile from itertools import count from detectron2.config import LazyConfig, LazyCall as L from omegaconf import DictConfig class TestLazyPythonConfig(unittest.TestCase): def setUp(self): self.root_filename = os.path.join(os.path.dirname(__file__), "root_cfg.py") def test_load(self): cfg = LazyConfig.load(self.root_filename) self.assertEqual(cfg.dir1a_dict.a, "modified") self.assertEqual(cfg.dir1b_dict.a, 1) self.assertEqual(cfg.lazyobj.x, "base_a_1") cfg.lazyobj.x = "new_x" # reload cfg = LazyConfig.load(self.root_filename) self.assertEqual(cfg.lazyobj.x, "base_a_1") def test_save_load(self): cfg = LazyConfig.load(self.root_filename) with tempfile.TemporaryDirectory(prefix="detectron2") as d: fname = os.path.join(d, "test_config.yaml") LazyConfig.save(cfg, fname) cfg2 = LazyConfig.load(fname) self.assertEqual(cfg2.lazyobj._target_, "itertools.count") self.assertEqual(cfg.lazyobj._target_, count) cfg2.lazyobj.pop("_target_") cfg.lazyobj.pop("_target_") # the rest are equal self.assertEqual(cfg, cfg2) def test_failed_save(self): cfg = DictConfig({"x": lambda: 3}, flags={"allow_objects": True}) with tempfile.TemporaryDirectory(prefix="detectron2") as d: fname = os.path.join(d, "test_config.yaml") LazyConfig.save(cfg, fname) self.assertTrue(os.path.exists(fname)) self.assertTrue(os.path.exists(fname + ".pkl")) def test_overrides(self): cfg = LazyConfig.load(self.root_filename) LazyConfig.apply_overrides(cfg, ["lazyobj.x=123", 'dir1b_dict.a="123"']) self.assertEqual(cfg.dir1b_dict.a, "123") self.assertEqual(cfg.lazyobj.x, 123) def test_invalid_overrides(self): cfg = LazyConfig.load(self.root_filename) with self.assertRaises(KeyError): LazyConfig.apply_overrides(cfg, ["lazyobj.x.xxx=123"]) def test_to_py(self): cfg = LazyConfig.load(self.root_filename) cfg.lazyobj.x = {"a": 1, "b": 2, "c": L(count)(x={"r": "a", "s": 2.4, "t": [1, 2, 3, "z"]})} cfg.list = ["a", 1, "b", 3.2] py_str = LazyConfig.to_py(cfg) expected = """cfg.dir1a_dict.a = "modified" cfg.dir1a_dict.b = 2 cfg.dir1b_dict.a = 1 cfg.dir1b_dict.b = 2 cfg.lazyobj = itertools.count( x={ "a": 1, "b": 2, "c": itertools.count(x={"r": "a", "s": 2.4, "t": [1, 2, 3, "z"]}), }, y="base_a_1_from_b", ) cfg.list = ["a", 1, "b", 3.2] """ self.assertEqual(py_str, expected) ================================================ FILE: detectron2/tests/config/test_yacs_config.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. import os import tempfile import unittest import torch from omegaconf import OmegaConf from detectron2 import model_zoo from detectron2.config import configurable, downgrade_config, get_cfg, upgrade_config from detectron2.layers import ShapeSpec from detectron2.modeling import build_model _V0_CFG = """ MODEL: RPN_HEAD: NAME: "TEST" VERSION: 0 """ _V1_CFG = """ MODEL: WEIGHT: "/path/to/weight" """ class TestConfigVersioning(unittest.TestCase): def test_upgrade_downgrade_consistency(self): cfg = get_cfg() # check that custom is preserved cfg.USER_CUSTOM = 1 down = downgrade_config(cfg, to_version=0) up = upgrade_config(down) self.assertTrue(up == cfg) def _merge_cfg_str(self, cfg, merge_str): f = tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) try: f.write(merge_str) f.close() cfg.merge_from_file(f.name) finally: os.remove(f.name) return cfg def test_auto_upgrade(self): cfg = get_cfg() latest_ver = cfg.VERSION cfg.USER_CUSTOM = 1 self._merge_cfg_str(cfg, _V0_CFG) self.assertEqual(cfg.MODEL.RPN.HEAD_NAME, "TEST") self.assertEqual(cfg.VERSION, latest_ver) def test_guess_v1(self): cfg = get_cfg() latest_ver = cfg.VERSION self._merge_cfg_str(cfg, _V1_CFG) self.assertEqual(cfg.VERSION, latest_ver) class _TestClassA(torch.nn.Module): @configurable def __init__(self, arg1, arg2, arg3=3): super().__init__() self.arg1 = arg1 self.arg2 = arg2 self.arg3 = arg3 assert arg1 == 1 assert arg2 == 2 assert arg3 == 3 @classmethod def from_config(cls, cfg): args = {"arg1": cfg.ARG1, "arg2": cfg.ARG2} return args class _TestClassB(_TestClassA): @configurable def __init__(self, input_shape, arg1, arg2, arg3=3): """ Doc of _TestClassB """ assert input_shape == "shape" super().__init__(arg1, arg2, arg3) @classmethod def from_config(cls, cfg, input_shape): # test extra positional arg in from_config args = {"arg1": cfg.ARG1, "arg2": cfg.ARG2} args["input_shape"] = input_shape return args class _LegacySubClass(_TestClassB): # an old subclass written in cfg style def __init__(self, cfg, input_shape, arg4=4): super().__init__(cfg, input_shape) assert self.arg1 == 1 assert self.arg2 == 2 assert self.arg3 == 3 class _NewSubClassNewInit(_TestClassB): # test new subclass with a new __init__ @configurable def __init__(self, input_shape, arg4=4, **kwargs): super().__init__(input_shape, **kwargs) assert self.arg1 == 1 assert self.arg2 == 2 assert self.arg3 == 3 class _LegacySubClassNotCfg(_TestClassB): # an old subclass written in cfg style, but argument is not called "cfg" def __init__(self, config, input_shape): super().__init__(config, input_shape) assert self.arg1 == 1 assert self.arg2 == 2 assert self.arg3 == 3 class _TestClassC(_TestClassB): @classmethod def from_config(cls, cfg, input_shape, **kwargs): # test extra kwarg overwrite args = {"arg1": cfg.ARG1, "arg2": cfg.ARG2} args["input_shape"] = input_shape args.update(kwargs) return args class _TestClassD(_TestClassA): @configurable def __init__(self, input_shape: ShapeSpec, arg1: int, arg2, arg3=3): assert input_shape == "shape" super().__init__(arg1, arg2, arg3) # _TestClassA.from_config does not have input_shape args. # Test whether input_shape will be forwarded to __init__ @configurable(from_config=lambda cfg, arg2: {"arg1": cfg.ARG1, "arg2": arg2, "arg3": cfg.ARG3}) def _test_func(arg1, arg2=2, arg3=3, arg4=4): return arg1, arg2, arg3, arg4 class TestConfigurable(unittest.TestCase): def testInitWithArgs(self): _ = _TestClassA(arg1=1, arg2=2, arg3=3) _ = _TestClassB("shape", arg1=1, arg2=2) _ = _TestClassC("shape", arg1=1, arg2=2) _ = _TestClassD("shape", arg1=1, arg2=2, arg3=3) def testPatchedAttr(self): self.assertTrue("Doc" in _TestClassB.__init__.__doc__) self.assertEqual(_TestClassD.__init__.__annotations__["arg1"], int) def testInitWithCfg(self): cfg = get_cfg() cfg.ARG1 = 1 cfg.ARG2 = 2 cfg.ARG3 = 3 _ = _TestClassA(cfg) _ = _TestClassB(cfg, input_shape="shape") _ = _TestClassC(cfg, input_shape="shape") _ = _TestClassD(cfg, input_shape="shape") _ = _LegacySubClass(cfg, input_shape="shape") _ = _NewSubClassNewInit(cfg, input_shape="shape") _ = _LegacySubClassNotCfg(cfg, input_shape="shape") with self.assertRaises(TypeError): # disallow forwarding positional args to __init__ since it's prone to errors _ = _TestClassD(cfg, "shape") # call with kwargs instead _ = _TestClassA(cfg=cfg) _ = _TestClassB(cfg=cfg, input_shape="shape") _ = _TestClassC(cfg=cfg, input_shape="shape") _ = _TestClassD(cfg=cfg, input_shape="shape") _ = _LegacySubClass(cfg=cfg, input_shape="shape") _ = _NewSubClassNewInit(cfg=cfg, input_shape="shape") _ = _LegacySubClassNotCfg(config=cfg, input_shape="shape") def testInitWithCfgOverwrite(self): cfg = get_cfg() cfg.ARG1 = 1 cfg.ARG2 = 999 # wrong config with self.assertRaises(AssertionError): _ = _TestClassA(cfg, arg3=3) # overwrite arg2 with correct config later: _ = _TestClassA(cfg, arg2=2, arg3=3) _ = _TestClassB(cfg, input_shape="shape", arg2=2, arg3=3) _ = _TestClassC(cfg, input_shape="shape", arg2=2, arg3=3) _ = _TestClassD(cfg, input_shape="shape", arg2=2, arg3=3) # call with kwargs cfg=cfg instead _ = _TestClassA(cfg=cfg, arg2=2, arg3=3) _ = _TestClassB(cfg=cfg, input_shape="shape", arg2=2, arg3=3) _ = _TestClassC(cfg=cfg, input_shape="shape", arg2=2, arg3=3) _ = _TestClassD(cfg=cfg, input_shape="shape", arg2=2, arg3=3) def testInitWithCfgWrongArgs(self): cfg = get_cfg() cfg.ARG1 = 1 cfg.ARG2 = 2 with self.assertRaises(TypeError): _ = _TestClassB(cfg, "shape", not_exist=1) with self.assertRaises(TypeError): _ = _TestClassC(cfg, "shape", not_exist=1) with self.assertRaises(TypeError): _ = _TestClassD(cfg, "shape", not_exist=1) def testBadClass(self): class _BadClass1: @configurable def __init__(self, a=1, b=2): pass class _BadClass2: @configurable def __init__(self, a=1, b=2): pass def from_config(self, cfg): # noqa pass class _BadClass3: @configurable def __init__(self, a=1, b=2): pass # bad name: must be cfg @classmethod def from_config(cls, config): # noqa pass with self.assertRaises(AttributeError): _ = _BadClass1(a=1) with self.assertRaises(TypeError): _ = _BadClass2(a=1) with self.assertRaises(TypeError): _ = _BadClass3(get_cfg()) def testFuncWithCfg(self): cfg = get_cfg() cfg.ARG1 = 10 cfg.ARG3 = 30 self.assertEqual(_test_func(1), (1, 2, 3, 4)) with self.assertRaises(TypeError): _test_func(cfg) self.assertEqual(_test_func(cfg, arg2=2), (10, 2, 30, 4)) self.assertEqual(_test_func(cfg, arg1=100, arg2=20), (100, 20, 30, 4)) self.assertEqual(_test_func(cfg, arg1=100, arg2=20, arg4=40), (100, 20, 30, 40)) self.assertTrue(callable(_test_func.from_config)) def testOmegaConf(self): cfg = model_zoo.get_config("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml") cfg = OmegaConf.create(cfg.dump()) if not torch.cuda.is_available(): cfg.MODEL.DEVICE = "cpu" # test that a model can be built with omegaconf config as well build_model(cfg) ================================================ FILE: detectron2/tests/data/__init__.py ================================================ ================================================ FILE: detectron2/tests/data/test_coco.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import json import numpy as np import os import tempfile import unittest import pycocotools.mask as mask_util from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.data.datasets.coco import convert_to_coco_dict, load_coco_json from detectron2.structures import BoxMode def make_mask(): """ Makes a donut shaped binary mask. """ H = 100 W = 100 mask = np.zeros([H, W], dtype=np.uint8) for x in range(W): for y in range(H): d = np.linalg.norm(np.array([W, H]) / 2 - np.array([x, y])) if d > 10 and d < 20: mask[y, x] = 1 return mask def uncompressed_rle(mask): l = mask.flatten(order="F").tolist() counts = [] p = False cnt = 0 for i in l: if i == p: cnt += 1 else: counts.append(cnt) p = i cnt = 1 counts.append(cnt) return {"counts": counts, "size": [mask.shape[0], mask.shape[1]]} def make_dataset_dicts(mask, compressed: bool = True): """ Returns a list of dicts that represents a single COCO data point for object detection. The single instance given by `mask` is represented by RLE, either compressed or uncompressed. """ record = {} record["file_name"] = "test" record["image_id"] = 0 record["height"] = mask.shape[0] record["width"] = mask.shape[1] y, x = np.nonzero(mask) if compressed: segmentation = mask_util.encode(np.asarray(mask, order="F")) else: segmentation = uncompressed_rle(mask) min_x = np.min(x) max_x = np.max(x) min_y = np.min(y) max_y = np.max(y) obj = { "bbox": [min_x, min_y, max_x, max_y], "bbox_mode": BoxMode.XYXY_ABS, "category_id": 0, "iscrowd": 0, "segmentation": segmentation, } record["annotations"] = [obj] return [record] class TestRLEToJson(unittest.TestCase): def test(self): # Make a dummy dataset. mask = make_mask() DatasetCatalog.register("test_dataset", lambda: make_dataset_dicts(mask)) MetadataCatalog.get("test_dataset").set(thing_classes=["test_label"]) # Dump to json. json_dict = convert_to_coco_dict("test_dataset") with tempfile.TemporaryDirectory() as tmpdir: json_file_name = os.path.join(tmpdir, "test.json") with open(json_file_name, "w") as f: json.dump(json_dict, f) # Load from json. dicts = load_coco_json(json_file_name, "") # Check the loaded mask matches the original. anno = dicts[0]["annotations"][0] loaded_mask = mask_util.decode(anno["segmentation"]) self.assertTrue(np.array_equal(loaded_mask, mask)) DatasetCatalog.pop("test_dataset") MetadataCatalog.pop("test_dataset") def test_uncompressed_RLE(self): mask = make_mask() rle = mask_util.encode(np.asarray(mask, order="F")) uncompressed = uncompressed_rle(mask) compressed = mask_util.frPyObjects(uncompressed, *rle["size"]) self.assertEqual(rle, compressed) class TestConvertCOCO(unittest.TestCase): @staticmethod def generate_data(): record = { "file_name": "test", "image_id": 0, "height": 100, "width": 100, "annotations": [ { "bbox": [10, 10, 10, 10, 5], "bbox_mode": BoxMode.XYWHA_ABS, "category_id": 0, "iscrowd": 0, }, { "bbox": [15, 15, 3, 3], "bbox_mode": BoxMode.XYXY_ABS, "category_id": 0, "iscrowd": 0, }, ], } return [record] def test_convert_to_coco(self): DatasetCatalog.register("test_dataset", lambda: TestConvertCOCO.generate_data()) MetadataCatalog.get("test_dataset").set(thing_classes=["test_label"]) convert_to_coco_dict("test_dataset") DatasetCatalog.pop("test_dataset") MetadataCatalog.pop("test_dataset") ================================================ FILE: detectron2/tests/data/test_coco_evaluation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import contextlib import copy import io import json import numpy as np import os import tempfile import unittest import torch from pycocotools.coco import COCO from pycocotools.cocoeval import COCOeval from detectron2.data import DatasetCatalog from detectron2.evaluation import COCOEvaluator from detectron2.evaluation.fast_eval_api import COCOeval_opt from detectron2.structures import Boxes, Instances class TestCOCOeval(unittest.TestCase): def test_fast_eval(self): # A small set of images/categories from COCO val # fmt: off detections = [{"image_id": 139, "category_id": 1, "bbox": [417.3332824707031, 159.27003479003906, 47.66064453125, 143.00193786621094], "score": 0.9949821829795837, "segmentation": {"size": [426, 640], "counts": "Tc`52W=3N0N4aNN^E7]:4XE1g:8kDMT;U100000001O1gE[Nk8h1dFiNY9Z1aFkN]9g2J3NdN`FlN`9S1cFRN07]9g1bFoM6;X9c1cFoM=8R9g1bFQN>3U9Y30O01OO1O001N2O1N1O4L4L5UNoE3V:CVF6Q:@YF9l9@ZF 0 else 0.0 msg = "%s: comparing COCO APIs, %s differs by %f" % (name, k, abs_diff) self.assertTrue(abs_diff < 1e-4, msg=msg) def test_unknown_category(self): dataset = "coco_2017_val_100" evaluator = COCOEvaluator(dataset) evaluator.reset() inputs = DatasetCatalog.get(dataset)[:2] pred = Instances((100, 100)) pred.pred_boxes = Boxes(torch.rand(2, 4)) pred.scores = torch.rand(2) pred.pred_classes = torch.tensor([10, 80]) output = {"instances": pred} evaluator.process(inputs, [output, output]) with self.assertRaises(AssertionError): evaluator.evaluate() ================================================ FILE: detectron2/tests/data/test_dataset.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import os import pickle import sys import unittest from functools import partial import torch from iopath.common.file_io import LazyPath from detectron2 import model_zoo from detectron2.config import get_cfg, instantiate from detectron2.data import ( DatasetCatalog, DatasetFromList, MapDataset, ToIterableDataset, build_batch_data_loader, build_detection_test_loader, build_detection_train_loader, ) from detectron2.data.common import AspectRatioGroupedDataset from detectron2.data.samplers import InferenceSampler, TrainingSampler def _a_slow_func(x): return "path/{}".format(x) class TestDatasetFromList(unittest.TestCase): # Failing for py3.6, likely due to pickle @unittest.skipIf(sys.version_info.minor <= 6, "Not supported in Python 3.6") def test_using_lazy_path(self): dataset = [] for i in range(10): dataset.append({"file_name": LazyPath(partial(_a_slow_func, i))}) dataset = DatasetFromList(dataset) for i in range(10): path = dataset[i]["file_name"] self.assertTrue(isinstance(path, LazyPath)) self.assertEqual(os.fspath(path), _a_slow_func(i)) class TestMapDataset(unittest.TestCase): @staticmethod def map_func(x): if x == 2: return None return x * 2 def test_map_style(self): ds = DatasetFromList([1, 2, 3]) ds = MapDataset(ds, TestMapDataset.map_func) self.assertEqual(ds[0], 2) self.assertEqual(ds[2], 6) self.assertIn(ds[1], [2, 6]) def test_iter_style(self): class DS(torch.utils.data.IterableDataset): def __iter__(self): yield from [1, 2, 3] ds = DS() ds = MapDataset(ds, TestMapDataset.map_func) self.assertIsInstance(ds, torch.utils.data.IterableDataset) data = list(iter(ds)) self.assertEqual(data, [2, 6]) def test_pickleability(self): ds = DatasetFromList([1, 2, 3]) ds = MapDataset(ds, lambda x: x * 2) ds = pickle.loads(pickle.dumps(ds)) self.assertEqual(ds[0], 2) class TestAspectRatioGrouping(unittest.TestCase): def test_reiter_leak(self): data = [(1, 0), (0, 1), (1, 0), (0, 1)] data = [{"width": a, "height": b} for (a, b) in data] batchsize = 2 dataset = AspectRatioGroupedDataset(data, batchsize) for _ in range(5): for idx, __ in enumerate(dataset): if idx == 1: # manually break, so the iterator does not stop by itself break # check that bucket sizes are valid for bucket in dataset._buckets: self.assertLess(len(bucket), batchsize) class TestDataLoader(unittest.TestCase): def _get_kwargs(self): # get kwargs of build_detection_train_loader cfg = model_zoo.get_config("common/data/coco.py").dataloader.train cfg.dataset.names = "coco_2017_val_100" cfg.pop("_target_") kwargs = {k: instantiate(v) for k, v in cfg.items()} return kwargs def test_build_dataloader_train(self): kwargs = self._get_kwargs() dl = build_detection_train_loader(**kwargs) next(iter(dl)) def test_build_iterable_dataloader_train(self): kwargs = self._get_kwargs() ds = DatasetFromList(kwargs.pop("dataset")) ds = ToIterableDataset(ds, TrainingSampler(len(ds))) dl = build_detection_train_loader(dataset=ds, **kwargs) next(iter(dl)) def test_build_iterable_dataloader_from_cfg(self): cfg = get_cfg() class MyData(torch.utils.data.IterableDataset): def __iter__(self): while True: yield 1 cfg.DATASETS.TRAIN = ["iter_data"] DatasetCatalog.register("iter_data", lambda: MyData()) dl = build_detection_train_loader(cfg, mapper=lambda x: x, aspect_ratio_grouping=False) next(iter(dl)) dl = build_detection_test_loader(cfg, "iter_data", mapper=lambda x: x) next(iter(dl)) def _check_is_range(self, data_loader, N): # check that data_loader produces range(N) data = list(iter(data_loader)) data = [x for batch in data for x in batch] # flatten the batches self.assertEqual(len(data), N) self.assertEqual(set(data), set(range(N))) def test_build_batch_dataloader_inference(self): # Test that build_batch_data_loader can be used for inference N = 96 ds = DatasetFromList(list(range(N))) sampler = InferenceSampler(len(ds)) dl = build_batch_data_loader(ds, sampler, 8, num_workers=3) self._check_is_range(dl, N) def test_build_dataloader_inference(self): N = 50 ds = DatasetFromList(list(range(N))) sampler = InferenceSampler(len(ds)) # test that parallel loader works correctly dl = build_detection_test_loader( dataset=ds, sampler=sampler, mapper=lambda x: x, num_workers=3 ) self._check_is_range(dl, N) # test that batch_size works correctly dl = build_detection_test_loader( dataset=ds, sampler=sampler, mapper=lambda x: x, batch_size=4, num_workers=0 ) self._check_is_range(dl, N) def test_build_iterable_dataloader_inference(self): # Test that build_detection_test_loader supports iterable dataset N = 50 ds = DatasetFromList(list(range(N))) ds = ToIterableDataset(ds, InferenceSampler(len(ds))) dl = build_detection_test_loader(dataset=ds, mapper=lambda x: x, num_workers=3) self._check_is_range(dl, N) ================================================ FILE: detectron2/tests/data/test_detection_utils.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import numpy as np import os import unittest import pycocotools.mask as mask_util from detectron2.data import MetadataCatalog, detection_utils from detectron2.data import transforms as T from detectron2.structures import BitMasks, BoxMode from detectron2.utils.file_io import PathManager class TestTransformAnnotations(unittest.TestCase): def test_transform_simple_annotation(self): transforms = T.TransformList([T.HFlipTransform(400)]) anno = { "bbox": np.asarray([10, 10, 200, 300]), "bbox_mode": BoxMode.XYXY_ABS, "category_id": 3, "segmentation": [[10, 10, 100, 100, 100, 10], [150, 150, 200, 150, 200, 200]], } output = detection_utils.transform_instance_annotations(anno, transforms, (400, 400)) self.assertTrue(np.allclose(output["bbox"], [200, 10, 390, 300])) self.assertEqual(len(output["segmentation"]), len(anno["segmentation"])) self.assertTrue(np.allclose(output["segmentation"][0], [390, 10, 300, 100, 300, 10])) detection_utils.annotations_to_instances([output, output], (400, 400)) def test_transform_empty_annotation(self): detection_utils.annotations_to_instances([], (400, 400)) def test_flip_keypoints(self): transforms = T.TransformList([T.HFlipTransform(400)]) anno = { "bbox": np.asarray([10, 10, 200, 300]), "bbox_mode": BoxMode.XYXY_ABS, "keypoints": np.random.rand(17, 3) * 50 + 15, } output = detection_utils.transform_instance_annotations( copy.deepcopy(anno), transforms, (400, 400), keypoint_hflip_indices=detection_utils.create_keypoint_hflip_indices( ["keypoints_coco_2017_train"] ), ) # The first keypoint is nose self.assertTrue(np.allclose(output["keypoints"][0, 0], 400 - anno["keypoints"][0, 0])) # The last 16 keypoints are 8 left-right pairs self.assertTrue( np.allclose( output["keypoints"][1:, 0].reshape(-1, 2)[:, ::-1], 400 - anno["keypoints"][1:, 0].reshape(-1, 2), ) ) self.assertTrue( np.allclose( output["keypoints"][1:, 1:].reshape(-1, 2, 2)[:, ::-1, :], anno["keypoints"][1:, 1:].reshape(-1, 2, 2), ) ) def test_crop(self): transforms = T.TransformList([T.CropTransform(300, 300, 10, 10)]) keypoints = np.random.rand(17, 3) * 50 + 15 keypoints[:, 2] = 2 anno = { "bbox": np.asarray([10, 10, 200, 400]), "bbox_mode": BoxMode.XYXY_ABS, "keypoints": keypoints, } output = detection_utils.transform_instance_annotations( copy.deepcopy(anno), transforms, (10, 10) ) # box is shifted and cropped self.assertTrue((output["bbox"] == np.asarray([0, 0, 0, 10])).all()) # keypoints are no longer visible self.assertTrue((output["keypoints"][:, 2] == 0).all()) def test_transform_RLE(self): transforms = T.TransformList([T.HFlipTransform(400)]) mask = np.zeros((300, 400), order="F").astype("uint8") mask[:, :200] = 1 anno = { "bbox": np.asarray([10, 10, 200, 300]), "bbox_mode": BoxMode.XYXY_ABS, "segmentation": mask_util.encode(mask[:, :, None])[0], "category_id": 3, } output = detection_utils.transform_instance_annotations( copy.deepcopy(anno), transforms, (300, 400) ) mask = output["segmentation"] self.assertTrue((mask[:, 200:] == 1).all()) self.assertTrue((mask[:, :200] == 0).all()) inst = detection_utils.annotations_to_instances( [output, output], (400, 400), mask_format="bitmask" ) self.assertTrue(isinstance(inst.gt_masks, BitMasks)) def test_transform_RLE_resize(self): transforms = T.TransformList( [T.HFlipTransform(400), T.ScaleTransform(300, 400, 400, 400, "bilinear")] ) mask = np.zeros((300, 400), order="F").astype("uint8") mask[:, :200] = 1 anno = { "bbox": np.asarray([10, 10, 200, 300]), "bbox_mode": BoxMode.XYXY_ABS, "segmentation": mask_util.encode(mask[:, :, None])[0], "category_id": 3, } output = detection_utils.transform_instance_annotations( copy.deepcopy(anno), transforms, (400, 400) ) inst = detection_utils.annotations_to_instances( [output, output], (400, 400), mask_format="bitmask" ) self.assertTrue(isinstance(inst.gt_masks, BitMasks)) def test_gen_crop(self): instance = {"bbox": [10, 10, 100, 100], "bbox_mode": BoxMode.XYXY_ABS} t = detection_utils.gen_crop_transform_with_instance((10, 10), (150, 150), instance) # the box center must fall into the cropped region self.assertTrue(t.x0 <= 55 <= t.x0 + t.w) def test_gen_crop_outside_boxes(self): instance = {"bbox": [10, 10, 100, 100], "bbox_mode": BoxMode.XYXY_ABS} with self.assertRaises(AssertionError): detection_utils.gen_crop_transform_with_instance((10, 10), (15, 15), instance) def test_read_sem_seg(self): cityscapes_dir = MetadataCatalog.get("cityscapes_fine_sem_seg_val").gt_dir sem_seg_gt_path = os.path.join( cityscapes_dir, "frankfurt", "frankfurt_000001_083852_gtFine_labelIds.png" ) if not PathManager.exists(sem_seg_gt_path): raise unittest.SkipTest( "Semantic segmentation ground truth {} not found.".format(sem_seg_gt_path) ) sem_seg = detection_utils.read_image(sem_seg_gt_path, "L") self.assertEqual(sem_seg.ndim, 3) self.assertEqual(sem_seg.shape[2], 1) self.assertEqual(sem_seg.dtype, np.uint8) self.assertEqual(sem_seg.max(), 32) self.assertEqual(sem_seg.min(), 1) def test_read_exif_orientation(self): # https://github.com/recurser/exif-orientation-examples/raw/master/Landscape_5.jpg URL = "detectron2://assets/Landscape_5.jpg" img = detection_utils.read_image(URL, "RGB") self.assertEqual(img.ndim, 3) self.assertEqual(img.dtype, np.uint8) self.assertEqual(img.shape, (1200, 1800, 3)) # check that shape is not transposed def test_opencv_exif_orientation(self): import cv2 URL = "detectron2://assets/Landscape_5.jpg" with PathManager.open(URL, "rb") as f: img = cv2.imdecode(np.frombuffer(f.read(), dtype="uint8"), cv2.IMREAD_COLOR) self.assertEqual(img.dtype, np.uint8) self.assertEqual(img.shape, (1200, 1800, 3)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/data/test_rotation_transform.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import unittest from detectron2.data.transforms.transform import RotationTransform class TestRotationTransform(unittest.TestCase): def assertEqualsArrays(self, a1, a2): self.assertTrue(np.allclose(a1, a2)) def randomData(self, h=5, w=5): image = np.random.rand(h, w) coords = np.array([[i, j] for j in range(h + 1) for i in range(w + 1)], dtype=float) return image, coords, h, w def test180(self): image, coords, h, w = self.randomData(6, 6) rot = RotationTransform(h, w, 180, expand=False, center=None) self.assertEqualsArrays(rot.apply_image(image), image[::-1, ::-1]) rotated_coords = [[w - c[0], h - c[1]] for c in coords] self.assertEqualsArrays(rot.apply_coords(coords), rotated_coords) def test45_coords(self): _, coords, h, w = self.randomData(4, 6) rot = RotationTransform(h, w, 45, expand=False, center=None) rotated_coords = [ [(x + y - (h + w) / 2) / np.sqrt(2) + w / 2, h / 2 + (y + (w - h) / 2 - x) / np.sqrt(2)] for (x, y) in coords ] self.assertEqualsArrays(rot.apply_coords(coords), rotated_coords) def test90(self): image, coords, h, w = self.randomData() rot = RotationTransform(h, w, 90, expand=False, center=None) self.assertEqualsArrays(rot.apply_image(image), image.T[::-1]) rotated_coords = [[c[1], w - c[0]] for c in coords] self.assertEqualsArrays(rot.apply_coords(coords), rotated_coords) def test90_expand(self): # non-square image image, coords, h, w = self.randomData(h=5, w=8) rot = RotationTransform(h, w, 90, expand=True, center=None) self.assertEqualsArrays(rot.apply_image(image), image.T[::-1]) rotated_coords = [[c[1], w - c[0]] for c in coords] self.assertEqualsArrays(rot.apply_coords(coords), rotated_coords) def test_center_expand(self): # center has no effect if expand=True because it only affects shifting image, coords, h, w = self.randomData(h=5, w=8) angle = np.random.randint(360) rot1 = RotationTransform(h, w, angle, expand=True, center=None) rot2 = RotationTransform(h, w, angle, expand=True, center=(0, 0)) rot3 = RotationTransform(h, w, angle, expand=True, center=(h, w)) rot4 = RotationTransform(h, w, angle, expand=True, center=(2, 5)) for r1 in [rot1, rot2, rot3, rot4]: for r2 in [rot1, rot2, rot3, rot4]: self.assertEqualsArrays(r1.apply_image(image), r2.apply_image(image)) self.assertEqualsArrays(r1.apply_coords(coords), r2.apply_coords(coords)) def test_inverse_transform(self): image, coords, h, w = self.randomData(h=5, w=8) rot = RotationTransform(h, w, 90, expand=True, center=None) rot_image = rot.apply_image(image) self.assertEqualsArrays(rot.inverse().apply_image(rot_image), image) rot = RotationTransform(h, w, 65, expand=True, center=None) rotated_coords = rot.apply_coords(coords) self.assertEqualsArrays(rot.inverse().apply_coords(rotated_coords), coords) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/data/test_sampler.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import itertools import math import operator import unittest import torch from torch.utils import data from torch.utils.data.sampler import SequentialSampler from detectron2.data.build import worker_init_reset_seed from detectron2.data.common import DatasetFromList, ToIterableDataset from detectron2.data.samplers import ( GroupedBatchSampler, InferenceSampler, RepeatFactorTrainingSampler, TrainingSampler, ) from detectron2.utils.env import seed_all_rng class TestGroupedBatchSampler(unittest.TestCase): def test_missing_group_id(self): sampler = SequentialSampler(list(range(100))) group_ids = [1] * 100 samples = GroupedBatchSampler(sampler, group_ids, 2) for mini_batch in samples: self.assertEqual(len(mini_batch), 2) def test_groups(self): sampler = SequentialSampler(list(range(100))) group_ids = [1, 0] * 50 samples = GroupedBatchSampler(sampler, group_ids, 2) for mini_batch in samples: self.assertEqual((mini_batch[0] + mini_batch[1]) % 2, 0) class TestSamplerDeterministic(unittest.TestCase): def test_to_iterable(self): sampler = TrainingSampler(100, seed=10) gt_output = list(itertools.islice(sampler, 100)) self.assertEqual(set(gt_output), set(range(100))) dataset = DatasetFromList(list(range(100))) dataset = ToIterableDataset(dataset, sampler) data_loader = data.DataLoader(dataset, num_workers=0, collate_fn=operator.itemgetter(0)) output = list(itertools.islice(data_loader, 100)) self.assertEqual(output, gt_output) data_loader = data.DataLoader( dataset, num_workers=2, collate_fn=operator.itemgetter(0), worker_init_fn=worker_init_reset_seed, # reset seed should not affect behavior of TrainingSampler ) output = list(itertools.islice(data_loader, 100)) # multiple workers should not lead to duplicate or different data self.assertEqual(output, gt_output) def test_training_sampler_seed(self): seed_all_rng(42) sampler = TrainingSampler(30) data = list(itertools.islice(sampler, 65)) seed_all_rng(42) sampler = TrainingSampler(30) seed_all_rng(999) # should be ineffective data2 = list(itertools.islice(sampler, 65)) self.assertEqual(data, data2) class TestRepeatFactorTrainingSampler(unittest.TestCase): def test_repeat_factors_from_category_frequency(self): repeat_thresh = 0.5 dataset_dicts = [ {"annotations": [{"category_id": 0}, {"category_id": 1}]}, {"annotations": [{"category_id": 0}]}, {"annotations": []}, ] rep_factors = RepeatFactorTrainingSampler.repeat_factors_from_category_frequency( dataset_dicts, repeat_thresh ) expected_rep_factors = torch.tensor([math.sqrt(3 / 2), 1.0, 1.0]) self.assertTrue(torch.allclose(rep_factors, expected_rep_factors)) class TestInferenceSampler(unittest.TestCase): def test_local_indices(self): sizes = [0, 16, 2, 42] world_sizes = [5, 2, 3, 4] expected_results = [ [range(0) for _ in range(5)], [range(8), range(8, 16)], [range(1), range(1, 2), range(0)], [range(11), range(11, 22), range(22, 32), range(32, 42)], ] for size, world_size, expected_result in zip(sizes, world_sizes, expected_results): with self.subTest(f"size={size}, world_size={world_size}"): local_indices = [ InferenceSampler._get_local_indices(size, world_size, r) for r in range(world_size) ] self.assertEqual(local_indices, expected_result) ================================================ FILE: detectron2/tests/data/test_transforms.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np import unittest from unittest import mock import torch from PIL import Image, ImageOps from torch.nn import functional as F from detectron2.config import get_cfg from detectron2.data import detection_utils from detectron2.data import transforms as T from detectron2.utils.logger import setup_logger logger = logging.getLogger(__name__) def polygon_allclose(poly1, poly2): """ Test whether two polygons are the same. Both arguments are nx2 numpy arrays. """ # ABCD and CDAB are the same polygon. So it's important to check after rolling for k in range(len(poly1)): rolled_poly1 = np.roll(poly1, k, axis=0) if np.allclose(rolled_poly1, poly2): return True return False class TestTransforms(unittest.TestCase): def setUp(self): setup_logger() def test_apply_rotated_boxes(self): np.random.seed(125) cfg = get_cfg() is_train = True augs = detection_utils.build_augmentation(cfg, is_train) image = np.random.rand(200, 300) image, transforms = T.apply_augmentations(augs, image) image_shape = image.shape[:2] # h, w assert image_shape == (800, 1200) annotation = {"bbox": [179, 97, 62, 40, -56]} boxes = np.array([annotation["bbox"]], dtype=np.float64) # boxes.shape = (1, 5) transformed_bbox = transforms.apply_rotated_box(boxes)[0] expected_bbox = np.array([484, 388, 248, 160, 56], dtype=np.float64) err_msg = "transformed_bbox = {}, expected {}".format(transformed_bbox, expected_bbox) assert np.allclose(transformed_bbox, expected_bbox), err_msg def test_resize_and_crop(self): np.random.seed(125) min_scale = 0.2 max_scale = 2.0 target_height = 1100 target_width = 1000 resize_aug = T.ResizeScale(min_scale, max_scale, target_height, target_width) fixed_size_crop_aug = T.FixedSizeCrop((target_height, target_width)) hflip_aug = T.RandomFlip() augs = [resize_aug, fixed_size_crop_aug, hflip_aug] original_image = np.random.rand(900, 800) image, transforms = T.apply_augmentations(augs, original_image) image_shape = image.shape[:2] # h, w self.assertEqual((1100, 1000), image_shape) boxes = np.array( [[91, 46, 144, 111], [523, 251, 614, 295]], dtype=np.float64, ) transformed_bboxs = transforms.apply_box(boxes) expected_bboxs = np.array( [ [895.42, 33.42666667, 933.91125, 80.66], [554.0825, 182.39333333, 620.17125, 214.36666667], ], dtype=np.float64, ) err_msg = "transformed_bbox = {}, expected {}".format(transformed_bboxs, expected_bboxs) self.assertTrue(np.allclose(transformed_bboxs, expected_bboxs), err_msg) polygon = np.array([[91, 46], [144, 46], [144, 111], [91, 111]]) transformed_polygons = transforms.apply_polygons([polygon]) expected_polygon = np.array([[934.0, 33.0], [934.0, 80.0], [896.0, 80.0], [896.0, 33.0]]) self.assertEqual(1, len(transformed_polygons)) err_msg = "transformed_polygon = {}, expected {}".format( transformed_polygons[0], expected_polygon ) self.assertTrue(polygon_allclose(transformed_polygons[0], expected_polygon), err_msg) def test_apply_rotated_boxes_unequal_scaling_factor(self): np.random.seed(125) h, w = 400, 200 newh, neww = 800, 800 image = np.random.rand(h, w) augs = [] augs.append(T.Resize(shape=(newh, neww))) image, transforms = T.apply_augmentations(augs, image) image_shape = image.shape[:2] # h, w assert image_shape == (newh, neww) boxes = np.array( [ [150, 100, 40, 20, 0], [150, 100, 40, 20, 30], [150, 100, 40, 20, 90], [150, 100, 40, 20, -90], ], dtype=np.float64, ) transformed_boxes = transforms.apply_rotated_box(boxes) expected_bboxes = np.array( [ [600, 200, 160, 40, 0], [600, 200, 144.22205102, 52.91502622, 49.10660535], [600, 200, 80, 80, 90], [600, 200, 80, 80, -90], ], dtype=np.float64, ) err_msg = "transformed_boxes = {}, expected {}".format(transformed_boxes, expected_bboxes) assert np.allclose(transformed_boxes, expected_bboxes), err_msg def test_print_augmentation(self): t = T.RandomCrop("relative", (100, 100)) self.assertEqual(str(t), "RandomCrop(crop_type='relative', crop_size=(100, 100))") t0 = T.RandomFlip(prob=0.5) self.assertEqual(str(t0), "RandomFlip(prob=0.5)") t1 = T.RandomFlip() self.assertEqual(str(t1), "RandomFlip()") t = T.AugmentationList([t0, t1]) self.assertEqual(str(t), f"AugmentationList[{t0}, {t1}]") def test_random_apply_prob_out_of_range_check(self): test_probabilities = {0.0: True, 0.5: True, 1.0: True, -0.01: False, 1.01: False} for given_probability, is_valid in test_probabilities.items(): if not is_valid: self.assertRaises(AssertionError, T.RandomApply, None, prob=given_probability) else: T.RandomApply(T.NoOpTransform(), prob=given_probability) def test_random_apply_wrapping_aug_probability_occured_evaluation(self): transform_mock = mock.MagicMock(name="MockTransform", spec=T.Augmentation) image_mock = mock.MagicMock(name="MockImage") random_apply = T.RandomApply(transform_mock, prob=0.001) with mock.patch.object(random_apply, "_rand_range", return_value=0.0001): transform = random_apply.get_transform(image_mock) transform_mock.get_transform.assert_called_once_with(image_mock) self.assertIsNot(transform, transform_mock) def test_random_apply_wrapping_std_transform_probability_occured_evaluation(self): transform_mock = mock.MagicMock(name="MockTransform", spec=T.Transform) image_mock = mock.MagicMock(name="MockImage") random_apply = T.RandomApply(transform_mock, prob=0.001) with mock.patch.object(random_apply, "_rand_range", return_value=0.0001): transform = random_apply.get_transform(image_mock) self.assertIs(transform, transform_mock) def test_random_apply_probability_not_occured_evaluation(self): transform_mock = mock.MagicMock(name="MockTransform", spec=T.Augmentation) image_mock = mock.MagicMock(name="MockImage") random_apply = T.RandomApply(transform_mock, prob=0.001) with mock.patch.object(random_apply, "_rand_range", return_value=0.9): transform = random_apply.get_transform(image_mock) transform_mock.get_transform.assert_not_called() self.assertIsInstance(transform, T.NoOpTransform) def test_augmentation_input_args(self): input_shape = (100, 100) output_shape = (50, 50) # define two augmentations with different args class TG1(T.Augmentation): def get_transform(self, image, sem_seg): return T.ResizeTransform( input_shape[0], input_shape[1], output_shape[0], output_shape[1] ) class TG2(T.Augmentation): def get_transform(self, image): assert image.shape[:2] == output_shape # check that TG1 is applied return T.HFlipTransform(output_shape[1]) image = np.random.rand(*input_shape).astype("float32") sem_seg = (np.random.rand(*input_shape) < 0.5).astype("uint8") inputs = T.AugInput(image, sem_seg=sem_seg) # provide two args tfms = inputs.apply_augmentations([TG1(), TG2()]) self.assertIsInstance(tfms[0], T.ResizeTransform) self.assertIsInstance(tfms[1], T.HFlipTransform) self.assertTrue(inputs.image.shape[:2] == output_shape) self.assertTrue(inputs.sem_seg.shape[:2] == output_shape) class TG3(T.Augmentation): def get_transform(self, image, nonexist): pass with self.assertRaises(AttributeError): inputs.apply_augmentations([TG3()]) def test_augmentation_list(self): input_shape = (100, 100) image = np.random.rand(*input_shape).astype("float32") sem_seg = (np.random.rand(*input_shape) < 0.5).astype("uint8") inputs = T.AugInput(image, sem_seg=sem_seg) # provide two args augs = T.AugmentationList([T.RandomFlip(), T.Resize(20)]) _ = T.AugmentationList([augs, T.Resize(30)])(inputs) # 3 in latest fvcore (flattened transformlist), 2 in older # self.assertEqual(len(tfms), 3) def test_color_transforms(self): rand_img = np.random.random((100, 100, 3)) * 255 rand_img = rand_img.astype("uint8") # Test no-op noop_transform = T.ColorTransform(lambda img: img) self.assertTrue(np.array_equal(rand_img, noop_transform.apply_image(rand_img))) # Test a ImageOps operation magnitude = np.random.randint(0, 256) solarize_transform = T.PILColorTransform(lambda img: ImageOps.solarize(img, magnitude)) expected_img = ImageOps.solarize(Image.fromarray(rand_img), magnitude) self.assertTrue(np.array_equal(expected_img, solarize_transform.apply_image(rand_img))) def test_resize_transform(self): input_shapes = [(100, 100), (100, 100, 1), (100, 100, 3)] output_shapes = [(200, 200), (200, 200, 1), (200, 200, 3)] for in_shape, out_shape in zip(input_shapes, output_shapes): in_img = np.random.randint(0, 255, size=in_shape, dtype=np.uint8) tfm = T.ResizeTransform(in_shape[0], in_shape[1], out_shape[0], out_shape[1]) out_img = tfm.apply_image(in_img) self.assertEqual(out_img.shape, out_shape) def test_resize_shorted_edge_scriptable(self): def f(image): newh, neww = T.ResizeShortestEdge.get_output_shape( image.shape[-2], image.shape[-1], 80, 133 ) return F.interpolate(image.unsqueeze(0), size=(newh, neww)) input = torch.randn(3, 10, 10) script_f = torch.jit.script(f) self.assertTrue(torch.allclose(f(input), script_f(input))) # generalize to new shapes input = torch.randn(3, 8, 100) self.assertTrue(torch.allclose(f(input), script_f(input))) def test_extent_transform(self): input_shapes = [(100, 100), (100, 100, 1), (100, 100, 3)] src_rect = (20, 20, 80, 80) output_shapes = [(200, 200), (200, 200, 1), (200, 200, 3)] for in_shape, out_shape in zip(input_shapes, output_shapes): in_img = np.random.randint(0, 255, size=in_shape, dtype=np.uint8) tfm = T.ExtentTransform(src_rect, out_shape[:2]) out_img = tfm.apply_image(in_img) self.assertTrue(out_img.shape == out_shape) ================================================ FILE: detectron2/tests/export/test_c10.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest try: # Caffe2 used to be included in PyTorch, but since PyTorch 1.10+, # it is not included in pre-built packages. This is a safety BC check from detectron2.config import get_cfg from detectron2.export.c10 import Caffe2RPN from detectron2.layers import ShapeSpec except ImportError: raise unittest.SkipTest( f"PyTorch does not have Caffe2 support. Skipping all tests in {__name__}" ) from None class TestCaffe2RPN(unittest.TestCase): def test_instantiation(self): cfg = get_cfg() cfg.MODEL.RPN.BBOX_REG_WEIGHTS = (1, 1, 1, 1, 1) input_shapes = {"res4": ShapeSpec(channels=256, stride=4)} rpn = Caffe2RPN(cfg, input_shapes) assert rpn is not None cfg.MODEL.RPN.BBOX_REG_WEIGHTS = (10, 10, 5, 5, 1) with self.assertRaises(AssertionError): rpn = Caffe2RPN(cfg, input_shapes) ================================================ FILE: detectron2/tests/layers/__init__.py ================================================ ================================================ FILE: detectron2/tests/layers/test_blocks.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest import torch from torch import nn from detectron2.layers import ASPP, DepthwiseSeparableConv2d, FrozenBatchNorm2d from detectron2.modeling.backbone.resnet import BasicStem, ResNet """ Test for misc layers. """ class TestBlocks(unittest.TestCase): def test_separable_conv(self): DepthwiseSeparableConv2d(3, 10, norm1="BN", activation1=nn.PReLU()) def test_aspp(self): m = ASPP(3, 10, [2, 3, 4], norm="", activation=nn.PReLU()) self.assertIsNot(m.convs[0].activation.weight, m.convs[1].activation.weight) self.assertIsNot(m.convs[0].activation.weight, m.project.activation.weight) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_frozen_batchnorm_fp16(self): from torch.cuda.amp import autocast C = 10 input = torch.rand(1, C, 10, 10).cuda() m = FrozenBatchNorm2d(C).cuda() with autocast(): output = m(input.half()) self.assertEqual(output.dtype, torch.float16) # requires_grad triggers a different codepath input.requires_grad_() with autocast(): output = m(input.half()) self.assertEqual(output.dtype, torch.float16) def test_resnet_unused_stages(self): resnet = ResNet(BasicStem(), ResNet.make_default_stages(18), out_features=["res2"]) self.assertTrue(hasattr(resnet, "res2")) self.assertFalse(hasattr(resnet, "res3")) self.assertFalse(hasattr(resnet, "res5")) resnet = ResNet(BasicStem(), ResNet.make_default_stages(18), out_features=["res2", "res5"]) self.assertTrue(hasattr(resnet, "res2")) self.assertTrue(hasattr(resnet, "res4")) self.assertTrue(hasattr(resnet, "res5")) ================================================ FILE: detectron2/tests/layers/test_deformable.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import unittest import torch from detectron2.layers import DeformConv, ModulatedDeformConv from detectron2.utils.env import TORCH_VERSION @unittest.skipIf( TORCH_VERSION == (1, 8) and torch.cuda.is_available(), "This test fails under cuda11 + torch1.8.", ) class DeformableTest(unittest.TestCase): @unittest.skipIf(not torch.cuda.is_available(), "Deformable not supported for cpu") def test_forward_output(self): device = torch.device("cuda") N, C, H, W = shape = 1, 1, 5, 5 kernel_size = 3 padding = 1 inputs = torch.arange(np.prod(shape), dtype=torch.float32).reshape(*shape).to(device) """ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 """ offset_channels = kernel_size * kernel_size * 2 offset = torch.full((N, offset_channels, H, W), 0.5, dtype=torch.float32).to(device) # Test DCN v1 deform = DeformConv(C, C, kernel_size=kernel_size, padding=padding).to(device) deform.weight = torch.nn.Parameter(torch.ones_like(deform.weight)) output = deform(inputs, offset) output = output.detach().cpu().numpy() deform_results = np.array( [ [30, 41.25, 48.75, 45, 28.75], [62.25, 81, 90, 80.25, 50.25], [99.75, 126, 135, 117.75, 72.75], [105, 131.25, 138.75, 120, 73.75], [71.75, 89.25, 93.75, 80.75, 49.5], ] ) self.assertTrue(np.allclose(output.flatten(), deform_results.flatten())) # Test DCN v2 mask_channels = kernel_size * kernel_size mask = torch.full((N, mask_channels, H, W), 0.5, dtype=torch.float32).to(device) modulate_deform = ModulatedDeformConv(C, C, kernel_size, padding=padding, bias=False).to( device ) modulate_deform.weight = deform.weight output = modulate_deform(inputs, offset, mask) output = output.detach().cpu().numpy() self.assertTrue(np.allclose(output.flatten(), deform_results.flatten() * 0.5)) def test_forward_output_on_cpu(self): device = torch.device("cpu") N, C, H, W = shape = 1, 1, 5, 5 kernel_size = 3 padding = 1 inputs = torch.arange(np.prod(shape), dtype=torch.float32).reshape(*shape).to(device) offset_channels = kernel_size * kernel_size * 2 offset = torch.full((N, offset_channels, H, W), 0.5, dtype=torch.float32).to(device) # Test DCN v1 on cpu deform = DeformConv(C, C, kernel_size=kernel_size, padding=padding).to(device) deform.weight = torch.nn.Parameter(torch.ones_like(deform.weight)) output = deform(inputs, offset) output = output.detach().cpu().numpy() deform_results = np.array( [ [30, 41.25, 48.75, 45, 28.75], [62.25, 81, 90, 80.25, 50.25], [99.75, 126, 135, 117.75, 72.75], [105, 131.25, 138.75, 120, 73.75], [71.75, 89.25, 93.75, 80.75, 49.5], ] ) self.assertTrue(np.allclose(output.flatten(), deform_results.flatten())) @unittest.skipIf(not torch.cuda.is_available(), "This test requires gpu access") def test_forward_output_on_cpu_equals_output_on_gpu(self): N, C, H, W = shape = 2, 4, 10, 10 kernel_size = 3 padding = 1 for groups in [1, 2]: inputs = torch.arange(np.prod(shape), dtype=torch.float32).reshape(*shape) offset_channels = kernel_size * kernel_size * 2 offset = torch.full((N, offset_channels, H, W), 0.5, dtype=torch.float32) deform_gpu = DeformConv( C, C, kernel_size=kernel_size, padding=padding, groups=groups ).to("cuda") deform_gpu.weight = torch.nn.Parameter(torch.ones_like(deform_gpu.weight)) output_gpu = deform_gpu(inputs.to("cuda"), offset.to("cuda")).detach().cpu().numpy() deform_cpu = DeformConv( C, C, kernel_size=kernel_size, padding=padding, groups=groups ).to("cpu") deform_cpu.weight = torch.nn.Parameter(torch.ones_like(deform_cpu.weight)) output_cpu = deform_cpu(inputs.to("cpu"), offset.to("cpu")).detach().numpy() self.assertTrue(np.allclose(output_gpu.flatten(), output_cpu.flatten())) @unittest.skipIf(not torch.cuda.is_available(), "Deformable not supported for cpu") def test_small_input(self): device = torch.device("cuda") for kernel_size in [3, 5]: padding = kernel_size // 2 N, C, H, W = shape = (1, 1, kernel_size - 1, kernel_size - 1) inputs = torch.rand(shape).to(device) # input size is smaller than kernel size offset_channels = kernel_size * kernel_size * 2 offset = torch.randn((N, offset_channels, H, W), dtype=torch.float32).to(device) deform = DeformConv(C, C, kernel_size=kernel_size, padding=padding).to(device) output = deform(inputs, offset) self.assertTrue(output.shape == inputs.shape) mask_channels = kernel_size * kernel_size mask = torch.ones((N, mask_channels, H, W), dtype=torch.float32).to(device) modulate_deform = ModulatedDeformConv( C, C, kernel_size, padding=padding, bias=False ).to(device) output = modulate_deform(inputs, offset, mask) self.assertTrue(output.shape == inputs.shape) @unittest.skipIf(not torch.cuda.is_available(), "Deformable not supported for cpu") def test_raise_exception(self): device = torch.device("cuda") N, C, H, W = shape = 1, 1, 3, 3 kernel_size = 3 padding = 1 inputs = torch.rand(shape, dtype=torch.float32).to(device) offset_channels = kernel_size * kernel_size # This is wrong channels for offset offset = torch.randn((N, offset_channels, H, W), dtype=torch.float32).to(device) deform = DeformConv(C, C, kernel_size=kernel_size, padding=padding).to(device) self.assertRaises(RuntimeError, deform, inputs, offset) offset_channels = kernel_size * kernel_size * 2 offset = torch.randn((N, offset_channels, H, W), dtype=torch.float32).to(device) mask_channels = kernel_size * kernel_size * 2 # This is wrong channels for mask mask = torch.ones((N, mask_channels, H, W), dtype=torch.float32).to(device) modulate_deform = ModulatedDeformConv(C, C, kernel_size, padding=padding, bias=False).to( device ) self.assertRaises(RuntimeError, modulate_deform, inputs, offset, mask) def test_repr(self): module = DeformConv(3, 10, kernel_size=3, padding=1, deformable_groups=2) correct_string = ( "DeformConv(in_channels=3, out_channels=10, kernel_size=(3, 3), " "stride=(1, 1), padding=(1, 1), dilation=(1, 1), " "groups=1, deformable_groups=2, bias=False)" ) self.assertEqual(repr(module), correct_string) module = ModulatedDeformConv(3, 10, kernel_size=3, padding=1, deformable_groups=2) correct_string = ( "ModulatedDeformConv(in_channels=3, out_channels=10, kernel_size=(3, 3), " "stride=1, padding=1, dilation=1, groups=1, deformable_groups=2, bias=True)" ) self.assertEqual(repr(module), correct_string) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/layers/test_losses.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import unittest import torch from detectron2.layers import ciou_loss, diou_loss class TestLosses(unittest.TestCase): def test_diou_loss(self): """ loss = 1 - iou + d/c where, d = (distance between centers of the 2 boxes)^2 c = (diagonal length of the smallest enclosing box covering the 2 boxes)^2 """ # Identical boxes should have loss of 0 box = torch.tensor([-1, -1, 1, 1], dtype=torch.float32) loss = diou_loss(box, box) self.assertTrue(np.allclose(loss, [0.0])) # Half size box inside other box # iou = 0.5, d = 0.25, c = 8 box2 = torch.tensor([0, -1, 1, 1], dtype=torch.float32) loss = diou_loss(box, box2) self.assertTrue(np.allclose(loss, [0.53125])) # Two diagonally adjacent boxes # iou = 0, d = 2, c = 8 box3 = torch.tensor([0, 0, 1, 1], dtype=torch.float32) box4 = torch.tensor([1, 1, 2, 2], dtype=torch.float32) loss = diou_loss(box3, box4) self.assertTrue(np.allclose(loss, [1.25])) # Test batched loss and reductions box1s = torch.stack([box, box3], dim=0) box2s = torch.stack([box2, box4], dim=0) loss = diou_loss(box1s, box2s, reduction="sum") self.assertTrue(np.allclose(loss, [1.78125])) loss = diou_loss(box1s, box2s, reduction="mean") self.assertTrue(np.allclose(loss, [0.890625])) def test_ciou_loss(self): """ loss = 1 - iou + d/c + alpha*v where, d = (distance between centers of the 2 boxes)^2 c = (diagonal length of the smallest enclosing box covering the 2 boxes)^2 v = (4/pi^2) * (arctan(box1_w/box1_h) - arctan(box2_w/box2_h))^2 alpha = v/(1 - iou + v) """ # Identical boxes should have loss of 0 box = torch.tensor([-1, -1, 1, 1], dtype=torch.float32) loss = ciou_loss(box, box) self.assertTrue(np.allclose(loss, [0.0])) # Half size box inside other box # iou = 0.5, d = 0.25, c = 8 # v = (4/pi^2) * (arctan(1) - arctan(0.5))^2 = 0.042 # alpha = 0.0775 box2 = torch.tensor([0, -1, 1, 1], dtype=torch.float32) loss = ciou_loss(box, box2) self.assertTrue(np.allclose(loss, [0.5345])) # Two diagonally adjacent boxes # iou = 0, d = 2, c = 8, v = 0, alpha = 0 box3 = torch.tensor([0, 0, 1, 1], dtype=torch.float32) box4 = torch.tensor([1, 1, 2, 2], dtype=torch.float32) loss = ciou_loss(box3, box4) self.assertTrue(np.allclose(loss, [1.25])) # Test batched loss and reductions box1s = torch.stack([box, box3], dim=0) box2s = torch.stack([box2, box4], dim=0) loss = ciou_loss(box1s, box2s, reduction="sum") self.assertTrue(np.allclose(loss, [1.7845])) loss = ciou_loss(box1s, box2s, reduction="mean") self.assertTrue(np.allclose(loss, [0.89225])) ================================================ FILE: detectron2/tests/layers/test_mask_ops.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import contextlib import io import numpy as np import unittest from collections import defaultdict import torch import tqdm from fvcore.common.benchmark import benchmark from pycocotools.coco import COCO from tabulate import tabulate from torch.nn import functional as F from detectron2.data import MetadataCatalog from detectron2.layers.mask_ops import ( pad_masks, paste_mask_in_image_old, paste_masks_in_image, scale_boxes, ) from detectron2.structures import BitMasks, Boxes, BoxMode, PolygonMasks from detectron2.structures.masks import polygons_to_bitmask from detectron2.utils.file_io import PathManager from detectron2.utils.testing import random_boxes def iou_between_full_image_bit_masks(a, b): intersect = (a & b).sum() union = (a | b).sum() return intersect / union def rasterize_polygons_with_grid_sample(full_image_bit_mask, box, mask_size, threshold=0.5): x0, y0, x1, y1 = box[0], box[1], box[2], box[3] img_h, img_w = full_image_bit_mask.shape mask_y = np.arange(0.0, mask_size) + 0.5 # mask y sample coords in [0.5, mask_size - 0.5] mask_x = np.arange(0.0, mask_size) + 0.5 # mask x sample coords in [0.5, mask_size - 0.5] mask_y = mask_y / mask_size * (y1 - y0) + y0 mask_x = mask_x / mask_size * (x1 - x0) + x0 mask_x = (mask_x - 0.5) / (img_w - 1) * 2 + -1 mask_y = (mask_y - 0.5) / (img_h - 1) * 2 + -1 gy, gx = torch.meshgrid(torch.from_numpy(mask_y), torch.from_numpy(mask_x)) ind = torch.stack([gx, gy], dim=-1).to(dtype=torch.float32) full_image_bit_mask = torch.from_numpy(full_image_bit_mask) mask = F.grid_sample( full_image_bit_mask[None, None, :, :].to(dtype=torch.float32), ind[None, :, :, :], align_corners=True, ) return mask[0, 0] >= threshold class TestMaskCropPaste(unittest.TestCase): def setUp(self): json_file = MetadataCatalog.get("coco_2017_val_100").json_file if not PathManager.isfile(json_file): raise unittest.SkipTest("{} not found".format(json_file)) with contextlib.redirect_stdout(io.StringIO()): json_file = PathManager.get_local_path(json_file) self.coco = COCO(json_file) def test_crop_paste_consistency(self): """ rasterize_polygons_within_box (used in training) and paste_masks_in_image (used in inference) should be inverse operations to each other. This function runs several implementation of the above two operations and prints the reconstruction error. """ anns = self.coco.loadAnns(self.coco.getAnnIds(iscrowd=False)) # avoid crowd annotations selected_anns = anns[:100] ious = [] for ann in tqdm.tqdm(selected_anns): results = self.process_annotation(ann) ious.append([k[2] for k in results]) ious = np.array(ious) mean_ious = ious.mean(axis=0) table = [] res_dic = defaultdict(dict) for row, iou in zip(results, mean_ious): table.append((row[0], row[1], iou)) res_dic[row[0]][row[1]] = iou print(tabulate(table, headers=["rasterize", "paste", "iou"], tablefmt="simple")) # assert that the reconstruction is good: self.assertTrue(res_dic["polygon"]["aligned"] > 0.94) self.assertTrue(res_dic["roialign"]["aligned"] > 0.95) def process_annotation(self, ann, mask_side_len=28): # Parse annotation data img_info = self.coco.loadImgs(ids=[ann["image_id"]])[0] height, width = img_info["height"], img_info["width"] gt_polygons = [np.array(p, dtype=np.float64) for p in ann["segmentation"]] gt_bbox = BoxMode.convert(ann["bbox"], BoxMode.XYWH_ABS, BoxMode.XYXY_ABS) gt_bit_mask = polygons_to_bitmask(gt_polygons, height, width) # Run rasterize .. torch_gt_bbox = torch.tensor(gt_bbox).to(dtype=torch.float32).reshape(-1, 4) box_bitmasks = { "polygon": PolygonMasks([gt_polygons]).crop_and_resize(torch_gt_bbox, mask_side_len)[0], "gridsample": rasterize_polygons_with_grid_sample(gt_bit_mask, gt_bbox, mask_side_len), "roialign": BitMasks(torch.from_numpy(gt_bit_mask[None, :, :])).crop_and_resize( torch_gt_bbox, mask_side_len )[0], } # Run paste .. results = defaultdict(dict) for k, box_bitmask in box_bitmasks.items(): padded_bitmask, scale = pad_masks(box_bitmask[None, :, :], 1) scaled_boxes = scale_boxes(torch_gt_bbox, scale) r = results[k] r["old"] = paste_mask_in_image_old( padded_bitmask[0], scaled_boxes[0], height, width, threshold=0.5 ) r["aligned"] = paste_masks_in_image( box_bitmask[None, :, :], Boxes(torch_gt_bbox), (height, width) )[0] table = [] for rasterize_method, r in results.items(): for paste_method, mask in r.items(): mask = np.asarray(mask) iou = iou_between_full_image_bit_masks(gt_bit_mask.astype("uint8"), mask) table.append((rasterize_method, paste_method, iou)) return table def test_polygon_area(self): # Draw polygon boxes for d in [5.0, 10.0, 1000.0]: polygon = PolygonMasks([[[0, 0, 0, d, d, d, d, 0]]]) area = polygon.area()[0] target = d**2 self.assertEqual(area, target) # Draw polygon triangles for d in [5.0, 10.0, 1000.0]: polygon = PolygonMasks([[[0, 0, 0, d, d, d]]]) area = polygon.area()[0] target = d**2 / 2 self.assertEqual(area, target) def test_paste_mask_scriptable(self): scripted_f = torch.jit.script(paste_masks_in_image) N = 10 masks = torch.rand(N, 28, 28) boxes = Boxes(random_boxes(N, 100)).tensor image_shape = (150, 150) out = paste_masks_in_image(masks, boxes, image_shape) scripted_out = scripted_f(masks, boxes, image_shape) self.assertTrue(torch.equal(out, scripted_out)) def benchmark_paste(): S = 800 H, W = image_shape = (S, S) N = 64 torch.manual_seed(42) masks = torch.rand(N, 28, 28) center = torch.rand(N, 2) * 600 + 100 wh = torch.clamp(torch.randn(N, 2) * 40 + 200, min=50) x0y0 = torch.clamp(center - wh * 0.5, min=0.0) x1y1 = torch.clamp(center + wh * 0.5, max=S) boxes = Boxes(torch.cat([x0y0, x1y1], axis=1)) def func(device, n=3): m = masks.to(device=device) b = boxes.to(device=device) def bench(): for _ in range(n): paste_masks_in_image(m, b, image_shape) if device.type == "cuda": torch.cuda.synchronize() return bench specs = [{"device": torch.device("cpu"), "n": 3}] if torch.cuda.is_available(): specs.append({"device": torch.device("cuda"), "n": 3}) benchmark(func, "paste_masks", specs, num_iters=10, warmup_iters=2) if __name__ == "__main__": benchmark_paste() unittest.main() ================================================ FILE: detectron2/tests/layers/test_nms.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from __future__ import absolute_import, division, print_function, unicode_literals import unittest import torch from detectron2.layers import batched_nms from detectron2.utils.testing import random_boxes class TestNMS(unittest.TestCase): def _create_tensors(self, N): boxes = random_boxes(N, 200) scores = torch.rand(N) return boxes, scores def test_nms_scriptability(self): N = 2000 num_classes = 50 boxes, scores = self._create_tensors(N) idxs = torch.randint(0, num_classes, (N,)) scripted_batched_nms = torch.jit.script(batched_nms) err_msg = "NMS is incompatible with jit-scripted NMS for IoU={}" for iou in [0.2, 0.5, 0.8]: keep_ref = batched_nms(boxes, scores, idxs, iou) backup = boxes.clone() scripted_keep = scripted_batched_nms(boxes, scores, idxs, iou) assert torch.allclose(boxes, backup), "boxes modified by jit-scripted batched_nms" self.assertTrue(torch.equal(keep_ref, scripted_keep), err_msg.format(iou)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/layers/test_nms_rotated.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from __future__ import absolute_import, division, print_function, unicode_literals import numpy as np import unittest from copy import deepcopy import torch from torchvision import ops from detectron2.layers import batched_nms, batched_nms_rotated, nms_rotated from detectron2.utils.testing import random_boxes def nms_edit_distance(keep1, keep2): """ Compare the "keep" result of two nms call. They are allowed to be different in terms of edit distance due to floating point precision issues, e.g., if a box happen to have an IoU of 0.5 with another box, one implentation may choose to keep it while another may discard it. """ keep1, keep2 = keep1.cpu(), keep2.cpu() if torch.equal(keep1, keep2): # they should be equal most of the time return 0 keep1, keep2 = tuple(keep1), tuple(keep2) m, n = len(keep1), len(keep2) # edit distance with DP f = [np.arange(n + 1), np.arange(n + 1)] for i in range(m): cur_row = i % 2 other_row = (i + 1) % 2 f[other_row][0] = i + 1 for j in range(n): f[other_row][j + 1] = ( f[cur_row][j] if keep1[i] == keep2[j] else min(min(f[cur_row][j], f[cur_row][j + 1]), f[other_row][j]) + 1 ) return f[m % 2][n] class TestNMSRotated(unittest.TestCase): def reference_horizontal_nms(self, boxes, scores, iou_threshold): """ Args: box_scores (N, 5): boxes in corner-form and probabilities. (Note here 5 == 4 + 1, i.e., 4-dim horizontal box + 1-dim prob) iou_threshold: intersection over union threshold. Returns: picked: a list of indexes of the kept boxes """ picked = [] _, indexes = scores.sort(descending=True) while len(indexes) > 0: current = indexes[0] picked.append(current.item()) if len(indexes) == 1: break current_box = boxes[current, :] indexes = indexes[1:] rest_boxes = boxes[indexes, :] iou = ops.box_iou(rest_boxes, current_box.unsqueeze(0)).squeeze(1) indexes = indexes[iou <= iou_threshold] return torch.as_tensor(picked) def _create_tensors(self, N, device="cpu"): boxes = random_boxes(N, 200, device=device) scores = torch.rand(N, device=device) return boxes, scores def test_batched_nms_rotated_0_degree_cpu(self, device="cpu"): N = 2000 num_classes = 50 boxes, scores = self._create_tensors(N, device=device) idxs = torch.randint(0, num_classes, (N,)) rotated_boxes = torch.zeros(N, 5, device=device) rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0 rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0 rotated_boxes[:, 2] = boxes[:, 2] - boxes[:, 0] rotated_boxes[:, 3] = boxes[:, 3] - boxes[:, 1] err_msg = "Rotated NMS with 0 degree is incompatible with horizontal NMS for IoU={}" for iou in [0.2, 0.5, 0.8]: backup = boxes.clone() keep_ref = batched_nms(boxes, scores, idxs, iou) assert torch.allclose(boxes, backup), "boxes modified by batched_nms" backup = rotated_boxes.clone() keep = batched_nms_rotated(rotated_boxes, scores, idxs, iou) assert torch.allclose( rotated_boxes, backup ), "rotated_boxes modified by batched_nms_rotated" # Occasionally the gap can be large if there are many IOU on the threshold boundary self.assertLessEqual(nms_edit_distance(keep, keep_ref), 5, err_msg.format(iou)) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_batched_nms_rotated_0_degree_cuda(self): self.test_batched_nms_rotated_0_degree_cpu(device="cuda") def test_nms_rotated_0_degree_cpu(self, device="cpu"): N = 1000 boxes, scores = self._create_tensors(N, device=device) rotated_boxes = torch.zeros(N, 5, device=device) rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0 rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0 rotated_boxes[:, 2] = boxes[:, 2] - boxes[:, 0] rotated_boxes[:, 3] = boxes[:, 3] - boxes[:, 1] err_msg = "Rotated NMS incompatible between CPU and reference implementation for IoU={}" for iou in [0.2, 0.5, 0.8]: keep_ref = self.reference_horizontal_nms(boxes, scores, iou) keep = nms_rotated(rotated_boxes, scores, iou) self.assertLessEqual(nms_edit_distance(keep, keep_ref), 1, err_msg.format(iou)) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_nms_rotated_0_degree_cuda(self): self.test_nms_rotated_0_degree_cpu(device="cuda") def test_nms_rotated_90_degrees_cpu(self): N = 1000 boxes, scores = self._create_tensors(N) rotated_boxes = torch.zeros(N, 5) rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0 rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0 # Note for rotated_boxes[:, 2] and rotated_boxes[:, 3]: # widths and heights are intentionally swapped here for 90 degrees case # so that the reference horizontal nms could be used rotated_boxes[:, 2] = boxes[:, 3] - boxes[:, 1] rotated_boxes[:, 3] = boxes[:, 2] - boxes[:, 0] rotated_boxes[:, 4] = torch.ones(N) * 90 err_msg = "Rotated NMS incompatible between CPU and reference implementation for IoU={}" for iou in [0.2, 0.5, 0.8]: keep_ref = self.reference_horizontal_nms(boxes, scores, iou) keep = nms_rotated(rotated_boxes, scores, iou) self.assertLessEqual(nms_edit_distance(keep, keep_ref), 1, err_msg.format(iou)) def test_nms_rotated_180_degrees_cpu(self): N = 1000 boxes, scores = self._create_tensors(N) rotated_boxes = torch.zeros(N, 5) rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0 rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0 rotated_boxes[:, 2] = boxes[:, 2] - boxes[:, 0] rotated_boxes[:, 3] = boxes[:, 3] - boxes[:, 1] rotated_boxes[:, 4] = torch.ones(N) * 180 err_msg = "Rotated NMS incompatible between CPU and reference implementation for IoU={}" for iou in [0.2, 0.5, 0.8]: keep_ref = self.reference_horizontal_nms(boxes, scores, iou) keep = nms_rotated(rotated_boxes, scores, iou) self.assertLessEqual(nms_edit_distance(keep, keep_ref), 1, err_msg.format(iou)) class TestScriptable(unittest.TestCase): def setUp(self): class TestingModule(torch.nn.Module): def forward(self, boxes, scores, threshold): return nms_rotated(boxes, scores, threshold) self.module = TestingModule() def test_scriptable_cpu(self): m = deepcopy(self.module).cpu() _ = torch.jit.script(m) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_scriptable_cuda(self): m = deepcopy(self.module).cuda() _ = torch.jit.script(m) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/layers/test_roi_align.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import unittest from copy import copy import cv2 import torch from fvcore.common.benchmark import benchmark from torch.nn import functional as F from detectron2.layers.roi_align import ROIAlign, roi_align class ROIAlignTest(unittest.TestCase): def test_forward_output(self): input = np.arange(25).reshape(5, 5).astype("float32") """ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 """ output = self._simple_roialign(input, [1, 1, 3, 3], (4, 4), aligned=False) output_correct = self._simple_roialign(input, [1, 1, 3, 3], (4, 4), aligned=True) # without correction: old_results = [ [7.5, 8, 8.5, 9], [10, 10.5, 11, 11.5], [12.5, 13, 13.5, 14], [15, 15.5, 16, 16.5], ] # with 0.5 correction: correct_results = [ [4.5, 5.0, 5.5, 6.0], [7.0, 7.5, 8.0, 8.5], [9.5, 10.0, 10.5, 11.0], [12.0, 12.5, 13.0, 13.5], ] # This is an upsampled version of [[6, 7], [11, 12]] self.assertTrue(np.allclose(output.flatten(), np.asarray(old_results).flatten())) self.assertTrue( np.allclose(output_correct.flatten(), np.asarray(correct_results).flatten()) ) # Also see similar issues in tensorflow at # https://github.com/tensorflow/tensorflow/issues/26278 def test_resize(self): H, W = 30, 30 input = np.random.rand(H, W).astype("float32") * 100 box = [10, 10, 20, 20] output = self._simple_roialign(input, box, (5, 5), aligned=True) input2x = cv2.resize(input, (W // 2, H // 2), interpolation=cv2.INTER_LINEAR) box2x = [x / 2 for x in box] output2x = self._simple_roialign(input2x, box2x, (5, 5), aligned=True) diff = np.abs(output2x - output) self.assertTrue(diff.max() < 1e-4) def test_grid_sample_equivalence(self): H, W = 30, 30 input = np.random.rand(H, W).astype("float32") * 100 box = [10, 10, 20, 20] for ratio in [1, 2, 3]: output = self._simple_roialign(input, box, (5, 5), sampling_ratio=ratio) output_grid_sample = grid_sample_roi_align( torch.from_numpy(input[None, None, :, :]).float(), torch.as_tensor(box).float()[None, :], 5, 1.0, ratio, ) self.assertTrue(torch.allclose(output, output_grid_sample)) def _simple_roialign(self, img, box, resolution, sampling_ratio=0, aligned=True): """ RoiAlign with scale 1.0. """ if isinstance(resolution, int): resolution = (resolution, resolution) op = ROIAlign(resolution, 1.0, sampling_ratio, aligned=aligned) input = torch.from_numpy(img[None, None, :, :].astype("float32")) rois = [0] + list(box) rois = torch.from_numpy(np.asarray(rois)[None, :].astype("float32")) output = op.forward(input, rois) if torch.cuda.is_available(): output_cuda = op.forward(input.cuda(), rois.cuda()).cpu() self.assertTrue(torch.allclose(output, output_cuda)) return output[0, 0] def _simple_roialign_with_grad(self, img, box, resolution, device): if isinstance(resolution, int): resolution = (resolution, resolution) op = ROIAlign(resolution, 1.0, 0, aligned=True) input = torch.from_numpy(img[None, None, :, :].astype("float32")) rois = [0] + list(box) rois = torch.from_numpy(np.asarray(rois)[None, :].astype("float32")) input = input.to(device=device) rois = rois.to(device=device) input.requires_grad = True output = op.forward(input, rois) return input, output def test_empty_box(self): img = np.random.rand(5, 5) box = [3, 4, 5, 4] o = self._simple_roialign(img, box, 7) self.assertTrue(o.shape == (7, 7)) self.assertTrue((o == 0).all()) for dev in ["cpu"] + ["cuda"] if torch.cuda.is_available() else []: input, output = self._simple_roialign_with_grad(img, box, 7, torch.device(dev)) output.sum().backward() self.assertTrue(torch.allclose(input.grad, torch.zeros_like(input))) def test_empty_batch(self): input = torch.zeros(0, 3, 10, 10, dtype=torch.float32) rois = torch.zeros(0, 5, dtype=torch.float32) op = ROIAlign((7, 7), 1.0, 0, aligned=True) output = op.forward(input, rois) self.assertTrue(output.shape == (0, 3, 7, 7)) def grid_sample_roi_align(input, boxes, output_size, scale, sampling_ratio): # unlike true roi_align, this does not support different batch_idx from detectron2.projects.point_rend.point_features import ( generate_regular_grid_point_coords, get_point_coords_wrt_image, point_sample, ) N, _, H, W = input.shape R = len(boxes) assert N == 1 boxes = boxes * scale grid = generate_regular_grid_point_coords(R, output_size * sampling_ratio, device=boxes.device) coords = get_point_coords_wrt_image(boxes, grid) coords = coords / torch.as_tensor([W, H], device=coords.device) # R, s^2, 2 res = point_sample(input, coords.unsqueeze(0), align_corners=False) # 1,C, R,s^2 res = ( res.squeeze(0) .permute(1, 0, 2) .reshape(R, -1, output_size * sampling_ratio, output_size * sampling_ratio) ) res = F.avg_pool2d(res, sampling_ratio) return res def benchmark_roi_align(): def random_boxes(mean_box, stdev, N, maxsize): ret = torch.rand(N, 4) * stdev + torch.tensor(mean_box, dtype=torch.float) ret.clamp_(min=0, max=maxsize) return ret def func(shape, nboxes_per_img, sampling_ratio, device, box_size="large"): N, _, H, _ = shape input = torch.rand(*shape) boxes = [] batch_idx = [] for k in range(N): if box_size == "large": b = random_boxes([80, 80, 130, 130], 24, nboxes_per_img, H) else: b = random_boxes([100, 100, 110, 110], 4, nboxes_per_img, H) boxes.append(b) batch_idx.append(torch.zeros(nboxes_per_img, 1, dtype=torch.float32) + k) boxes = torch.cat(boxes, axis=0) batch_idx = torch.cat(batch_idx, axis=0) boxes = torch.cat([batch_idx, boxes], axis=1) input = input.to(device=device) boxes = boxes.to(device=device) def bench(): if False and sampling_ratio > 0 and N == 1: # enable to benchmark grid_sample (slower) grid_sample_roi_align(input, boxes[:, 1:], 7, 1.0, sampling_ratio) else: roi_align(input, boxes, 7, 1.0, sampling_ratio, True) if device == "cuda": torch.cuda.synchronize() return bench def gen_args(arg): args = [] for size in ["small", "large"]: for ratio in [0, 2]: args.append(copy(arg)) args[-1]["sampling_ratio"] = ratio args[-1]["box_size"] = size return args arg = dict(shape=(1, 512, 256, 256), nboxes_per_img=512, device="cuda") benchmark(func, "cuda_roialign", gen_args(arg), num_iters=20, warmup_iters=1) arg.update({"device": "cpu", "shape": (1, 256, 128, 128)}) benchmark(func, "cpu_roialign", gen_args(arg), num_iters=5, warmup_iters=1) if __name__ == "__main__": if torch.cuda.is_available(): benchmark_roi_align() unittest.main() ================================================ FILE: detectron2/tests/layers/test_roi_align_rotated.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import unittest import cv2 import torch from torch.autograd import Variable, gradcheck from detectron2.layers.roi_align import ROIAlign from detectron2.layers.roi_align_rotated import ROIAlignRotated logger = logging.getLogger(__name__) class ROIAlignRotatedTest(unittest.TestCase): def _box_to_rotated_box(self, box, angle): return [ (box[0] + box[2]) / 2.0, (box[1] + box[3]) / 2.0, box[2] - box[0], box[3] - box[1], angle, ] def _rot90(self, img, num): num = num % 4 # note: -1 % 4 == 3 for _ in range(num): img = img.transpose(0, 1).flip(0) return img def test_forward_output_0_90_180_270(self): for i in range(4): # i = 0, 1, 2, 3 corresponding to 0, 90, 180, 270 degrees img = torch.arange(25, dtype=torch.float32).reshape(5, 5) """ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 """ box = [1, 1, 3, 3] rotated_box = self._box_to_rotated_box(box=box, angle=90 * i) result = self._simple_roi_align_rotated(img=img, box=rotated_box, resolution=(4, 4)) # Here's an explanation for 0 degree case: # point 0 in the original input lies at [0.5, 0.5] # (the center of bin [0, 1] x [0, 1]) # point 1 in the original input lies at [1.5, 0.5], etc. # since the resolution is (4, 4) that divides [1, 3] x [1, 3] # into 4 x 4 equal bins, # the top-left bin is [1, 1.5] x [1, 1.5], and its center # (1.25, 1.25) lies at the 3/4 position # between point 0 and point 1, point 5 and point 6, # point 0 and point 5, point 1 and point 6, so it can be calculated as # 0.25*(0*0.25+1*0.75)+(5*0.25+6*0.75)*0.75 = 4.5 result_expected = torch.tensor( [ [4.5, 5.0, 5.5, 6.0], [7.0, 7.5, 8.0, 8.5], [9.5, 10.0, 10.5, 11.0], [12.0, 12.5, 13.0, 13.5], ] ) # This is also an upsampled version of [[6, 7], [11, 12]] # When the box is rotated by 90 degrees CCW, # the result would be rotated by 90 degrees CW, thus it's -i here result_expected = self._rot90(result_expected, -i) assert torch.allclose(result, result_expected) def test_resize(self): H, W = 30, 30 input = torch.rand(H, W) * 100 box = [10, 10, 20, 20] rotated_box = self._box_to_rotated_box(box, angle=0) output = self._simple_roi_align_rotated(img=input, box=rotated_box, resolution=(5, 5)) input2x = cv2.resize(input.numpy(), (W // 2, H // 2), interpolation=cv2.INTER_LINEAR) input2x = torch.from_numpy(input2x) box2x = [x / 2 for x in box] rotated_box2x = self._box_to_rotated_box(box2x, angle=0) output2x = self._simple_roi_align_rotated(img=input2x, box=rotated_box2x, resolution=(5, 5)) assert torch.allclose(output2x, output) def _simple_roi_align_rotated(self, img, box, resolution): """ RoiAlignRotated with scale 1.0 and 0 sample ratio. """ op = ROIAlignRotated(output_size=resolution, spatial_scale=1.0, sampling_ratio=0) input = img[None, None, :, :] rois = [0] + list(box) rois = torch.tensor(rois, dtype=torch.float32)[None, :] result_cpu = op.forward(input, rois) if torch.cuda.is_available(): result_cuda = op.forward(input.cuda(), rois.cuda()) assert torch.allclose(result_cpu, result_cuda.cpu()) return result_cpu[0, 0] def test_empty_box(self): img = torch.rand(5, 5) out = self._simple_roi_align_rotated(img, [2, 3, 0, 0, 0], (7, 7)) self.assertTrue((out == 0).all()) def test_roi_align_rotated_gradcheck_cpu(self): dtype = torch.float64 device = torch.device("cpu") roi_align_rotated_op = ROIAlignRotated( output_size=(5, 5), spatial_scale=0.5, sampling_ratio=1 ).to(dtype=dtype, device=device) x = torch.rand(1, 1, 10, 10, dtype=dtype, device=device, requires_grad=True) # roi format is (batch index, x_center, y_center, width, height, angle) rois = torch.tensor( [[0, 4.5, 4.5, 9, 9, 0], [0, 2, 7, 4, 4, 0], [0, 7, 7, 4, 4, 0]], dtype=dtype, device=device, ) def func(input): return roi_align_rotated_op(input, rois) assert gradcheck(func, (x,)), "gradcheck failed for RoIAlignRotated CPU" assert gradcheck(func, (x.transpose(2, 3),)), "gradcheck failed for RoIAlignRotated CPU" @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_roi_align_rotated_gradient_cuda(self): """ Compute gradients for ROIAlignRotated with multiple bounding boxes on the GPU, and compare the result with ROIAlign """ # torch.manual_seed(123) dtype = torch.float64 device = torch.device("cuda") pool_h, pool_w = (5, 5) roi_align = ROIAlign(output_size=(pool_h, pool_w), spatial_scale=1, sampling_ratio=2).to( device=device ) roi_align_rotated = ROIAlignRotated( output_size=(pool_h, pool_w), spatial_scale=1, sampling_ratio=2 ).to(device=device) x = torch.rand(1, 1, 10, 10, dtype=dtype, device=device, requires_grad=True) # x_rotated = x.clone() won't work (will lead to grad_fun=CloneBackward)! x_rotated = Variable(x.data.clone(), requires_grad=True) # roi_rotated format is (batch index, x_center, y_center, width, height, angle) rois_rotated = torch.tensor( [[0, 4.5, 4.5, 9, 9, 0], [0, 2, 7, 4, 4, 0], [0, 7, 7, 4, 4, 0]], dtype=dtype, device=device, ) y_rotated = roi_align_rotated(x_rotated, rois_rotated) s_rotated = y_rotated.sum() s_rotated.backward() # roi format is (batch index, x1, y1, x2, y2) rois = torch.tensor( [[0, 0, 0, 9, 9], [0, 0, 5, 4, 9], [0, 5, 5, 9, 9]], dtype=dtype, device=device ) y = roi_align(x, rois) s = y.sum() s.backward() assert torch.allclose( x.grad, x_rotated.grad ), "gradients for ROIAlign and ROIAlignRotated mismatch on CUDA" if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/modeling/__init__.py ================================================ ================================================ FILE: detectron2/tests/modeling/test_anchor_generator.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import unittest import torch from detectron2.config import get_cfg from detectron2.layers import ShapeSpec from detectron2.modeling.anchor_generator import DefaultAnchorGenerator, RotatedAnchorGenerator logger = logging.getLogger(__name__) class TestAnchorGenerator(unittest.TestCase): def test_default_anchor_generator(self): cfg = get_cfg() cfg.MODEL.ANCHOR_GENERATOR.SIZES = [[32, 64]] cfg.MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS = [[0.25, 1, 4]] anchor_generator = DefaultAnchorGenerator(cfg, [ShapeSpec(stride=4)]) # only the last two dimensions of features matter here num_images = 2 features = {"stage3": torch.rand(num_images, 96, 1, 2)} anchors = anchor_generator([features["stage3"]]) expected_anchor_tensor = torch.tensor( [ [-32.0, -8.0, 32.0, 8.0], [-16.0, -16.0, 16.0, 16.0], [-8.0, -32.0, 8.0, 32.0], [-64.0, -16.0, 64.0, 16.0], [-32.0, -32.0, 32.0, 32.0], [-16.0, -64.0, 16.0, 64.0], [-28.0, -8.0, 36.0, 8.0], # -28.0 == -32.0 + STRIDE (4) [-12.0, -16.0, 20.0, 16.0], [-4.0, -32.0, 12.0, 32.0], [-60.0, -16.0, 68.0, 16.0], [-28.0, -32.0, 36.0, 32.0], [-12.0, -64.0, 20.0, 64.0], ] ) self.assertTrue(torch.allclose(anchors[0].tensor, expected_anchor_tensor)) def test_default_anchor_generator_centered(self): # test explicit args anchor_generator = DefaultAnchorGenerator( sizes=[32, 64], aspect_ratios=[0.25, 1, 4], strides=[4] ) # only the last two dimensions of features matter here num_images = 2 features = {"stage3": torch.rand(num_images, 96, 1, 2)} expected_anchor_tensor = torch.tensor( [ [-30.0, -6.0, 34.0, 10.0], [-14.0, -14.0, 18.0, 18.0], [-6.0, -30.0, 10.0, 34.0], [-62.0, -14.0, 66.0, 18.0], [-30.0, -30.0, 34.0, 34.0], [-14.0, -62.0, 18.0, 66.0], [-26.0, -6.0, 38.0, 10.0], [-10.0, -14.0, 22.0, 18.0], [-2.0, -30.0, 14.0, 34.0], [-58.0, -14.0, 70.0, 18.0], [-26.0, -30.0, 38.0, 34.0], [-10.0, -62.0, 22.0, 66.0], ] ) anchors = anchor_generator([features["stage3"]]) self.assertTrue(torch.allclose(anchors[0].tensor, expected_anchor_tensor)) anchors = torch.jit.script(anchor_generator)([features["stage3"]]) self.assertTrue(torch.allclose(anchors[0].tensor, expected_anchor_tensor)) def test_rrpn_anchor_generator(self): cfg = get_cfg() cfg.MODEL.ANCHOR_GENERATOR.SIZES = [[32, 64]] cfg.MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS = [[0.25, 1, 4]] cfg.MODEL.ANCHOR_GENERATOR.ANGLES = [0, 45] # test single list[float] anchor_generator = RotatedAnchorGenerator(cfg, [ShapeSpec(stride=4)]) # only the last two dimensions of features matter here num_images = 2 features = {"stage3": torch.rand(num_images, 96, 1, 2)} anchors = anchor_generator([features["stage3"]]) expected_anchor_tensor = torch.tensor( [ [0.0, 0.0, 64.0, 16.0, 0.0], [0.0, 0.0, 64.0, 16.0, 45.0], [0.0, 0.0, 32.0, 32.0, 0.0], [0.0, 0.0, 32.0, 32.0, 45.0], [0.0, 0.0, 16.0, 64.0, 0.0], [0.0, 0.0, 16.0, 64.0, 45.0], [0.0, 0.0, 128.0, 32.0, 0.0], [0.0, 0.0, 128.0, 32.0, 45.0], [0.0, 0.0, 64.0, 64.0, 0.0], [0.0, 0.0, 64.0, 64.0, 45.0], [0.0, 0.0, 32.0, 128.0, 0.0], [0.0, 0.0, 32.0, 128.0, 45.0], [4.0, 0.0, 64.0, 16.0, 0.0], # 4.0 == 0.0 + STRIDE (4) [4.0, 0.0, 64.0, 16.0, 45.0], [4.0, 0.0, 32.0, 32.0, 0.0], [4.0, 0.0, 32.0, 32.0, 45.0], [4.0, 0.0, 16.0, 64.0, 0.0], [4.0, 0.0, 16.0, 64.0, 45.0], [4.0, 0.0, 128.0, 32.0, 0.0], [4.0, 0.0, 128.0, 32.0, 45.0], [4.0, 0.0, 64.0, 64.0, 0.0], [4.0, 0.0, 64.0, 64.0, 45.0], [4.0, 0.0, 32.0, 128.0, 0.0], [4.0, 0.0, 32.0, 128.0, 45.0], ] ) self.assertTrue(torch.allclose(anchors[0].tensor, expected_anchor_tensor)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/modeling/test_backbone.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import unittest import torch import detectron2.export.torchscript # apply patch # noqa from detectron2 import model_zoo from detectron2.config import get_cfg from detectron2.layers import ShapeSpec from detectron2.modeling.backbone import build_resnet_backbone from detectron2.modeling.backbone.fpn import build_resnet_fpn_backbone class TestBackBone(unittest.TestCase): def test_resnet_scriptability(self): cfg = get_cfg() resnet = build_resnet_backbone(cfg, ShapeSpec(channels=3)) scripted_resnet = torch.jit.script(resnet) inp = torch.rand(2, 3, 100, 100) out1 = resnet(inp)["res4"] out2 = scripted_resnet(inp)["res4"] self.assertTrue(torch.allclose(out1, out2)) def test_fpn_scriptability(self): cfg = model_zoo.get_config("Misc/scratch_mask_rcnn_R_50_FPN_3x_gn.yaml") bb = build_resnet_fpn_backbone(cfg, ShapeSpec(channels=3)) bb_s = torch.jit.script(bb) inp = torch.rand(2, 3, 128, 128) out1 = bb(inp)["p5"] out2 = bb_s(inp)["p5"] self.assertTrue(torch.allclose(out1, out2)) ================================================ FILE: detectron2/tests/modeling/test_box2box_transform.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import unittest import torch from detectron2.modeling.box_regression import ( Box2BoxTransform, Box2BoxTransformLinear, Box2BoxTransformRotated, ) from detectron2.utils.testing import random_boxes logger = logging.getLogger(__name__) class TestBox2BoxTransform(unittest.TestCase): def test_reconstruction(self): weights = (5, 5, 10, 10) b2b_tfm = Box2BoxTransform(weights=weights) src_boxes = random_boxes(10) dst_boxes = random_boxes(10) devices = [torch.device("cpu")] if torch.cuda.is_available(): devices.append(torch.device("cuda")) for device in devices: src_boxes = src_boxes.to(device=device) dst_boxes = dst_boxes.to(device=device) deltas = b2b_tfm.get_deltas(src_boxes, dst_boxes) dst_boxes_reconstructed = b2b_tfm.apply_deltas(deltas, src_boxes) self.assertTrue(torch.allclose(dst_boxes, dst_boxes_reconstructed)) def test_apply_deltas_tracing(self): weights = (5, 5, 10, 10) b2b_tfm = Box2BoxTransform(weights=weights) with torch.no_grad(): func = torch.jit.trace(b2b_tfm.apply_deltas, (torch.randn(10, 20), torch.randn(10, 4))) o = func(torch.randn(10, 20), torch.randn(10, 4)) self.assertEqual(o.shape, (10, 20)) o = func(torch.randn(5, 20), torch.randn(5, 4)) self.assertEqual(o.shape, (5, 20)) def random_rotated_boxes(mean_box, std_length, std_angle, N): return torch.cat( [torch.rand(N, 4) * std_length, torch.rand(N, 1) * std_angle], dim=1 ) + torch.tensor(mean_box, dtype=torch.float) class TestBox2BoxTransformRotated(unittest.TestCase): def test_reconstruction(self): weights = (5, 5, 10, 10, 1) b2b_transform = Box2BoxTransformRotated(weights=weights) src_boxes = random_rotated_boxes([10, 10, 20, 20, -30], 5, 60.0, 10) dst_boxes = random_rotated_boxes([10, 10, 20, 20, -30], 5, 60.0, 10) devices = [torch.device("cpu")] if torch.cuda.is_available(): devices.append(torch.device("cuda")) for device in devices: src_boxes = src_boxes.to(device=device) dst_boxes = dst_boxes.to(device=device) deltas = b2b_transform.get_deltas(src_boxes, dst_boxes) dst_boxes_reconstructed = b2b_transform.apply_deltas(deltas, src_boxes) assert torch.allclose(dst_boxes[:, :4], dst_boxes_reconstructed[:, :4], atol=1e-5) # angle difference has to be normalized assert torch.allclose( (dst_boxes[:, 4] - dst_boxes_reconstructed[:, 4] + 180.0) % 360.0 - 180.0, torch.zeros_like(dst_boxes[:, 4]), atol=1e-4, ) class TestBox2BoxTransformLinear(unittest.TestCase): def test_reconstruction(self): b2b_tfm = Box2BoxTransformLinear() src_boxes = random_boxes(10) dst_boxes = torch.tensor([0, 0, 101, 101] * 10).reshape(10, 4).float() devices = [torch.device("cpu")] if torch.cuda.is_available(): devices.append(torch.device("cuda")) for device in devices: src_boxes = src_boxes.to(device=device) dst_boxes = dst_boxes.to(device=device) deltas = b2b_tfm.get_deltas(src_boxes, dst_boxes) dst_boxes_reconstructed = b2b_tfm.apply_deltas(deltas, src_boxes) self.assertTrue(torch.allclose(dst_boxes, dst_boxes_reconstructed, atol=1e-3)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/modeling/test_fast_rcnn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import unittest import torch from detectron2.layers import ShapeSpec from detectron2.modeling.box_regression import Box2BoxTransform, Box2BoxTransformRotated from detectron2.modeling.roi_heads.fast_rcnn import FastRCNNOutputLayers from detectron2.modeling.roi_heads.rotated_fast_rcnn import RotatedFastRCNNOutputLayers from detectron2.structures import Boxes, Instances, RotatedBoxes from detectron2.utils.events import EventStorage logger = logging.getLogger(__name__) class FastRCNNTest(unittest.TestCase): def test_fast_rcnn(self): torch.manual_seed(132) box_head_output_size = 8 box_predictor = FastRCNNOutputLayers( ShapeSpec(channels=box_head_output_size), box2box_transform=Box2BoxTransform(weights=(10, 10, 5, 5)), num_classes=5, ) feature_pooled = torch.rand(2, box_head_output_size) predictions = box_predictor(feature_pooled) proposal_boxes = torch.tensor([[0.8, 1.1, 3.2, 2.8], [2.3, 2.5, 7, 8]], dtype=torch.float32) gt_boxes = torch.tensor([[1, 1, 3, 3], [2, 2, 6, 6]], dtype=torch.float32) proposal = Instances((10, 10)) proposal.proposal_boxes = Boxes(proposal_boxes) proposal.gt_boxes = Boxes(gt_boxes) proposal.gt_classes = torch.tensor([1, 2]) with EventStorage(): # capture events in a new storage to discard them losses = box_predictor.losses(predictions, [proposal]) expected_losses = { "loss_cls": torch.tensor(1.7951188087), "loss_box_reg": torch.tensor(4.0357131958), } for name in expected_losses.keys(): assert torch.allclose(losses[name], expected_losses[name]) def test_fast_rcnn_empty_batch(self, device="cpu"): box_predictor = FastRCNNOutputLayers( ShapeSpec(channels=10), box2box_transform=Box2BoxTransform(weights=(10, 10, 5, 5)), num_classes=8, ).to(device=device) logits = torch.randn(0, 100, requires_grad=True, device=device) deltas = torch.randn(0, 4, requires_grad=True, device=device) losses = box_predictor.losses([logits, deltas], []) for value in losses.values(): self.assertTrue(torch.allclose(value, torch.zeros_like(value))) sum(losses.values()).backward() self.assertTrue(logits.grad is not None) self.assertTrue(deltas.grad is not None) predictions, _ = box_predictor.inference([logits, deltas], []) self.assertEqual(len(predictions), 0) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_fast_rcnn_empty_batch_cuda(self): self.test_fast_rcnn_empty_batch(device=torch.device("cuda")) def test_fast_rcnn_rotated(self): torch.manual_seed(132) box_head_output_size = 8 box_predictor = RotatedFastRCNNOutputLayers( ShapeSpec(channels=box_head_output_size), box2box_transform=Box2BoxTransformRotated(weights=(10, 10, 5, 5, 1)), num_classes=5, ) feature_pooled = torch.rand(2, box_head_output_size) predictions = box_predictor(feature_pooled) proposal_boxes = torch.tensor( [[2, 1.95, 2.4, 1.7, 0], [4.65, 5.25, 4.7, 5.5, 0]], dtype=torch.float32 ) gt_boxes = torch.tensor([[2, 2, 2, 2, 0], [4, 4, 4, 4, 0]], dtype=torch.float32) proposal = Instances((10, 10)) proposal.proposal_boxes = RotatedBoxes(proposal_boxes) proposal.gt_boxes = RotatedBoxes(gt_boxes) proposal.gt_classes = torch.tensor([1, 2]) with EventStorage(): # capture events in a new storage to discard them losses = box_predictor.losses(predictions, [proposal]) # Note: the expected losses are slightly different even if # the boxes are essentially the same as in the FastRCNNOutput test, because # bbox_pred in FastRCNNOutputLayers have different Linear layers/initialization # between the two cases. expected_losses = { "loss_cls": torch.tensor(1.7920907736), "loss_box_reg": torch.tensor(4.0410838127), } for name in expected_losses.keys(): assert torch.allclose(losses[name], expected_losses[name]) def test_predict_boxes_tracing(self): class Model(torch.nn.Module): def __init__(self, output_layer): super(Model, self).__init__() self._output_layer = output_layer def forward(self, proposal_deltas, proposal_boxes): instances = Instances((10, 10)) instances.proposal_boxes = Boxes(proposal_boxes) return self._output_layer.predict_boxes((None, proposal_deltas), [instances]) box_head_output_size = 8 box_predictor = FastRCNNOutputLayers( ShapeSpec(channels=box_head_output_size), box2box_transform=Box2BoxTransform(weights=(10, 10, 5, 5)), num_classes=5, ) model = Model(box_predictor) from detectron2.export.torchscript_patch import patch_builtin_len with torch.no_grad(), patch_builtin_len(): func = torch.jit.trace(model, (torch.randn(10, 20), torch.randn(10, 4))) o = func(torch.randn(10, 20), torch.randn(10, 4)) self.assertEqual(o[0].shape, (10, 20)) o = func(torch.randn(5, 20), torch.randn(5, 4)) self.assertEqual(o[0].shape, (5, 20)) o = func(torch.randn(20, 20), torch.randn(20, 4)) self.assertEqual(o[0].shape, (20, 20)) def test_predict_probs_tracing(self): class Model(torch.nn.Module): def __init__(self, output_layer): super(Model, self).__init__() self._output_layer = output_layer def forward(self, scores, proposal_boxes): instances = Instances((10, 10)) instances.proposal_boxes = Boxes(proposal_boxes) return self._output_layer.predict_probs((scores, None), [instances]) box_head_output_size = 8 box_predictor = FastRCNNOutputLayers( ShapeSpec(channels=box_head_output_size), box2box_transform=Box2BoxTransform(weights=(10, 10, 5, 5)), num_classes=5, ) model = Model(box_predictor) from detectron2.export.torchscript_patch import patch_builtin_len with torch.no_grad(), patch_builtin_len(): func = torch.jit.trace(model, (torch.randn(10, 6), torch.rand(10, 4))) o = func(torch.randn(10, 6), torch.randn(10, 4)) self.assertEqual(o[0].shape, (10, 6)) o = func(torch.randn(5, 6), torch.randn(5, 4)) self.assertEqual(o[0].shape, (5, 6)) o = func(torch.randn(20, 6), torch.randn(20, 4)) self.assertEqual(o[0].shape, (20, 6)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/modeling/test_matcher.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest from typing import List import torch from detectron2.config import get_cfg from detectron2.modeling.matcher import Matcher class TestMatcher(unittest.TestCase): def test_scriptability(self): cfg = get_cfg() anchor_matcher = Matcher( cfg.MODEL.RPN.IOU_THRESHOLDS, cfg.MODEL.RPN.IOU_LABELS, allow_low_quality_matches=True ) match_quality_matrix = torch.tensor( [[0.15, 0.45, 0.2, 0.6], [0.3, 0.65, 0.05, 0.1], [0.05, 0.4, 0.25, 0.4]] ) expected_matches = torch.tensor([1, 1, 2, 0]) expected_match_labels = torch.tensor([-1, 1, 0, 1], dtype=torch.int8) matches, match_labels = anchor_matcher(match_quality_matrix) self.assertTrue(torch.allclose(matches, expected_matches)) self.assertTrue(torch.allclose(match_labels, expected_match_labels)) # nonzero_tuple must be import explicitly to let jit know what it is. # https://github.com/pytorch/pytorch/issues/38964 from detectron2.layers import nonzero_tuple # noqa F401 def f(thresholds: List[float], labels: List[int]): return Matcher(thresholds, labels, allow_low_quality_matches=True) scripted_anchor_matcher = torch.jit.script(f)( cfg.MODEL.RPN.IOU_THRESHOLDS, cfg.MODEL.RPN.IOU_LABELS ) matches, match_labels = scripted_anchor_matcher(match_quality_matrix) self.assertTrue(torch.allclose(matches, expected_matches)) self.assertTrue(torch.allclose(match_labels, expected_match_labels)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/modeling/test_mmdet.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest from detectron2.layers import ShapeSpec from detectron2.modeling.mmdet_wrapper import MMDetBackbone, MMDetDetector try: import mmdet.models # noqa HAS_MMDET = True except ImportError: HAS_MMDET = False @unittest.skipIf(not HAS_MMDET, "mmdet not available") class TestMMDetWrapper(unittest.TestCase): def test_backbone(self): MMDetBackbone( backbone=dict( type="DetectoRS_ResNet", conv_cfg=dict(type="ConvAWS"), sac=dict(type="SAC", use_deform=True), stage_with_sac=(False, True, True, True), depth=50, num_stages=4, out_indices=(0, 1, 2, 3), frozen_stages=1, norm_cfg=dict(type="BN", requires_grad=True), norm_eval=True, style="pytorch", ), neck=dict( type="FPN", in_channels=[256, 512, 1024, 2048], out_channels=256, num_outs=5, ), # skip pretrained model for tests # pretrained_backbone="torchvision://resnet50", output_shapes=[ShapeSpec(channels=256, stride=s) for s in [4, 8, 16, 32, 64]], output_names=["p2", "p3", "p4", "p5", "p6"], ) def test_detector(self): # a basic R50 Mask R-CNN MMDetDetector( detector=dict( type="MaskRCNN", backbone=dict( type="ResNet", depth=50, num_stages=4, out_indices=(0, 1, 2, 3), frozen_stages=1, norm_cfg=dict(type="BN", requires_grad=True), norm_eval=True, style="pytorch", # skip pretrained model for tests # init_cfg=dict(type='Pretrained', checkpoint='torchvision://resnet50')) ), neck=dict( type="FPN", in_channels=[256, 512, 1024, 2048], out_channels=256, num_outs=5 ), rpn_head=dict( type="RPNHead", in_channels=256, feat_channels=256, anchor_generator=dict( type="AnchorGenerator", scales=[8], ratios=[0.5, 1.0, 2.0], strides=[4, 8, 16, 32, 64], ), bbox_coder=dict( type="DeltaXYWHBBoxCoder", target_means=[0.0, 0.0, 0.0, 0.0], target_stds=[1.0, 1.0, 1.0, 1.0], ), loss_cls=dict(type="CrossEntropyLoss", use_sigmoid=True, loss_weight=1.0), loss_bbox=dict(type="L1Loss", loss_weight=1.0), ), roi_head=dict( type="StandardRoIHead", bbox_roi_extractor=dict( type="SingleRoIExtractor", roi_layer=dict(type="RoIAlign", output_size=7, sampling_ratio=0), out_channels=256, featmap_strides=[4, 8, 16, 32], ), bbox_head=dict( type="Shared2FCBBoxHead", in_channels=256, fc_out_channels=1024, roi_feat_size=7, num_classes=80, bbox_coder=dict( type="DeltaXYWHBBoxCoder", target_means=[0.0, 0.0, 0.0, 0.0], target_stds=[0.1, 0.1, 0.2, 0.2], ), reg_class_agnostic=False, loss_cls=dict(type="CrossEntropyLoss", use_sigmoid=False, loss_weight=1.0), loss_bbox=dict(type="L1Loss", loss_weight=1.0), ), mask_roi_extractor=dict( type="SingleRoIExtractor", roi_layer=dict(type="RoIAlign", output_size=14, sampling_ratio=0), out_channels=256, featmap_strides=[4, 8, 16, 32], ), mask_head=dict( type="FCNMaskHead", num_convs=4, in_channels=256, conv_out_channels=256, num_classes=80, loss_mask=dict(type="CrossEntropyLoss", use_mask=True, loss_weight=1.0), ), ), # model training and testing settings train_cfg=dict( rpn=dict( assigner=dict( type="MaxIoUAssigner", pos_iou_thr=0.7, neg_iou_thr=0.3, min_pos_iou=0.3, match_low_quality=True, ignore_iof_thr=-1, ), sampler=dict( type="RandomSampler", num=256, pos_fraction=0.5, neg_pos_ub=-1, add_gt_as_proposals=False, ), allowed_border=-1, pos_weight=-1, debug=False, ), rpn_proposal=dict( nms_pre=2000, max_per_img=1000, nms=dict(type="nms", iou_threshold=0.7), min_bbox_size=0, ), rcnn=dict( assigner=dict( type="MaxIoUAssigner", pos_iou_thr=0.5, neg_iou_thr=0.5, min_pos_iou=0.5, match_low_quality=True, ignore_iof_thr=-1, ), sampler=dict( type="RandomSampler", num=512, pos_fraction=0.25, neg_pos_ub=-1, add_gt_as_proposals=True, ), mask_size=28, pos_weight=-1, debug=False, ), ), test_cfg=dict( rpn=dict( nms_pre=1000, max_per_img=1000, nms=dict(type="nms", iou_threshold=0.7), min_bbox_size=0, ), rcnn=dict( score_thr=0.05, nms=dict(type="nms", iou_threshold=0.5), max_per_img=100, mask_thr_binary=0.5, ), ), ), pixel_mean=[1, 2, 3], pixel_std=[1, 2, 3], ) ================================================ FILE: detectron2/tests/modeling/test_model_e2e.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import itertools import unittest from contextlib import contextmanager from copy import deepcopy import torch from detectron2.structures import BitMasks, Boxes, ImageList, Instances from detectron2.utils.events import EventStorage from detectron2.utils.testing import get_model_no_weights @contextmanager def typecheck_hook(model, *, in_dtype=None, out_dtype=None): """ Check that the model must be called with the given input/output dtype """ if not isinstance(in_dtype, set): in_dtype = {in_dtype} if not isinstance(out_dtype, set): out_dtype = {out_dtype} def flatten(x): if isinstance(x, torch.Tensor): return [x] if isinstance(x, (list, tuple)): return list(itertools.chain(*[flatten(t) for t in x])) if isinstance(x, dict): return flatten(list(x.values())) return [] def hook(module, input, output): if in_dtype is not None: dtypes = {x.dtype for x in flatten(input)} assert ( dtypes == in_dtype ), f"Expected input dtype of {type(module)} is {in_dtype}. Got {dtypes} instead!" if out_dtype is not None: dtypes = {x.dtype for x in flatten(output)} assert ( dtypes == out_dtype ), f"Expected output dtype of {type(module)} is {out_dtype}. Got {dtypes} instead!" with model.register_forward_hook(hook): yield def create_model_input(img, inst=None): if inst is not None: return {"image": img, "instances": inst} else: return {"image": img} def get_empty_instance(h, w): inst = Instances((h, w)) inst.gt_boxes = Boxes(torch.rand(0, 4)) inst.gt_classes = torch.tensor([]).to(dtype=torch.int64) inst.gt_masks = BitMasks(torch.rand(0, h, w)) return inst def get_regular_bitmask_instances(h, w): inst = Instances((h, w)) inst.gt_boxes = Boxes(torch.rand(3, 4)) inst.gt_boxes.tensor[:, 2:] += inst.gt_boxes.tensor[:, :2] inst.gt_classes = torch.tensor([3, 4, 5]).to(dtype=torch.int64) inst.gt_masks = BitMasks((torch.rand(3, h, w) > 0.5)) return inst class InstanceModelE2ETest: def setUp(self): torch.manual_seed(43) self.model = get_model_no_weights(self.CONFIG_PATH) def _test_eval(self, input_sizes): inputs = [create_model_input(torch.rand(3, s[0], s[1])) for s in input_sizes] self.model.eval() self.model(inputs) def _test_train(self, input_sizes, instances): assert len(input_sizes) == len(instances) inputs = [ create_model_input(torch.rand(3, s[0], s[1]), inst) for s, inst in zip(input_sizes, instances) ] self.model.train() with EventStorage(): losses = self.model(inputs) sum(losses.values()).backward() del losses def _inf_tensor(self, *shape): return 1.0 / torch.zeros(*shape, device=self.model.device) def _nan_tensor(self, *shape): return torch.zeros(*shape, device=self.model.device).fill_(float("nan")) def test_empty_data(self): instances = [get_empty_instance(200, 250), get_empty_instance(200, 249)] self._test_eval([(200, 250), (200, 249)]) self._test_train([(200, 250), (200, 249)], instances) @unittest.skipIf(not torch.cuda.is_available(), "CUDA unavailable") def test_eval_tocpu(self): model = deepcopy(self.model).cpu() model.eval() input_sizes = [(200, 250), (200, 249)] inputs = [create_model_input(torch.rand(3, s[0], s[1])) for s in input_sizes] model(inputs) class MaskRCNNE2ETest(InstanceModelE2ETest, unittest.TestCase): CONFIG_PATH = "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml" def test_half_empty_data(self): instances = [get_empty_instance(200, 250), get_regular_bitmask_instances(200, 249)] self._test_train([(200, 250), (200, 249)], instances) # This test is flaky because in some environment the output features are zero due to relu # def test_rpn_inf_nan_data(self): # self.model.eval() # for tensor in [self._inf_tensor, self._nan_tensor]: # images = ImageList(tensor(1, 3, 512, 512), [(510, 510)]) # features = { # "p2": tensor(1, 256, 256, 256), # "p3": tensor(1, 256, 128, 128), # "p4": tensor(1, 256, 64, 64), # "p5": tensor(1, 256, 32, 32), # "p6": tensor(1, 256, 16, 16), # } # props, _ = self.model.proposal_generator(images, features) # self.assertEqual(len(props[0]), 0) def test_roiheads_inf_nan_data(self): self.model.eval() for tensor in [self._inf_tensor, self._nan_tensor]: images = ImageList(tensor(1, 3, 512, 512), [(510, 510)]) features = { "p2": tensor(1, 256, 256, 256), "p3": tensor(1, 256, 128, 128), "p4": tensor(1, 256, 64, 64), "p5": tensor(1, 256, 32, 32), "p6": tensor(1, 256, 16, 16), } props = [Instances((510, 510))] props[0].proposal_boxes = Boxes([[10, 10, 20, 20]]).to(device=self.model.device) props[0].objectness_logits = torch.tensor([1.0]).reshape(1, 1) det, _ = self.model.roi_heads(images, features, props) self.assertEqual(len(det[0]), 0) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_autocast(self): from torch.cuda.amp import autocast inputs = [{"image": torch.rand(3, 100, 100)}] self.model.eval() with autocast(), typecheck_hook( self.model.backbone, in_dtype=torch.float32, out_dtype=torch.float16 ), typecheck_hook( self.model.roi_heads.box_predictor, in_dtype=torch.float16, out_dtype=torch.float16 ): out = self.model.inference(inputs, do_postprocess=False)[0] self.assertEqual(out.pred_boxes.tensor.dtype, torch.float32) self.assertEqual(out.pred_masks.dtype, torch.float16) self.assertEqual(out.scores.dtype, torch.float32) # scores comes from softmax class RetinaNetE2ETest(InstanceModelE2ETest, unittest.TestCase): CONFIG_PATH = "COCO-Detection/retinanet_R_50_FPN_1x.yaml" def test_inf_nan_data(self): self.model.eval() self.model.score_threshold = -999999999 for tensor in [self._inf_tensor, self._nan_tensor]: images = ImageList(tensor(1, 3, 512, 512), [(510, 510)]) features = [ tensor(1, 256, 128, 128), tensor(1, 256, 64, 64), tensor(1, 256, 32, 32), tensor(1, 256, 16, 16), tensor(1, 256, 8, 8), ] pred_logits, pred_anchor_deltas = self.model.head(features) pred_logits = [tensor(*x.shape) for x in pred_logits] pred_anchor_deltas = [tensor(*x.shape) for x in pred_anchor_deltas] det = self.model.forward_inference(images, features, [pred_logits, pred_anchor_deltas]) # all predictions (if any) are infinite or nan if len(det[0]): self.assertTrue(torch.isfinite(det[0].pred_boxes.tensor).sum() == 0) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_autocast(self): from torch.cuda.amp import autocast inputs = [{"image": torch.rand(3, 100, 100)}] self.model.eval() with autocast(), typecheck_hook( self.model.backbone, in_dtype=torch.float32, out_dtype=torch.float16 ), typecheck_hook(self.model.head, in_dtype=torch.float16, out_dtype=torch.float16): out = self.model(inputs)[0]["instances"] self.assertEqual(out.pred_boxes.tensor.dtype, torch.float32) self.assertEqual(out.scores.dtype, torch.float16) class FCOSE2ETest(InstanceModelE2ETest, unittest.TestCase): CONFIG_PATH = "COCO-Detection/fcos_R_50_FPN_1x.py" class SemSegE2ETest(unittest.TestCase): CONFIG_PATH = "Misc/semantic_R_50_FPN_1x.yaml" def setUp(self): torch.manual_seed(43) self.model = get_model_no_weights(self.CONFIG_PATH) def _test_eval(self, input_sizes): inputs = [create_model_input(torch.rand(3, s[0], s[1])) for s in input_sizes] self.model.eval() self.model(inputs) def test_forward(self): self._test_eval([(200, 250), (200, 249)]) ================================================ FILE: detectron2/tests/modeling/test_roi_heads.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import unittest from copy import deepcopy import torch from torch import nn from detectron2 import model_zoo from detectron2.config import get_cfg from detectron2.export.torchscript_patch import ( freeze_training_mode, patch_builtin_len, patch_instances, ) from detectron2.layers import ShapeSpec from detectron2.modeling.proposal_generator.build import build_proposal_generator from detectron2.modeling.roi_heads import ( FastRCNNConvFCHead, KRCNNConvDeconvUpsampleHead, MaskRCNNConvUpsampleHead, StandardROIHeads, build_roi_heads, ) from detectron2.projects import point_rend from detectron2.structures import BitMasks, Boxes, ImageList, Instances, RotatedBoxes from detectron2.utils.events import EventStorage from detectron2.utils.testing import assert_instances_allclose, random_boxes logger = logging.getLogger(__name__) """ Make sure the losses of ROIHeads/RPN do not change, to avoid breaking the forward logic by mistake. This relies on assumption that pytorch's RNG is stable. """ class ROIHeadsTest(unittest.TestCase): def test_roi_heads(self): torch.manual_seed(121) cfg = get_cfg() cfg.MODEL.ROI_BOX_HEAD.NAME = "FastRCNNConvFCHead" cfg.MODEL.ROI_BOX_HEAD.NUM_FC = 2 cfg.MODEL.ROI_BOX_HEAD.POOLER_TYPE = "ROIAlignV2" cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS = (10, 10, 5, 5) cfg.MODEL.MASK_ON = True num_images = 2 images_tensor = torch.rand(num_images, 20, 30) image_sizes = [(10, 10), (20, 30)] images = ImageList(images_tensor, image_sizes) num_channels = 1024 features = {"res4": torch.rand(num_images, num_channels, 1, 2)} feature_shape = {"res4": ShapeSpec(channels=num_channels, stride=16)} image_shape = (15, 15) gt_boxes0 = torch.tensor([[1, 1, 3, 3], [2, 2, 6, 6]], dtype=torch.float32) gt_instance0 = Instances(image_shape) gt_instance0.gt_boxes = Boxes(gt_boxes0) gt_instance0.gt_classes = torch.tensor([2, 1]) gt_instance0.gt_masks = BitMasks(torch.rand((2,) + image_shape) > 0.5) gt_boxes1 = torch.tensor([[1, 5, 2, 8], [7, 3, 10, 5]], dtype=torch.float32) gt_instance1 = Instances(image_shape) gt_instance1.gt_boxes = Boxes(gt_boxes1) gt_instance1.gt_classes = torch.tensor([1, 2]) gt_instance1.gt_masks = BitMasks(torch.rand((2,) + image_shape) > 0.5) gt_instances = [gt_instance0, gt_instance1] proposal_generator = build_proposal_generator(cfg, feature_shape) roi_heads = StandardROIHeads(cfg, feature_shape) with EventStorage(): # capture events in a new storage to discard them proposals, proposal_losses = proposal_generator(images, features, gt_instances) _, detector_losses = roi_heads(images, features, proposals, gt_instances) detector_losses.update(proposal_losses) expected_losses = { "loss_cls": 4.5253729820251465, "loss_box_reg": 0.009785720147192478, "loss_mask": 0.693184494972229, "loss_rpn_cls": 0.08186662942171097, "loss_rpn_loc": 0.1104838103055954, } succ = all( torch.allclose(detector_losses[name], torch.tensor(expected_losses.get(name, 0.0))) for name in detector_losses.keys() ) self.assertTrue( succ, "Losses has changed! New losses: {}".format( {k: v.item() for k, v in detector_losses.items()} ), ) def test_rroi_heads(self): torch.manual_seed(121) cfg = get_cfg() cfg.MODEL.PROPOSAL_GENERATOR.NAME = "RRPN" cfg.MODEL.ANCHOR_GENERATOR.NAME = "RotatedAnchorGenerator" cfg.MODEL.ROI_HEADS.NAME = "RROIHeads" cfg.MODEL.ROI_BOX_HEAD.NAME = "FastRCNNConvFCHead" cfg.MODEL.ROI_BOX_HEAD.NUM_FC = 2 cfg.MODEL.RPN.BBOX_REG_WEIGHTS = (1, 1, 1, 1, 1) cfg.MODEL.RPN.HEAD_NAME = "StandardRPNHead" cfg.MODEL.ROI_BOX_HEAD.POOLER_TYPE = "ROIAlignRotated" cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS = (10, 10, 5, 5, 1) num_images = 2 images_tensor = torch.rand(num_images, 20, 30) image_sizes = [(10, 10), (20, 30)] images = ImageList(images_tensor, image_sizes) num_channels = 1024 features = {"res4": torch.rand(num_images, num_channels, 1, 2)} feature_shape = {"res4": ShapeSpec(channels=num_channels, stride=16)} image_shape = (15, 15) gt_boxes0 = torch.tensor([[2, 2, 2, 2, 30], [4, 4, 4, 4, 0]], dtype=torch.float32) gt_instance0 = Instances(image_shape) gt_instance0.gt_boxes = RotatedBoxes(gt_boxes0) gt_instance0.gt_classes = torch.tensor([2, 1]) gt_boxes1 = torch.tensor([[1.5, 5.5, 1, 3, 0], [8.5, 4, 3, 2, -50]], dtype=torch.float32) gt_instance1 = Instances(image_shape) gt_instance1.gt_boxes = RotatedBoxes(gt_boxes1) gt_instance1.gt_classes = torch.tensor([1, 2]) gt_instances = [gt_instance0, gt_instance1] proposal_generator = build_proposal_generator(cfg, feature_shape) roi_heads = build_roi_heads(cfg, feature_shape) with EventStorage(): # capture events in a new storage to discard them proposals, proposal_losses = proposal_generator(images, features, gt_instances) _, detector_losses = roi_heads(images, features, proposals, gt_instances) detector_losses.update(proposal_losses) expected_losses = { "loss_cls": 4.365657806396484, "loss_box_reg": 0.0015851043863222003, "loss_rpn_cls": 0.2427729219198227, "loss_rpn_loc": 0.3646621108055115, } succ = all( torch.allclose(detector_losses[name], torch.tensor(expected_losses.get(name, 0.0))) for name in detector_losses.keys() ) self.assertTrue( succ, "Losses has changed! New losses: {}".format( {k: v.item() for k, v in detector_losses.items()} ), ) def test_box_head_scriptability(self): input_shape = ShapeSpec(channels=1024, height=14, width=14) box_features = torch.randn(4, 1024, 14, 14) box_head = FastRCNNConvFCHead( input_shape, conv_dims=[512, 512], fc_dims=[1024, 1024] ).eval() script_box_head = torch.jit.script(box_head) origin_output = box_head(box_features) script_output = script_box_head(box_features) self.assertTrue(torch.equal(origin_output, script_output)) def test_mask_head_scriptability(self): input_shape = ShapeSpec(channels=1024) mask_features = torch.randn(4, 1024, 14, 14) image_shapes = [(10, 10), (15, 15)] pred_instance0 = Instances(image_shapes[0]) pred_classes0 = torch.tensor([1, 2, 3], dtype=torch.int64) pred_instance0.pred_classes = pred_classes0 pred_instance1 = Instances(image_shapes[1]) pred_classes1 = torch.tensor([4], dtype=torch.int64) pred_instance1.pred_classes = pred_classes1 mask_head = MaskRCNNConvUpsampleHead( input_shape, num_classes=80, conv_dims=[256, 256] ).eval() # pred_instance will be in-place changed during the inference # process of `MaskRCNNConvUpsampleHead` origin_outputs = mask_head(mask_features, deepcopy([pred_instance0, pred_instance1])) fields = {"pred_masks": torch.Tensor, "pred_classes": torch.Tensor} with freeze_training_mode(mask_head), patch_instances(fields) as NewInstances: sciript_mask_head = torch.jit.script(mask_head) pred_instance0 = NewInstances.from_instances(pred_instance0) pred_instance1 = NewInstances.from_instances(pred_instance1) script_outputs = sciript_mask_head(mask_features, [pred_instance0, pred_instance1]) for origin_ins, script_ins in zip(origin_outputs, script_outputs): assert_instances_allclose(origin_ins, script_ins, rtol=0) def test_keypoint_head_scriptability(self): input_shape = ShapeSpec(channels=1024, height=14, width=14) keypoint_features = torch.randn(4, 1024, 14, 14) image_shapes = [(10, 10), (15, 15)] pred_boxes0 = torch.tensor([[1, 1, 3, 3], [2, 2, 6, 6], [1, 5, 2, 8]], dtype=torch.float32) pred_instance0 = Instances(image_shapes[0]) pred_instance0.pred_boxes = Boxes(pred_boxes0) pred_boxes1 = torch.tensor([[7, 3, 10, 5]], dtype=torch.float32) pred_instance1 = Instances(image_shapes[1]) pred_instance1.pred_boxes = Boxes(pred_boxes1) keypoint_head = KRCNNConvDeconvUpsampleHead( input_shape, num_keypoints=17, conv_dims=[512, 512] ).eval() origin_outputs = keypoint_head( keypoint_features, deepcopy([pred_instance0, pred_instance1]) ) fields = { "pred_boxes": Boxes, "pred_keypoints": torch.Tensor, "pred_keypoint_heatmaps": torch.Tensor, } with freeze_training_mode(keypoint_head), patch_instances(fields) as NewInstances: script_keypoint_head = torch.jit.script(keypoint_head) pred_instance0 = NewInstances.from_instances(pred_instance0) pred_instance1 = NewInstances.from_instances(pred_instance1) script_outputs = script_keypoint_head( keypoint_features, [pred_instance0, pred_instance1] ) for origin_ins, script_ins in zip(origin_outputs, script_outputs): assert_instances_allclose(origin_ins, script_ins, rtol=0) def test_StandardROIHeads_scriptability(self): cfg = get_cfg() cfg.MODEL.ROI_BOX_HEAD.NAME = "FastRCNNConvFCHead" cfg.MODEL.ROI_BOX_HEAD.NUM_FC = 2 cfg.MODEL.ROI_BOX_HEAD.POOLER_TYPE = "ROIAlignV2" cfg.MODEL.ROI_BOX_HEAD.BBOX_REG_WEIGHTS = (10, 10, 5, 5) cfg.MODEL.MASK_ON = True cfg.MODEL.ROI_HEADS.NMS_THRESH_TEST = 0.01 cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = 0.01 num_images = 2 images_tensor = torch.rand(num_images, 20, 30) image_sizes = [(10, 10), (20, 30)] images = ImageList(images_tensor, image_sizes) num_channels = 1024 features = {"res4": torch.rand(num_images, num_channels, 1, 2)} feature_shape = {"res4": ShapeSpec(channels=num_channels, stride=16)} roi_heads = StandardROIHeads(cfg, feature_shape).eval() proposal0 = Instances(image_sizes[0]) proposal_boxes0 = torch.tensor([[1, 1, 3, 3], [2, 2, 6, 6]], dtype=torch.float32) proposal0.proposal_boxes = Boxes(proposal_boxes0) proposal0.objectness_logits = torch.tensor([0.5, 0.7], dtype=torch.float32) proposal1 = Instances(image_sizes[1]) proposal_boxes1 = torch.tensor([[1, 5, 2, 8], [7, 3, 10, 5]], dtype=torch.float32) proposal1.proposal_boxes = Boxes(proposal_boxes1) proposal1.objectness_logits = torch.tensor([0.1, 0.9], dtype=torch.float32) proposals = [proposal0, proposal1] pred_instances, _ = roi_heads(images, features, proposals) fields = { "objectness_logits": torch.Tensor, "proposal_boxes": Boxes, "pred_classes": torch.Tensor, "scores": torch.Tensor, "pred_masks": torch.Tensor, "pred_boxes": Boxes, "pred_keypoints": torch.Tensor, "pred_keypoint_heatmaps": torch.Tensor, } with freeze_training_mode(roi_heads), patch_instances(fields) as new_instances: proposal0 = new_instances.from_instances(proposal0) proposal1 = new_instances.from_instances(proposal1) proposals = [proposal0, proposal1] scripted_rot_heads = torch.jit.script(roi_heads) scripted_pred_instances, _ = scripted_rot_heads(images, features, proposals) for instance, scripted_instance in zip(pred_instances, scripted_pred_instances): assert_instances_allclose(instance, scripted_instance, rtol=0) def test_PointRend_mask_head_tracing(self): cfg = model_zoo.get_config("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml") point_rend.add_pointrend_config(cfg) cfg.MODEL.ROI_HEADS.IN_FEATURES = ["p2", "p3"] cfg.MODEL.ROI_MASK_HEAD.NAME = "PointRendMaskHead" cfg.MODEL.ROI_MASK_HEAD.POOLER_TYPE = "" cfg.MODEL.ROI_MASK_HEAD.POINT_HEAD_ON = True chan = 256 head = point_rend.PointRendMaskHead( cfg, { "p2": ShapeSpec(channels=chan, stride=4), "p3": ShapeSpec(channels=chan, stride=8), }, ) def gen_inputs(h, w, N): p2 = torch.rand(1, chan, h, w) p3 = torch.rand(1, chan, h // 2, w // 2) boxes = random_boxes(N, max_coord=h) return p2, p3, boxes class Wrap(nn.ModuleDict): def forward(self, p2, p3, boxes): features = { "p2": p2, "p3": p3, } inst = Instances((p2.shape[2] * 4, p2.shape[3] * 4)) inst.pred_boxes = Boxes(boxes) inst.pred_classes = torch.zeros(inst.__len__(), dtype=torch.long) out = self.head(features, [inst])[0] return out.pred_masks model = Wrap({"head": head}) model.eval() with torch.no_grad(), patch_builtin_len(): traced = torch.jit.trace(model, gen_inputs(302, 208, 20)) inputs = gen_inputs(100, 120, 30) out_eager = model(*inputs) out_trace = traced(*inputs) self.assertTrue(torch.allclose(out_eager, out_trace)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/modeling/test_roi_pooler.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import unittest import torch from detectron2.modeling.poolers import ROIPooler from detectron2.structures import Boxes, RotatedBoxes from detectron2.utils.testing import random_boxes logger = logging.getLogger(__name__) class TestROIPooler(unittest.TestCase): def _test_roialignv2_roialignrotated_match(self, device): pooler_resolution = 14 canonical_level = 4 canonical_scale_factor = 2**canonical_level pooler_scales = (1.0 / canonical_scale_factor,) sampling_ratio = 0 N, C, H, W = 2, 4, 10, 8 N_rois = 10 std = 11 mean = 0 feature = (torch.rand(N, C, H, W) - 0.5) * 2 * std + mean features = [feature.to(device)] rois = [] rois_rotated = [] for _ in range(N): boxes = random_boxes(N_rois, W * canonical_scale_factor) rotated_boxes = torch.zeros(N_rois, 5) rotated_boxes[:, 0] = (boxes[:, 0] + boxes[:, 2]) / 2.0 rotated_boxes[:, 1] = (boxes[:, 1] + boxes[:, 3]) / 2.0 rotated_boxes[:, 2] = boxes[:, 2] - boxes[:, 0] rotated_boxes[:, 3] = boxes[:, 3] - boxes[:, 1] rois.append(Boxes(boxes).to(device)) rois_rotated.append(RotatedBoxes(rotated_boxes).to(device)) roialignv2_pooler = ROIPooler( output_size=pooler_resolution, scales=pooler_scales, sampling_ratio=sampling_ratio, pooler_type="ROIAlignV2", ) roialignv2_out = roialignv2_pooler(features, rois) roialignrotated_pooler = ROIPooler( output_size=pooler_resolution, scales=pooler_scales, sampling_ratio=sampling_ratio, pooler_type="ROIAlignRotated", ) roialignrotated_out = roialignrotated_pooler(features, rois_rotated) self.assertTrue(torch.allclose(roialignv2_out, roialignrotated_out, atol=1e-4)) def test_roialignv2_roialignrotated_match_cpu(self): self._test_roialignv2_roialignrotated_match(device="cpu") @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_roialignv2_roialignrotated_match_cuda(self): self._test_roialignv2_roialignrotated_match(device="cuda") def _test_scriptability(self, device): pooler_resolution = 14 canonical_level = 4 canonical_scale_factor = 2**canonical_level pooler_scales = (1.0 / canonical_scale_factor,) sampling_ratio = 0 N, C, H, W = 2, 4, 10, 8 N_rois = 10 std = 11 mean = 0 feature = (torch.rand(N, C, H, W) - 0.5) * 2 * std + mean features = [feature.to(device)] rois = [] for _ in range(N): boxes = random_boxes(N_rois, W * canonical_scale_factor) rois.append(Boxes(boxes).to(device)) roialignv2_pooler = ROIPooler( output_size=pooler_resolution, scales=pooler_scales, sampling_ratio=sampling_ratio, pooler_type="ROIAlignV2", ) roialignv2_out = roialignv2_pooler(features, rois) scripted_roialignv2_out = torch.jit.script(roialignv2_pooler)(features, rois) self.assertTrue(torch.equal(roialignv2_out, scripted_roialignv2_out)) def test_scriptability_cpu(self): self._test_scriptability(device="cpu") @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_scriptability_gpu(self): self._test_scriptability(device="cuda") def test_no_images(self): N, C, H, W = 0, 32, 32, 32 feature = torch.rand(N, C, H, W) - 0.5 features = [feature] pooler = ROIPooler( output_size=14, scales=(1.0,), sampling_ratio=0.0, pooler_type="ROIAlignV2" ) output = pooler.forward(features, []) self.assertEqual(output.shape, (0, C, 14, 14)) def test_roi_pooler_tracing(self): class Model(torch.nn.Module): def __init__(self, roi): super(Model, self).__init__() self.roi = roi def forward(self, x, boxes): return self.roi(x, [Boxes(boxes)]) pooler_resolution = 14 canonical_level = 4 canonical_scale_factor = 2**canonical_level pooler_scales = (1.0 / canonical_scale_factor, 0.5 / canonical_scale_factor) sampling_ratio = 0 N, C, H, W = 1, 4, 10, 8 N_rois = 10 std = 11 mean = 0 feature = (torch.rand(N, C, H, W) - 0.5) * 2 * std + mean feature = [feature, feature] rois = random_boxes(N_rois, W * canonical_scale_factor) # Add one larger box so that this level has only one box. # This may trigger the bug https://github.com/pytorch/pytorch/issues/49852 # that we shall workaround. rois = torch.cat([rois, torch.tensor([[0, 0, 448, 448]])]) model = Model( ROIPooler( output_size=pooler_resolution, scales=pooler_scales, sampling_ratio=sampling_ratio, pooler_type="ROIAlign", ) ) with torch.no_grad(): func = torch.jit.trace(model, (feature, rois)) o = func(feature, rois) self.assertEqual(o.shape, (11, 4, 14, 14)) o = func(feature, rois[:5]) self.assertEqual(o.shape, (5, 4, 14, 14)) o = func(feature, random_boxes(20, W * canonical_scale_factor)) self.assertEqual(o.shape, (20, 4, 14, 14)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/modeling/test_rpn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import unittest import torch from detectron2.config import get_cfg from detectron2.export import scripting_with_instances from detectron2.layers import ShapeSpec from detectron2.modeling.backbone import build_backbone from detectron2.modeling.proposal_generator import RPN, build_proposal_generator from detectron2.modeling.proposal_generator.proposal_utils import ( add_ground_truth_to_proposals, find_top_rpn_proposals, ) from detectron2.structures import Boxes, ImageList, Instances, RotatedBoxes from detectron2.utils.events import EventStorage logger = logging.getLogger(__name__) class RPNTest(unittest.TestCase): def get_gt_and_features(self): num_images = 2 images_tensor = torch.rand(num_images, 20, 30) image_sizes = [(10, 10), (20, 30)] images = ImageList(images_tensor, image_sizes) image_shape = (15, 15) num_channels = 1024 features = {"res4": torch.rand(num_images, num_channels, 1, 2)} gt_boxes = torch.tensor([[1, 1, 3, 3], [2, 2, 6, 6]], dtype=torch.float32) gt_instances = Instances(image_shape) gt_instances.gt_boxes = Boxes(gt_boxes) return (gt_instances, features, images, image_sizes) def test_rpn(self): torch.manual_seed(121) cfg = get_cfg() backbone = build_backbone(cfg) proposal_generator = RPN(cfg, backbone.output_shape()) (gt_instances, features, images, image_sizes) = self.get_gt_and_features() with EventStorage(): # capture events in a new storage to discard them proposals, proposal_losses = proposal_generator( images, features, [gt_instances[0], gt_instances[1]] ) expected_losses = { "loss_rpn_cls": torch.tensor(0.08011703193), "loss_rpn_loc": torch.tensor(0.101470276), } for name in expected_losses.keys(): err_msg = "proposal_losses[{}] = {}, expected losses = {}".format( name, proposal_losses[name], expected_losses[name] ) self.assertTrue(torch.allclose(proposal_losses[name], expected_losses[name]), err_msg) self.assertEqual(len(proposals), len(image_sizes)) for proposal, im_size in zip(proposals, image_sizes): self.assertEqual(proposal.image_size, im_size) expected_proposal_box = torch.tensor([[0, 0, 10, 10], [7.2702, 0, 10, 10]]) expected_objectness_logit = torch.tensor([0.1596, -0.0007]) self.assertTrue( torch.allclose(proposals[0].proposal_boxes.tensor, expected_proposal_box, atol=1e-4) ) self.assertTrue( torch.allclose(proposals[0].objectness_logits, expected_objectness_logit, atol=1e-4) ) def verify_rpn(self, conv_dims, expected_conv_dims): torch.manual_seed(121) cfg = get_cfg() cfg.MODEL.RPN.CONV_DIMS = conv_dims backbone = build_backbone(cfg) proposal_generator = RPN(cfg, backbone.output_shape()) for k, conv in enumerate(proposal_generator.rpn_head.conv): self.assertEqual(expected_conv_dims[k], conv.out_channels) return proposal_generator def test_rpn_larger_num_convs(self): conv_dims = [64, 64, 64, 64, 64] proposal_generator = self.verify_rpn(conv_dims, conv_dims) (gt_instances, features, images, image_sizes) = self.get_gt_and_features() with EventStorage(): # capture events in a new storage to discard them proposals, proposal_losses = proposal_generator( images, features, [gt_instances[0], gt_instances[1]] ) expected_losses = { "loss_rpn_cls": torch.tensor(0.08122821152), "loss_rpn_loc": torch.tensor(0.10064548254), } for name in expected_losses.keys(): err_msg = "proposal_losses[{}] = {}, expected losses = {}".format( name, proposal_losses[name], expected_losses[name] ) self.assertTrue(torch.allclose(proposal_losses[name], expected_losses[name]), err_msg) def test_rpn_conv_dims_not_set(self): conv_dims = [-1, -1, -1] expected_conv_dims = [1024, 1024, 1024] self.verify_rpn(conv_dims, expected_conv_dims) def test_rpn_scriptability(self): cfg = get_cfg() proposal_generator = RPN(cfg, {"res4": ShapeSpec(channels=1024, stride=16)}).eval() num_images = 2 images_tensor = torch.rand(num_images, 30, 40) image_sizes = [(32, 32), (30, 40)] images = ImageList(images_tensor, image_sizes) features = {"res4": torch.rand(num_images, 1024, 1, 2)} fields = {"proposal_boxes": Boxes, "objectness_logits": torch.Tensor} proposal_generator_ts = scripting_with_instances(proposal_generator, fields) proposals, _ = proposal_generator(images, features) proposals_ts, _ = proposal_generator_ts(images, features) for proposal, proposal_ts in zip(proposals, proposals_ts): self.assertEqual(proposal.image_size, proposal_ts.image_size) self.assertTrue( torch.equal(proposal.proposal_boxes.tensor, proposal_ts.proposal_boxes.tensor) ) self.assertTrue(torch.equal(proposal.objectness_logits, proposal_ts.objectness_logits)) def test_rrpn(self): torch.manual_seed(121) cfg = get_cfg() cfg.MODEL.PROPOSAL_GENERATOR.NAME = "RRPN" cfg.MODEL.ANCHOR_GENERATOR.NAME = "RotatedAnchorGenerator" cfg.MODEL.ANCHOR_GENERATOR.SIZES = [[32, 64]] cfg.MODEL.ANCHOR_GENERATOR.ASPECT_RATIOS = [[0.25, 1]] cfg.MODEL.ANCHOR_GENERATOR.ANGLES = [[0, 60]] cfg.MODEL.RPN.BBOX_REG_WEIGHTS = (1, 1, 1, 1, 1) cfg.MODEL.RPN.HEAD_NAME = "StandardRPNHead" backbone = build_backbone(cfg) proposal_generator = build_proposal_generator(cfg, backbone.output_shape()) num_images = 2 images_tensor = torch.rand(num_images, 20, 30) image_sizes = [(10, 10), (20, 30)] images = ImageList(images_tensor, image_sizes) image_shape = (15, 15) num_channels = 1024 features = {"res4": torch.rand(num_images, num_channels, 1, 2)} gt_boxes = torch.tensor([[2, 2, 2, 2, 0], [4, 4, 4, 4, 0]], dtype=torch.float32) gt_instances = Instances(image_shape) gt_instances.gt_boxes = RotatedBoxes(gt_boxes) with EventStorage(): # capture events in a new storage to discard them proposals, proposal_losses = proposal_generator( images, features, [gt_instances[0], gt_instances[1]] ) expected_losses = { "loss_rpn_cls": torch.tensor(0.04291602224), "loss_rpn_loc": torch.tensor(0.145077362), } for name in expected_losses.keys(): err_msg = "proposal_losses[{}] = {}, expected losses = {}".format( name, proposal_losses[name], expected_losses[name] ) self.assertTrue(torch.allclose(proposal_losses[name], expected_losses[name]), err_msg) expected_proposal_box = torch.tensor( [ [-1.77999556, 0.78155339, 68.04367828, 14.78156471, 60.59333801], [13.82740974, -1.50282836, 34.67269897, 29.19676590, -3.81942749], [8.10392570, -0.99071521, 145.39100647, 32.13126373, 3.67242432], [5.00000000, 4.57370186, 10.00000000, 9.14740372, 0.89196777], ] ) expected_objectness_logit = torch.tensor([0.10924313, 0.09881870, 0.07649877, 0.05858029]) torch.set_printoptions(precision=8, sci_mode=False) self.assertEqual(len(proposals), len(image_sizes)) proposal = proposals[0] # It seems that there's some randomness in the result across different machines: # This test can be run on a local machine for 100 times with exactly the same result, # However, a different machine might produce slightly different results, # thus the atol here. err_msg = "computed proposal boxes = {}, expected {}".format( proposal.proposal_boxes.tensor, expected_proposal_box ) self.assertTrue( torch.allclose(proposal.proposal_boxes.tensor[:4], expected_proposal_box, atol=1e-5), err_msg, ) err_msg = "computed objectness logits = {}, expected {}".format( proposal.objectness_logits, expected_objectness_logit ) self.assertTrue( torch.allclose(proposal.objectness_logits[:4], expected_objectness_logit, atol=1e-5), err_msg, ) def test_find_rpn_proposals_inf(self): N, Hi, Wi, A = 3, 3, 3, 3 proposals = [torch.rand(N, Hi * Wi * A, 4)] pred_logits = [torch.rand(N, Hi * Wi * A)] pred_logits[0][1][3:5].fill_(float("inf")) find_top_rpn_proposals(proposals, pred_logits, [(10, 10)], 0.5, 1000, 1000, 0, False) def test_find_rpn_proposals_tracing(self): N, Hi, Wi, A = 3, 50, 50, 9 proposal = torch.rand(N, Hi * Wi * A, 4) pred_logit = torch.rand(N, Hi * Wi * A) def func(proposal, logit, image_size): r = find_top_rpn_proposals( [proposal], [logit], [image_size], 0.7, 1000, 1000, 0, False )[0] size = r.image_size if not isinstance(size, torch.Tensor): size = torch.tensor(size) return (size, r.proposal_boxes.tensor, r.objectness_logits) other_inputs = [] # test that it generalizes to other shapes for Hi, Wi, shp in [(30, 30, 60), (10, 10, 800)]: other_inputs.append( ( torch.rand(N, Hi * Wi * A, 4), torch.rand(N, Hi * Wi * A), torch.tensor([shp, shp]), ) ) torch.jit.trace( func, (proposal, pred_logit, torch.tensor([100, 100])), check_inputs=other_inputs ) def test_append_gt_to_proposal(self): proposals = Instances( (10, 10), **{ "proposal_boxes": Boxes(torch.empty((0, 4))), "objectness_logits": torch.tensor([]), "custom_attribute": torch.tensor([]), } ) gt_boxes = Boxes(torch.tensor([[0, 0, 1, 1]])) self.assertRaises(AssertionError, add_ground_truth_to_proposals, [gt_boxes], [proposals]) gt_instances = Instances((10, 10)) gt_instances.gt_boxes = gt_boxes self.assertRaises( AssertionError, add_ground_truth_to_proposals, [gt_instances], [proposals] ) gt_instances.custom_attribute = torch.tensor([1]) gt_instances.custom_attribute2 = torch.tensor([1]) new_proposals = add_ground_truth_to_proposals([gt_instances], [proposals])[0] self.assertEqual(new_proposals.custom_attribute[0], 1) # new proposals should only include the attributes in proposals self.assertRaises(AttributeError, lambda: new_proposals.custom_attribute2) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/structures/__init__.py ================================================ ================================================ FILE: detectron2/tests/structures/test_boxes.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import json import math import numpy as np import unittest import torch from detectron2.structures import Boxes, BoxMode, pairwise_ioa, pairwise_iou from detectron2.utils.testing import reload_script_model class TestBoxMode(unittest.TestCase): def _convert_xy_to_wh(self, x): return BoxMode.convert(x, BoxMode.XYXY_ABS, BoxMode.XYWH_ABS) def _convert_xywha_to_xyxy(self, x): return BoxMode.convert(x, BoxMode.XYWHA_ABS, BoxMode.XYXY_ABS) def _convert_xywh_to_xywha(self, x): return BoxMode.convert(x, BoxMode.XYWH_ABS, BoxMode.XYWHA_ABS) def test_convert_int_mode(self): BoxMode.convert([1, 2, 3, 4], 0, 1) def test_box_convert_list(self): for tp in [list, tuple]: box = tp([5.0, 5.0, 10.0, 10.0]) output = self._convert_xy_to_wh(box) self.assertIsInstance(output, tp) self.assertIsInstance(output[0], float) self.assertEqual(output, tp([5.0, 5.0, 5.0, 5.0])) with self.assertRaises(Exception): self._convert_xy_to_wh([box]) def test_box_convert_array(self): box = np.asarray([[5, 5, 10, 10], [1, 1, 2, 3]]) output = self._convert_xy_to_wh(box) self.assertEqual(output.dtype, box.dtype) self.assertEqual(output.shape, box.shape) self.assertTrue((output[0] == [5, 5, 5, 5]).all()) self.assertTrue((output[1] == [1, 1, 1, 2]).all()) def test_box_convert_cpu_tensor(self): box = torch.tensor([[5, 5, 10, 10], [1, 1, 2, 3]]) output = self._convert_xy_to_wh(box) self.assertEqual(output.dtype, box.dtype) self.assertEqual(output.shape, box.shape) output = output.numpy() self.assertTrue((output[0] == [5, 5, 5, 5]).all()) self.assertTrue((output[1] == [1, 1, 1, 2]).all()) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_box_convert_cuda_tensor(self): box = torch.tensor([[5, 5, 10, 10], [1, 1, 2, 3]]).cuda() output = self._convert_xy_to_wh(box) self.assertEqual(output.dtype, box.dtype) self.assertEqual(output.shape, box.shape) self.assertEqual(output.device, box.device) output = output.cpu().numpy() self.assertTrue((output[0] == [5, 5, 5, 5]).all()) self.assertTrue((output[1] == [1, 1, 1, 2]).all()) def test_box_convert_xywha_to_xyxy_list(self): for tp in [list, tuple]: box = tp([50, 50, 30, 20, 0]) output = self._convert_xywha_to_xyxy(box) self.assertIsInstance(output, tp) self.assertEqual(output, tp([35, 40, 65, 60])) with self.assertRaises(Exception): self._convert_xywha_to_xyxy([box]) def test_box_convert_xywha_to_xyxy_array(self): for dtype in [np.float64, np.float32]: box = np.asarray( [ [50, 50, 30, 20, 0], [50, 50, 30, 20, 90], [1, 1, math.sqrt(2), math.sqrt(2), -45], ], dtype=dtype, ) output = self._convert_xywha_to_xyxy(box) self.assertEqual(output.dtype, box.dtype) expected = np.asarray([[35, 40, 65, 60], [40, 35, 60, 65], [0, 0, 2, 2]], dtype=dtype) self.assertTrue(np.allclose(output, expected, atol=1e-6), "output={}".format(output)) def test_box_convert_xywha_to_xyxy_tensor(self): for dtype in [torch.float32, torch.float64]: box = torch.tensor( [ [50, 50, 30, 20, 0], [50, 50, 30, 20, 90], [1, 1, math.sqrt(2), math.sqrt(2), -45], ], dtype=dtype, ) output = self._convert_xywha_to_xyxy(box) self.assertEqual(output.dtype, box.dtype) expected = torch.tensor([[35, 40, 65, 60], [40, 35, 60, 65], [0, 0, 2, 2]], dtype=dtype) self.assertTrue(torch.allclose(output, expected, atol=1e-6), "output={}".format(output)) def test_box_convert_xywh_to_xywha_list(self): for tp in [list, tuple]: box = tp([50, 50, 30, 20]) output = self._convert_xywh_to_xywha(box) self.assertIsInstance(output, tp) self.assertEqual(output, tp([65, 60, 30, 20, 0])) with self.assertRaises(Exception): self._convert_xywh_to_xywha([box]) def test_box_convert_xywh_to_xywha_array(self): for dtype in [np.float64, np.float32]: box = np.asarray([[30, 40, 70, 60], [30, 40, 60, 70], [-1, -1, 2, 2]], dtype=dtype) output = self._convert_xywh_to_xywha(box) self.assertEqual(output.dtype, box.dtype) expected = np.asarray( [[65, 70, 70, 60, 0], [60, 75, 60, 70, 0], [0, 0, 2, 2, 0]], dtype=dtype ) self.assertTrue(np.allclose(output, expected, atol=1e-6), "output={}".format(output)) def test_box_convert_xywh_to_xywha_tensor(self): for dtype in [torch.float32, torch.float64]: box = torch.tensor([[30, 40, 70, 60], [30, 40, 60, 70], [-1, -1, 2, 2]], dtype=dtype) output = self._convert_xywh_to_xywha(box) self.assertEqual(output.dtype, box.dtype) expected = torch.tensor( [[65, 70, 70, 60, 0], [60, 75, 60, 70, 0], [0, 0, 2, 2, 0]], dtype=dtype ) self.assertTrue(torch.allclose(output, expected, atol=1e-6), "output={}".format(output)) def test_json_serializable(self): payload = {"box_mode": BoxMode.XYWH_REL} try: json.dumps(payload) except Exception: self.fail("JSON serialization failed") def test_json_deserializable(self): payload = '{"box_mode": 2}' obj = json.loads(payload) try: obj["box_mode"] = BoxMode(obj["box_mode"]) except Exception: self.fail("JSON deserialization failed") class TestBoxIOU(unittest.TestCase): def create_boxes(self): boxes1 = torch.tensor([[0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0]]) boxes2 = torch.tensor( [ [0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 0.5, 1.0], [0.0, 0.0, 1.0, 0.5], [0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 1.0, 1.0], [0.5, 0.5, 1.5, 1.5], ] ) return boxes1, boxes2 def test_pairwise_iou(self): boxes1, boxes2 = self.create_boxes() expected_ious = torch.tensor( [ [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)], [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)], ] ) ious = pairwise_iou(Boxes(boxes1), Boxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious)) def test_pairwise_ioa(self): boxes1, boxes2 = self.create_boxes() expected_ioas = torch.tensor( [[1.0, 1.0, 1.0, 1.0, 1.0, 0.25], [1.0, 1.0, 1.0, 1.0, 1.0, 0.25]] ) ioas = pairwise_ioa(Boxes(boxes1), Boxes(boxes2)) self.assertTrue(torch.allclose(ioas, expected_ioas)) class TestBoxes(unittest.TestCase): def test_empty_cat(self): x = Boxes.cat([]) self.assertTrue(x.tensor.shape, (0, 4)) def test_to(self): x = Boxes(torch.rand(3, 4)) self.assertEqual(x.to(device="cpu").tensor.device.type, "cpu") def test_scriptability(self): def func(x): boxes = Boxes(x) test = boxes.to(torch.device("cpu")).tensor return boxes.area(), test f = torch.jit.script(func) f = reload_script_model(f) f(torch.rand((3, 4))) data = torch.rand((3, 4)) def func_cat(x: torch.Tensor): boxes1 = Boxes(x) boxes2 = Boxes(x) # boxes3 = Boxes.cat([boxes1, boxes2]) # this is not supported by torchsript for now. boxes3 = boxes1.cat([boxes1, boxes2]) return boxes3 f = torch.jit.script(func_cat) script_box = f(data) self.assertTrue(torch.equal(torch.cat([data, data]), script_box.tensor)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/structures/test_imagelist.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest from typing import List, Sequence, Tuple import torch from detectron2.structures import ImageList class TestImageList(unittest.TestCase): def test_imagelist_padding_tracing(self): # test that the trace does not contain hard-coded constant sizes def to_imagelist(tensors: Sequence[torch.Tensor]): image_list = ImageList.from_tensors(tensors, 4) return image_list.tensor, image_list.image_sizes def _tensor(*shape): return torch.ones(shape, dtype=torch.float32) # test CHW (inputs needs padding vs. no padding) for shape in [(3, 10, 10), (3, 12, 12)]: func = torch.jit.trace(to_imagelist, ([_tensor(*shape)],)) tensor, image_sizes = func([_tensor(3, 15, 20)]) self.assertEqual(tensor.shape, (1, 3, 16, 20), tensor.shape) self.assertEqual(image_sizes[0].tolist(), [15, 20], image_sizes[0]) # test HW func = torch.jit.trace(to_imagelist, ([_tensor(10, 10)],)) tensor, image_sizes = func([_tensor(15, 20)]) self.assertEqual(tensor.shape, (1, 16, 20), tensor.shape) self.assertEqual(image_sizes[0].tolist(), [15, 20], image_sizes[0]) # test 2x CHW func = torch.jit.trace( to_imagelist, ([_tensor(3, 16, 10), _tensor(3, 13, 11)],), ) tensor, image_sizes = func([_tensor(3, 25, 20), _tensor(3, 10, 10)]) self.assertEqual(tensor.shape, (2, 3, 28, 20), tensor.shape) self.assertEqual(image_sizes[0].tolist(), [25, 20], image_sizes[0]) self.assertEqual(image_sizes[1].tolist(), [10, 10], image_sizes[1]) # support calling with different spatial sizes, but not with different #images def test_imagelist_scriptability(self): image_nums = 2 image_tensor = torch.randn((image_nums, 10, 20), dtype=torch.float32) image_shape = [(10, 20)] * image_nums def f(image_tensor, image_shape: List[Tuple[int, int]]): return ImageList(image_tensor, image_shape) ret = f(image_tensor, image_shape) ret_script = torch.jit.script(f)(image_tensor, image_shape) self.assertEqual(len(ret), len(ret_script)) for i in range(image_nums): self.assertTrue(torch.equal(ret[i], ret_script[i])) def test_imagelist_from_tensors_scriptability(self): image_tensor_0 = torch.randn(10, 20, dtype=torch.float32) image_tensor_1 = torch.randn(12, 22, dtype=torch.float32) inputs = [image_tensor_0, image_tensor_1] def f(image_tensor: List[torch.Tensor]): return ImageList.from_tensors(image_tensor, 10) ret = f(inputs) ret_script = torch.jit.script(f)(inputs) self.assertEqual(len(ret), len(ret_script)) self.assertTrue(torch.equal(ret.tensor, ret_script.tensor)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/structures/test_instances.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest import torch from torch import Tensor from detectron2.export.torchscript import patch_instances from detectron2.structures import Boxes, Instances from detectron2.utils.testing import convert_scripted_instances class TestInstances(unittest.TestCase): def test_int_indexing(self): attr1 = torch.tensor([[0.0, 0.0, 1.0], [0.0, 0.0, 0.5], [0.0, 0.0, 1.0], [0.0, 0.5, 0.5]]) attr2 = torch.tensor([0.1, 0.2, 0.3, 0.4]) instances = Instances((100, 100)) instances.attr1 = attr1 instances.attr2 = attr2 for i in range(-len(instances), len(instances)): inst = instances[i] self.assertEqual((inst.attr1 == attr1[i]).all(), True) self.assertEqual((inst.attr2 == attr2[i]).all(), True) self.assertRaises(IndexError, lambda: instances[len(instances)]) self.assertRaises(IndexError, lambda: instances[-len(instances) - 1]) def test_script_new_fields(self): def get_mask(x: Instances) -> torch.Tensor: return x.mask class f(torch.nn.Module): def forward(self, x: Instances): proposal_boxes = x.proposal_boxes # noqa F841 objectness_logits = x.objectness_logits # noqa F841 return x class g(torch.nn.Module): def forward(self, x: Instances): return get_mask(x) class g2(torch.nn.Module): def __init__(self): super().__init__() self.g = g() def forward(self, x: Instances): proposal_boxes = x.proposal_boxes # noqa F841 return x, self.g(x) fields = {"proposal_boxes": Boxes, "objectness_logits": Tensor} with patch_instances(fields): torch.jit.script(f()) # can't script anymore after exiting the context with self.assertRaises(Exception): # will create a ConcreteType for g torch.jit.script(g2()) new_fields = {"mask": Tensor} with patch_instances(new_fields): # will compile g with a different Instances; this should pass torch.jit.script(g()) with self.assertRaises(Exception): torch.jit.script(g2()) new_fields = {"mask": Tensor, "proposal_boxes": Boxes} with patch_instances(new_fields) as NewInstances: # get_mask will be compiled with a different Instances; this should pass scripted_g2 = torch.jit.script(g2()) x = NewInstances((3, 4)) x.mask = torch.rand(3) x.proposal_boxes = Boxes(torch.rand(3, 4)) scripted_g2(x) # it should accept the new Instances object and run successfully def test_script_access_fields(self): class f(torch.nn.Module): def forward(self, x: Instances): proposal_boxes = x.proposal_boxes objectness_logits = x.objectness_logits return proposal_boxes.tensor + objectness_logits fields = {"proposal_boxes": Boxes, "objectness_logits": Tensor} with patch_instances(fields): torch.jit.script(f()) def test_script_len(self): class f(torch.nn.Module): def forward(self, x: Instances): return len(x) class g(torch.nn.Module): def forward(self, x: Instances): return len(x) image_shape = (15, 15) fields = {"proposal_boxes": Boxes} with patch_instances(fields) as new_instance: script_module = torch.jit.script(f()) x = new_instance(image_shape) with self.assertRaises(Exception): script_module(x) box_tensors = torch.tensor([[5, 5, 10, 10], [1, 1, 2, 3]]) x.proposal_boxes = Boxes(box_tensors) length = script_module(x) self.assertEqual(length, 2) fields = {"objectness_logits": Tensor} with patch_instances(fields) as new_instance: script_module = torch.jit.script(g()) x = new_instance(image_shape) objectness_logits = torch.tensor([1.0]).reshape(1, 1) x.objectness_logits = objectness_logits length = script_module(x) self.assertEqual(length, 1) def test_script_has(self): class f(torch.nn.Module): def forward(self, x: Instances): return x.has("proposal_boxes") image_shape = (15, 15) fields = {"proposal_boxes": Boxes} with patch_instances(fields) as new_instance: script_module = torch.jit.script(f()) x = new_instance(image_shape) self.assertFalse(script_module(x)) box_tensors = torch.tensor([[5, 5, 10, 10], [1, 1, 2, 3]]) x.proposal_boxes = Boxes(box_tensors) self.assertTrue(script_module(x)) def test_script_to(self): class f(torch.nn.Module): def forward(self, x: Instances): return x.to(torch.device("cpu")) image_shape = (15, 15) fields = {"proposal_boxes": Boxes, "a": Tensor} with patch_instances(fields) as new_instance: script_module = torch.jit.script(f()) x = new_instance(image_shape) script_module(x) box_tensors = torch.tensor([[5, 5, 10, 10], [1, 1, 2, 3]]) x.proposal_boxes = Boxes(box_tensors) x.a = box_tensors script_module(x) def test_script_getitem(self): class f(torch.nn.Module): def forward(self, x: Instances, idx): return x[idx] image_shape = (15, 15) fields = {"proposal_boxes": Boxes, "a": Tensor} inst = Instances(image_shape) inst.proposal_boxes = Boxes(torch.rand(4, 4)) inst.a = torch.rand(4, 10) idx = torch.tensor([True, False, True, False]) with patch_instances(fields) as new_instance: script_module = torch.jit.script(f()) out = f()(inst, idx) out_scripted = script_module(new_instance.from_instances(inst), idx) self.assertTrue( torch.equal(out.proposal_boxes.tensor, out_scripted.proposal_boxes.tensor) ) self.assertTrue(torch.equal(out.a, out_scripted.a)) def test_from_to_instances(self): orig = Instances((30, 30)) orig.proposal_boxes = Boxes(torch.rand(3, 4)) fields = {"proposal_boxes": Boxes, "a": Tensor} with patch_instances(fields) as NewInstances: # convert to NewInstances and back new1 = NewInstances.from_instances(orig) new2 = convert_scripted_instances(new1) self.assertTrue(torch.equal(orig.proposal_boxes.tensor, new1.proposal_boxes.tensor)) self.assertTrue(torch.equal(orig.proposal_boxes.tensor, new2.proposal_boxes.tensor)) def test_script_init_args(self): def f(x: Tensor): image_shape = (15, 15) # __init__ can take arguments inst = Instances(image_shape, a=x, proposal_boxes=Boxes(x)) inst2 = Instances(image_shape, a=x) return inst.a, inst2.a fields = {"proposal_boxes": Boxes, "a": Tensor} with patch_instances(fields): script_f = torch.jit.script(f) x = torch.randn(3, 4) outputs = script_f(x) self.assertTrue(torch.equal(outputs[0], x)) self.assertTrue(torch.equal(outputs[1], x)) def test_script_cat(self): def f(x: Tensor): image_shape = (15, 15) # __init__ can take arguments inst = Instances(image_shape, a=x) inst2 = Instances(image_shape, a=x) inst3 = Instances(image_shape, proposal_boxes=Boxes(x)) return inst.cat([inst, inst2]), inst3.cat([inst3, inst3]) fields = {"proposal_boxes": Boxes, "a": Tensor} with patch_instances(fields): script_f = torch.jit.script(f) x = torch.randn(3, 4) output, output2 = script_f(x) self.assertTrue(torch.equal(output.a, torch.cat([x, x]))) self.assertFalse(output.has("proposal_boxes")) self.assertTrue(torch.equal(output2.proposal_boxes.tensor, torch.cat([x, x]))) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/structures/test_keypoints.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest import torch from detectron2.structures.keypoints import Keypoints class TestKeypoints(unittest.TestCase): def test_cat_keypoints(self): keypoints1 = Keypoints(torch.rand(2, 21, 3)) keypoints2 = Keypoints(torch.rand(4, 21, 3)) cat_keypoints = keypoints1.cat([keypoints1, keypoints2]) self.assertTrue(torch.all(cat_keypoints.tensor[:2] == keypoints1.tensor).item()) self.assertTrue(torch.all(cat_keypoints.tensor[2:] == keypoints2.tensor).item()) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/structures/test_masks.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest import torch from detectron2.structures.masks import BitMasks, PolygonMasks, polygons_to_bitmask class TestBitMask(unittest.TestCase): def test_get_bounding_box(self): masks = torch.tensor( [ [ [False, False, False, True], [False, False, True, True], [False, True, True, False], [False, True, True, False], ], [ [False, False, False, False], [False, False, True, False], [False, True, True, False], [False, True, True, False], ], torch.zeros(4, 4), ] ) bitmask = BitMasks(masks) box_true = torch.tensor([[1, 0, 4, 4], [1, 1, 3, 4], [0, 0, 0, 0]], dtype=torch.float32) box = bitmask.get_bounding_boxes() self.assertTrue(torch.all(box.tensor == box_true).item()) for box in box_true: poly = box[[0, 1, 2, 1, 2, 3, 0, 3]].numpy() mask = polygons_to_bitmask([poly], 4, 4) reconstruct_box = BitMasks(mask[None, :, :]).get_bounding_boxes()[0].tensor self.assertTrue(torch.all(box == reconstruct_box).item()) reconstruct_box = PolygonMasks([[poly]]).get_bounding_boxes()[0].tensor self.assertTrue(torch.all(box == reconstruct_box).item()) def test_from_empty_polygons(self): masks = BitMasks.from_polygon_masks([], 100, 100) self.assertEqual(masks.tensor.shape, (0, 100, 100)) def test_getitem(self): masks = BitMasks(torch.ones(3, 10, 10)) self.assertEqual(masks[1].tensor.shape, (1, 10, 10)) self.assertEqual(masks[1:3].tensor.shape, (2, 10, 10)) self.assertEqual(masks[torch.tensor([True, False, False])].tensor.shape, (1, 10, 10)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/structures/test_rotated_boxes.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from __future__ import absolute_import, division, print_function, unicode_literals import logging import math import random import unittest import torch from fvcore.common.benchmark import benchmark from detectron2.layers.rotated_boxes import pairwise_iou_rotated from detectron2.structures.boxes import Boxes from detectron2.structures.rotated_boxes import RotatedBoxes, pairwise_iou from detectron2.utils.testing import reload_script_model logger = logging.getLogger(__name__) class TestRotatedBoxesLayer(unittest.TestCase): def test_iou_0_dim_cpu(self): boxes1 = torch.rand(0, 5, dtype=torch.float32) boxes2 = torch.rand(10, 5, dtype=torch.float32) expected_ious = torch.zeros(0, 10, dtype=torch.float32) ious = pairwise_iou_rotated(boxes1, boxes2) self.assertTrue(torch.allclose(ious, expected_ious)) boxes1 = torch.rand(10, 5, dtype=torch.float32) boxes2 = torch.rand(0, 5, dtype=torch.float32) expected_ious = torch.zeros(10, 0, dtype=torch.float32) ious = pairwise_iou_rotated(boxes1, boxes2) self.assertTrue(torch.allclose(ious, expected_ious)) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_iou_0_dim_cuda(self): boxes1 = torch.rand(0, 5, dtype=torch.float32) boxes2 = torch.rand(10, 5, dtype=torch.float32) expected_ious = torch.zeros(0, 10, dtype=torch.float32) ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda()) self.assertTrue(torch.allclose(ious_cuda.cpu(), expected_ious)) boxes1 = torch.rand(10, 5, dtype=torch.float32) boxes2 = torch.rand(0, 5, dtype=torch.float32) expected_ious = torch.zeros(10, 0, dtype=torch.float32) ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda()) self.assertTrue(torch.allclose(ious_cuda.cpu(), expected_ious)) def test_iou_half_overlap_cpu(self): boxes1 = torch.tensor([[0.5, 0.5, 1.0, 1.0, 0.0]], dtype=torch.float32) boxes2 = torch.tensor([[0.25, 0.5, 0.5, 1.0, 0.0]], dtype=torch.float32) expected_ious = torch.tensor([[0.5]], dtype=torch.float32) ious = pairwise_iou_rotated(boxes1, boxes2) self.assertTrue(torch.allclose(ious, expected_ious)) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_iou_half_overlap_cuda(self): boxes1 = torch.tensor([[0.5, 0.5, 1.0, 1.0, 0.0]], dtype=torch.float32) boxes2 = torch.tensor([[0.25, 0.5, 0.5, 1.0, 0.0]], dtype=torch.float32) expected_ious = torch.tensor([[0.5]], dtype=torch.float32) ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda()) self.assertTrue(torch.allclose(ious_cuda.cpu(), expected_ious)) def test_iou_precision(self): for device in ["cpu"] + (["cuda"] if torch.cuda.is_available() else []): boxes1 = torch.tensor([[565, 565, 10, 10.0, 0]], dtype=torch.float32, device=device) boxes2 = torch.tensor([[565, 565, 10, 8.3, 0]], dtype=torch.float32, device=device) iou = 8.3 / 10.0 expected_ious = torch.tensor([[iou]], dtype=torch.float32) ious = pairwise_iou_rotated(boxes1, boxes2) self.assertTrue(torch.allclose(ious.cpu(), expected_ious)) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_iou_too_many_boxes_cuda(self): s1, s2 = 5, 1289035 boxes1 = torch.zeros(s1, 5) boxes2 = torch.zeros(s2, 5) ious_cuda = pairwise_iou_rotated(boxes1.cuda(), boxes2.cuda()) self.assertTupleEqual(tuple(ious_cuda.shape), (s1, s2)) def test_iou_extreme(self): # Cause floating point issues in cuda kernels (#1266) for device in ["cpu"] + (["cuda"] if torch.cuda.is_available() else []): boxes1 = torch.tensor([[160.0, 153.0, 230.0, 23.0, -37.0]], device=device) boxes2 = torch.tensor( [ [ -1.117407639806935e17, 1.3858420478349148e18, 1000.0000610351562, 1000.0000610351562, 1612.0, ] ], device=device, ) ious = pairwise_iou_rotated(boxes1, boxes2) self.assertTrue(ious.min() >= 0, ious) def test_iou_issue_2154(self): for device in ["cpu"] + (["cuda"] if torch.cuda.is_available() else []): boxes1 = torch.tensor( [ [ 296.6620178222656, 458.73883056640625, 23.515729904174805, 47.677001953125, 0.08795166015625, ] ], device=device, ) boxes2 = torch.tensor( [[296.66201, 458.73882000000003, 23.51573, 47.67702, 0.087951]], device=device, ) ious = pairwise_iou_rotated(boxes1, boxes2) expected_ious = torch.tensor([[1.0]], dtype=torch.float32) self.assertTrue(torch.allclose(ious.cpu(), expected_ious)) def test_iou_issue_2167(self): for device in ["cpu"] + (["cuda"] if torch.cuda.is_available() else []): boxes1 = torch.tensor( [ [ 2563.74462890625000000000, 1436.79016113281250000000, 2174.70336914062500000000, 214.09500122070312500000, 115.11834716796875000000, ] ], device=device, ) boxes2 = torch.tensor( [ [ 2563.74462890625000000000, 1436.79028320312500000000, 2174.70288085937500000000, 214.09495544433593750000, 115.11835479736328125000, ] ], device=device, ) ious = pairwise_iou_rotated(boxes1, boxes2) expected_ious = torch.tensor([[1.0]], dtype=torch.float32) self.assertTrue(torch.allclose(ious.cpu(), expected_ious)) class TestRotatedBoxesStructure(unittest.TestCase): def test_clip_area_0_degree(self): for _ in range(50): num_boxes = 100 boxes_5d = torch.zeros(num_boxes, 5) boxes_5d[:, 0] = torch.FloatTensor(num_boxes).uniform_(-100, 500) boxes_5d[:, 1] = torch.FloatTensor(num_boxes).uniform_(-100, 500) boxes_5d[:, 2] = torch.FloatTensor(num_boxes).uniform_(0, 500) boxes_5d[:, 3] = torch.FloatTensor(num_boxes).uniform_(0, 500) # Convert from (x_ctr, y_ctr, w, h, 0) to (x1, y1, x2, y2) boxes_4d = torch.zeros(num_boxes, 4) boxes_4d[:, 0] = boxes_5d[:, 0] - boxes_5d[:, 2] / 2.0 boxes_4d[:, 1] = boxes_5d[:, 1] - boxes_5d[:, 3] / 2.0 boxes_4d[:, 2] = boxes_5d[:, 0] + boxes_5d[:, 2] / 2.0 boxes_4d[:, 3] = boxes_5d[:, 1] + boxes_5d[:, 3] / 2.0 image_size = (500, 600) test_boxes_4d = Boxes(boxes_4d) test_boxes_5d = RotatedBoxes(boxes_5d) # Before clip areas_4d = test_boxes_4d.area() areas_5d = test_boxes_5d.area() self.assertTrue(torch.allclose(areas_4d, areas_5d, atol=1e-1, rtol=1e-5)) # After clip test_boxes_4d.clip(image_size) test_boxes_5d.clip(image_size) areas_4d = test_boxes_4d.area() areas_5d = test_boxes_5d.area() self.assertTrue(torch.allclose(areas_4d, areas_5d, atol=1e-1, rtol=1e-5)) def test_clip_area_arbitrary_angle(self): num_boxes = 100 boxes_5d = torch.zeros(num_boxes, 5) boxes_5d[:, 0] = torch.FloatTensor(num_boxes).uniform_(-100, 500) boxes_5d[:, 1] = torch.FloatTensor(num_boxes).uniform_(-100, 500) boxes_5d[:, 2] = torch.FloatTensor(num_boxes).uniform_(0, 500) boxes_5d[:, 3] = torch.FloatTensor(num_boxes).uniform_(0, 500) boxes_5d[:, 4] = torch.FloatTensor(num_boxes).uniform_(-1800, 1800) clip_angle_threshold = random.uniform(0, 180) image_size = (500, 600) test_boxes_5d = RotatedBoxes(boxes_5d) # Before clip areas_before = test_boxes_5d.area() # After clip test_boxes_5d.clip(image_size, clip_angle_threshold) areas_diff = test_boxes_5d.area() - areas_before # the areas should only decrease after clipping self.assertTrue(torch.all(areas_diff <= 0)) # whenever the box is clipped (thus the area shrinks), # the angle for the box must be within the clip_angle_threshold # Note that the clip function will normalize the angle range # to be within (-180, 180] self.assertTrue( torch.all(torch.abs(boxes_5d[:, 4][torch.where(areas_diff < 0)]) < clip_angle_threshold) ) def test_normalize_angles(self): # torch.manual_seed(0) for _ in range(50): num_boxes = 100 boxes_5d = torch.zeros(num_boxes, 5) boxes_5d[:, 0] = torch.FloatTensor(num_boxes).uniform_(-100, 500) boxes_5d[:, 1] = torch.FloatTensor(num_boxes).uniform_(-100, 500) boxes_5d[:, 2] = torch.FloatTensor(num_boxes).uniform_(0, 500) boxes_5d[:, 3] = torch.FloatTensor(num_boxes).uniform_(0, 500) boxes_5d[:, 4] = torch.FloatTensor(num_boxes).uniform_(-1800, 1800) rotated_boxes = RotatedBoxes(boxes_5d) normalized_boxes = rotated_boxes.clone() normalized_boxes.normalize_angles() self.assertTrue(torch.all(normalized_boxes.tensor[:, 4] >= -180)) self.assertTrue(torch.all(normalized_boxes.tensor[:, 4] < 180)) # x, y, w, h should not change self.assertTrue(torch.allclose(boxes_5d[:, :4], normalized_boxes.tensor[:, :4])) # the cos/sin values of the angles should stay the same self.assertTrue( torch.allclose( torch.cos(boxes_5d[:, 4] * math.pi / 180), torch.cos(normalized_boxes.tensor[:, 4] * math.pi / 180), atol=1e-5, ) ) self.assertTrue( torch.allclose( torch.sin(boxes_5d[:, 4] * math.pi / 180), torch.sin(normalized_boxes.tensor[:, 4] * math.pi / 180), atol=1e-5, ) ) def test_pairwise_iou_0_degree(self): for device in ["cpu"] + (["cuda"] if torch.cuda.is_available() else []): boxes1 = torch.tensor( [[0.5, 0.5, 1.0, 1.0, 0.0], [0.5, 0.5, 1.0, 1.0, 0.0]], dtype=torch.float32, device=device, ) boxes2 = torch.tensor( [ [0.5, 0.5, 1.0, 1.0, 0.0], [0.25, 0.5, 0.5, 1.0, 0.0], [0.5, 0.25, 1.0, 0.5, 0.0], [0.25, 0.25, 0.5, 0.5, 0.0], [0.75, 0.75, 0.5, 0.5, 0.0], [1.0, 1.0, 1.0, 1.0, 0.0], ], dtype=torch.float32, device=device, ) expected_ious = torch.tensor( [ [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)], [1.0, 0.5, 0.5, 0.25, 0.25, 0.25 / (2 - 0.25)], ], dtype=torch.float32, device=device, ) ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious)) def test_pairwise_iou_45_degrees(self): for device in ["cpu"] + (["cuda"] if torch.cuda.is_available() else []): boxes1 = torch.tensor( [ [1, 1, math.sqrt(2), math.sqrt(2), 45], [1, 1, 2 * math.sqrt(2), 2 * math.sqrt(2), -45], ], dtype=torch.float32, device=device, ) boxes2 = torch.tensor([[1, 1, 2, 2, 0]], dtype=torch.float32, device=device) expected_ious = torch.tensor([[0.5], [0.5]], dtype=torch.float32, device=device) ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious)) def test_pairwise_iou_orthogonal(self): for device in ["cpu"] + (["cuda"] if torch.cuda.is_available() else []): boxes1 = torch.tensor([[5, 5, 10, 6, 55]], dtype=torch.float32, device=device) boxes2 = torch.tensor([[5, 5, 10, 6, -35]], dtype=torch.float32, device=device) iou = (6.0 * 6.0) / (6.0 * 6.0 + 4.0 * 6.0 + 4.0 * 6.0) expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device) ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious)) def test_pairwise_iou_large_close_boxes(self): for device in ["cpu"] + (["cuda"] if torch.cuda.is_available() else []): boxes1 = torch.tensor( [[299.500000, 417.370422, 600.000000, 364.259186, 27.1828]], dtype=torch.float32, device=device, ) boxes2 = torch.tensor( [[299.500000, 417.370422, 600.000000, 364.259155, 27.1828]], dtype=torch.float32, device=device, ) iou = 364.259155 / 364.259186 expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device) ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious)) def test_pairwise_iou_many_boxes(self): for device in ["cpu"] + (["cuda"] if torch.cuda.is_available() else []): num_boxes1 = 100 num_boxes2 = 200 boxes1 = torch.stack( [ torch.tensor( [5 + 20 * i, 5 + 20 * i, 10, 10, 0], dtype=torch.float32, device=device, ) for i in range(num_boxes1) ] ) boxes2 = torch.stack( [ torch.tensor( [5 + 20 * i, 5 + 20 * i, 10, 1 + 9 * i / num_boxes2, 0], dtype=torch.float32, device=device, ) for i in range(num_boxes2) ] ) expected_ious = torch.zeros(num_boxes1, num_boxes2, dtype=torch.float32, device=device) for i in range(min(num_boxes1, num_boxes2)): expected_ious[i][i] = (1 + 9 * i / num_boxes2) / 10.0 ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious)) def test_pairwise_iou_issue1207_simplified(self): for device in ["cpu"] + (["cuda"] if torch.cuda.is_available() else []): # Simplified test case of D2-issue-1207 boxes1 = torch.tensor([[3, 3, 8, 2, -45.0]], device=device) boxes2 = torch.tensor([[6, 0, 8, 2, -45.0]], device=device) iou = 0.0 expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device) ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious)) def test_pairwise_iou_issue1207(self): for device in ["cpu"] + (["cuda"] if torch.cuda.is_available() else []): # The original test case in D2-issue-1207 boxes1 = torch.tensor([[160.0, 153.0, 230.0, 23.0, -37.0]], device=device) boxes2 = torch.tensor([[190.0, 127.0, 80.0, 21.0, -46.0]], device=device) iou = 0.0 expected_ious = torch.tensor([[iou]], dtype=torch.float32, device=device) ious = pairwise_iou(RotatedBoxes(boxes1), RotatedBoxes(boxes2)) self.assertTrue(torch.allclose(ious, expected_ious)) def test_empty_cat(self): x = RotatedBoxes.cat([]) self.assertTrue(x.tensor.shape, (0, 5)) def test_scriptability(self): def func(x): boxes = RotatedBoxes(x) test = boxes.to(torch.device("cpu")).tensor return boxes.area(), test f = torch.jit.script(func) f = reload_script_model(f) f(torch.rand((3, 5))) data = torch.rand((3, 5)) def func_cat(x: torch.Tensor): boxes1 = RotatedBoxes(x) boxes2 = RotatedBoxes(x) # this is not supported by torchscript for now. # boxes3 = RotatedBoxes.cat([boxes1, boxes2]) boxes3 = boxes1.cat([boxes1, boxes2]) return boxes3 f = torch.jit.script(func_cat) script_box = f(data) self.assertTrue(torch.equal(torch.cat([data, data]), script_box.tensor)) def benchmark_rotated_iou(): num_boxes1 = 200 num_boxes2 = 500 boxes1 = torch.stack( [ torch.tensor([5 + 20 * i, 5 + 20 * i, 10, 10, 0], dtype=torch.float32) for i in range(num_boxes1) ] ) boxes2 = torch.stack( [ torch.tensor( [5 + 20 * i, 5 + 20 * i, 10, 1 + 9 * i / num_boxes2, 0], dtype=torch.float32, ) for i in range(num_boxes2) ] ) def func(dev, n=1): b1 = boxes1.to(device=dev) b2 = boxes2.to(device=dev) def bench(): for _ in range(n): pairwise_iou_rotated(b1, b2) if dev.type == "cuda": torch.cuda.synchronize() return bench # only run it once per timed loop, since it's slow args = [{"dev": torch.device("cpu"), "n": 1}] if torch.cuda.is_available(): args.append({"dev": torch.device("cuda"), "n": 10}) benchmark(func, "rotated_iou", args, warmup_iters=3) if __name__ == "__main__": unittest.main() benchmark_rotated_iou() ================================================ FILE: detectron2/tests/test_checkpoint.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest from collections import OrderedDict import torch from torch import nn from detectron2.checkpoint.c2_model_loading import align_and_update_state_dicts from detectron2.utils.logger import setup_logger class TestCheckpointer(unittest.TestCase): def setUp(self): setup_logger() def create_complex_model(self): m = nn.Module() m.block1 = nn.Module() m.block1.layer1 = nn.Linear(2, 3) m.layer2 = nn.Linear(3, 2) m.res = nn.Module() m.res.layer2 = nn.Linear(3, 2) state_dict = OrderedDict() state_dict["layer1.weight"] = torch.rand(3, 2) state_dict["layer1.bias"] = torch.rand(3) state_dict["layer2.weight"] = torch.rand(2, 3) state_dict["layer2.bias"] = torch.rand(2) state_dict["res.layer2.weight"] = torch.rand(2, 3) state_dict["res.layer2.bias"] = torch.rand(2) return m, state_dict def test_complex_model_loaded(self): for add_data_parallel in [False, True]: model, state_dict = self.create_complex_model() if add_data_parallel: model = nn.DataParallel(model) model_sd = model.state_dict() sd_to_load = align_and_update_state_dicts(model_sd, state_dict) model.load_state_dict(sd_to_load) for loaded, stored in zip(model_sd.values(), state_dict.values()): # different tensor references self.assertFalse(id(loaded) == id(stored)) # same content self.assertTrue(loaded.to(stored).equal(stored)) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/test_engine.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import json import math import os import tempfile import time import unittest from unittest import mock import torch from fvcore.common.checkpoint import Checkpointer from torch import nn from detectron2 import model_zoo from detectron2.config import configurable, get_cfg from detectron2.engine import DefaultTrainer, SimpleTrainer, default_setup, hooks from detectron2.modeling.meta_arch import META_ARCH_REGISTRY from detectron2.utils.events import CommonMetricPrinter, JSONWriter @META_ARCH_REGISTRY.register() class _SimpleModel(nn.Module): @configurable def __init__(self, sleep_sec=0): super().__init__() self.mod = nn.Linear(10, 20) self.sleep_sec = sleep_sec @classmethod def from_config(cls, cfg): return {} def forward(self, x): if self.sleep_sec > 0: time.sleep(self.sleep_sec) return {"loss": x.sum() + sum([x.mean() for x in self.parameters()])} class TestTrainer(unittest.TestCase): def _data_loader(self, device): device = torch.device(device) while True: yield torch.rand(3, 3).to(device) def test_simple_trainer(self, device="cpu"): model = _SimpleModel().to(device=device) trainer = SimpleTrainer( model, self._data_loader(device), torch.optim.SGD(model.parameters(), 0.1) ) trainer.train(0, 10) def test_simple_trainer_reset_dataloader(self, device="cpu"): model = _SimpleModel().to(device=device) trainer = SimpleTrainer( model, self._data_loader(device), torch.optim.SGD(model.parameters(), 0.1) ) trainer.train(0, 10) trainer.reset_data_loader(lambda: self._data_loader(device)) trainer.train(0, 10) @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def test_simple_trainer_cuda(self): self.test_simple_trainer(device="cuda") def test_writer_hooks(self): model = _SimpleModel(sleep_sec=0.1) trainer = SimpleTrainer( model, self._data_loader("cpu"), torch.optim.SGD(model.parameters(), 0.1) ) max_iter = 50 with tempfile.TemporaryDirectory(prefix="detectron2_test") as d: json_file = os.path.join(d, "metrics.json") writers = [CommonMetricPrinter(max_iter), JSONWriter(json_file)] trainer.register_hooks( [hooks.EvalHook(0, lambda: {"metric": 100}), hooks.PeriodicWriter(writers)] ) with self.assertLogs(writers[0].logger) as logs: trainer.train(0, max_iter) with open(json_file, "r") as f: data = [json.loads(line.strip()) for line in f] self.assertEqual([x["iteration"] for x in data], [19, 39, 49, 50]) # the eval metric is in the last line with iter 50 self.assertIn("metric", data[-1], "Eval metric must be in last line of JSON!") # test logged messages from CommonMetricPrinter self.assertEqual(len(logs.output), 3) for log, iter in zip(logs.output, [19, 39, 49]): self.assertIn(f"iter: {iter}", log) self.assertIn("eta: 0:00:00", logs.output[-1], "Last ETA must be 0!") def test_default_trainer(self): # TODO: this test requires manifold access, so changed device to CPU. see: T88318502 cfg = get_cfg() cfg.MODEL.DEVICE = "cpu" cfg.MODEL.META_ARCHITECTURE = "_SimpleModel" cfg.DATASETS.TRAIN = ("coco_2017_val_100",) with tempfile.TemporaryDirectory(prefix="detectron2_test") as d: cfg.OUTPUT_DIR = d trainer = DefaultTrainer(cfg) # test property self.assertIs(trainer.model, trainer._trainer.model) trainer.model = _SimpleModel() self.assertIs(trainer.model, trainer._trainer.model) def test_checkpoint_resume(self): model = _SimpleModel() dataloader = self._data_loader("cpu") opt = torch.optim.SGD(model.parameters(), 0.1) scheduler = torch.optim.lr_scheduler.StepLR(opt, 3) with tempfile.TemporaryDirectory(prefix="detectron2_test") as d: trainer = SimpleTrainer(model, dataloader, opt) checkpointer = Checkpointer(model, d, opt=opt, trainer=trainer) trainer.register_hooks( [ hooks.LRScheduler(scheduler=scheduler), # checkpoint after scheduler to properly save the state of scheduler hooks.PeriodicCheckpointer(checkpointer, 10), ] ) trainer.train(0, 12) self.assertAlmostEqual(opt.param_groups[0]["lr"], 1e-5) self.assertEqual(scheduler.last_epoch, 12) del trainer opt = torch.optim.SGD(model.parameters(), 999) # lr will be loaded trainer = SimpleTrainer(model, dataloader, opt) scheduler = torch.optim.lr_scheduler.StepLR(opt, 3) trainer.register_hooks( [ hooks.LRScheduler(scheduler=scheduler), ] ) checkpointer = Checkpointer(model, d, opt=opt, trainer=trainer) checkpointer.resume_or_load("non_exist.pth") self.assertEqual(trainer.iter, 11) # last finished iter number (0-based in Trainer) # number of times `scheduler.step()` was called (1-based) self.assertEqual(scheduler.last_epoch, 12) self.assertAlmostEqual(opt.param_groups[0]["lr"], 1e-5) def test_eval_hook(self): model = _SimpleModel() dataloader = self._data_loader("cpu") opt = torch.optim.SGD(model.parameters(), 0.1) for total_iter, period, eval_count in [(30, 15, 2), (31, 15, 3), (20, 0, 1)]: test_func = mock.Mock(return_value={"metric": 3.0}) trainer = SimpleTrainer(model, dataloader, opt) trainer.register_hooks([hooks.EvalHook(period, test_func)]) trainer.train(0, total_iter) self.assertEqual(test_func.call_count, eval_count) def test_best_checkpointer(self): model = _SimpleModel() dataloader = self._data_loader("cpu") opt = torch.optim.SGD(model.parameters(), 0.1) metric_name = "metric" total_iter = 40 test_period = 10 test_cases = [ ("max", iter([0.3, 0.4, 0.35, 0.5]), 3), ("min", iter([1.0, 0.8, 0.9, 0.9]), 2), ("min", iter([math.nan, 0.8, 0.9, 0.9]), 1), ] for mode, metrics, call_count in test_cases: trainer = SimpleTrainer(model, dataloader, opt) with tempfile.TemporaryDirectory(prefix="detectron2_test") as d: checkpointer = Checkpointer(model, d, opt=opt, trainer=trainer) trainer.register_hooks( [ hooks.EvalHook(test_period, lambda: {metric_name: next(metrics)}), hooks.BestCheckpointer(test_period, checkpointer, metric_name, mode=mode), ] ) with mock.patch.object(checkpointer, "save") as mock_save_method: trainer.train(0, total_iter) self.assertEqual(mock_save_method.call_count, call_count) def test_setup_config(self): with tempfile.TemporaryDirectory(prefix="detectron2_test") as d: cfg = get_cfg() cfg.OUTPUT_DIR = os.path.join(d, "yacs") default_setup(cfg, {}) cfg = model_zoo.get_config("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.py") cfg.train.output_dir = os.path.join(d, "omegaconf") default_setup(cfg, {}) ================================================ FILE: detectron2/tests/test_events.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import json import os import tempfile import unittest from detectron2.utils.events import CommonMetricPrinter, EventStorage, JSONWriter class TestEventWriter(unittest.TestCase): def testScalar(self): with tempfile.TemporaryDirectory( prefix="detectron2_tests" ) as dir, EventStorage() as storage: json_file = os.path.join(dir, "test.json") writer = JSONWriter(json_file) for k in range(60): storage.put_scalar("key", k, smoothing_hint=False) if (k + 1) % 20 == 0: writer.write() storage.step() writer.close() with open(json_file) as f: data = [json.loads(l) for l in f] self.assertTrue([int(k["key"]) for k in data] == [19, 39, 59]) def testScalarMismatchedPeriod(self): with tempfile.TemporaryDirectory( prefix="detectron2_tests" ) as dir, EventStorage() as storage: json_file = os.path.join(dir, "test.json") writer = JSONWriter(json_file) for k in range(60): if k % 17 == 0: # write in a differnt period storage.put_scalar("key2", k, smoothing_hint=False) storage.put_scalar("key", k, smoothing_hint=False) if (k + 1) % 20 == 0: writer.write() storage.step() writer.close() with open(json_file) as f: data = [json.loads(l) for l in f] self.assertTrue([int(k.get("key2", 0)) for k in data] == [17, 0, 34, 0, 51, 0]) self.assertTrue([int(k.get("key", 0)) for k in data] == [0, 19, 0, 39, 0, 59]) self.assertTrue([int(k["iteration"]) for k in data] == [17, 19, 34, 39, 51, 59]) def testPrintETA(self): with EventStorage() as s: p1 = CommonMetricPrinter(10) p2 = CommonMetricPrinter() s.put_scalar("time", 1.0) s.step() s.put_scalar("time", 1.0) s.step() with self.assertLogs("detectron2.utils.events") as logs: p1.write() self.assertIn("eta", logs.output[0]) with self.assertLogs("detectron2.utils.events") as logs: p2.write() self.assertNotIn("eta", logs.output[0]) ================================================ FILE: detectron2/tests/test_export_caffe2.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # -*- coding: utf-8 -*- import copy import os import tempfile import unittest import torch from torch.hub import _check_module_exists from detectron2 import model_zoo from detectron2.utils.logger import setup_logger from detectron2.utils.testing import get_sample_coco_image try: # Caffe2 used to be included in PyTorch, but since PyTorch 1.10+, # Caffe2 is not included in pre-built packages. This is a safety BC check from detectron2.export import Caffe2Model, Caffe2Tracer except ImportError: raise unittest.SkipTest( f"PyTorch does not have Caffe2 support. Skipping all tests in {__name__}" ) from None # TODO: this test requires manifold access, see: T88318502 # Running it on CircleCI causes crash, not sure why. @unittest.skipIf(os.environ.get("CIRCLECI"), "Caffe2 tests crash on CircleCI.") @unittest.skipIf(not _check_module_exists("onnx"), "ONNX not installed.") class TestCaffe2Export(unittest.TestCase): def setUp(self): setup_logger() def _test_model(self, config_path, device="cpu"): cfg = model_zoo.get_config(config_path) cfg.MODEL.DEVICE = device model = model_zoo.get(config_path, trained=True, device=device) inputs = [{"image": get_sample_coco_image()}] tracer = Caffe2Tracer(cfg, model, copy.deepcopy(inputs)) with tempfile.TemporaryDirectory(prefix="detectron2_unittest") as d: if not os.environ.get("CI"): # This requires onnx, which is not yet available on public CI c2_model = tracer.export_caffe2() c2_model.save_protobuf(d) c2_model.save_graph(os.path.join(d, "test.svg"), inputs=copy.deepcopy(inputs)) c2_model = Caffe2Model.load_protobuf(d) c2_model(inputs)[0]["instances"] ts_model = tracer.export_torchscript() ts_model.save(os.path.join(d, "model.ts")) def testMaskRCNN(self): self._test_model("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml") @unittest.skipIf(not torch.cuda.is_available(), "CUDA not available") def testMaskRCNNGPU(self): self._test_model("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml", device="cuda") def testRetinaNet(self): self._test_model("COCO-Detection/retinanet_R_50_FPN_3x.yaml") ================================================ FILE: detectron2/tests/test_export_onnx.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import io import unittest import warnings import torch from torch.hub import _check_module_exists from detectron2 import model_zoo from detectron2.config import get_cfg from detectron2.export import STABLE_ONNX_OPSET_VERSION from detectron2.export.flatten import TracingAdapter from detectron2.modeling import build_model from detectron2.utils.testing import ( _pytorch1111_symbolic_opset9_repeat_interleave, _pytorch1111_symbolic_opset9_to, get_sample_coco_image, register_custom_op_onnx_export, skipIfOnCPUCI, skipIfUnsupportedMinOpsetVersion, skipIfUnsupportedMinTorchVersion, unregister_custom_op_onnx_export, ) @unittest.skipIf(not _check_module_exists("onnx"), "ONNX not installed.") @skipIfUnsupportedMinTorchVersion("1.10") class TestONNXTracingExport(unittest.TestCase): def testMaskRCNNFPN(self): def inference_func(model, images): with warnings.catch_warnings(record=True): inputs = [{"image": image} for image in images] inst = model.inference(inputs, do_postprocess=False)[0] return [{"instances": inst}] self._test_model_zoo_from_config_path( "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml", inference_func ) @skipIfOnCPUCI def testMaskRCNNC4(self): def inference_func(model, image): inputs = [{"image": image}] return model.inference(inputs, do_postprocess=False)[0] self._test_model_zoo_from_config_path( "COCO-InstanceSegmentation/mask_rcnn_R_50_C4_3x.yaml", inference_func ) @skipIfOnCPUCI def testCascadeRCNN(self): def inference_func(model, image): inputs = [{"image": image}] return model.inference(inputs, do_postprocess=False)[0] self._test_model_zoo_from_config_path( "Misc/cascade_mask_rcnn_R_50_FPN_3x.yaml", inference_func ) def testRetinaNet(self): def inference_func(model, image): return model.forward([{"image": image}])[0]["instances"] self._test_model_zoo_from_config_path( "COCO-Detection/retinanet_R_50_FPN_3x.yaml", inference_func ) @skipIfOnCPUCI def testMaskRCNNFPN_batched(self): def inference_func(model, image1, image2): inputs = [{"image": image1}, {"image": image2}] return model.inference(inputs, do_postprocess=False) self._test_model_zoo_from_config_path( "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml", inference_func, batch=2 ) @skipIfUnsupportedMinOpsetVersion(16, STABLE_ONNX_OPSET_VERSION) @skipIfUnsupportedMinTorchVersion("1.11.1") def testMaskRCNNFPN_with_postproc(self): def inference_func(model, image): inputs = [{"image": image, "height": image.shape[1], "width": image.shape[2]}] return model.inference(inputs, do_postprocess=True)[0]["instances"] self._test_model_zoo_from_config_path( "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml", inference_func, opset_version=STABLE_ONNX_OPSET_VERSION, ) ################################################################################ # Testcase internals - DO NOT add tests below this point ################################################################################ def setUp(self): register_custom_op_onnx_export("::to", _pytorch1111_symbolic_opset9_to, 9, "1.11.1") register_custom_op_onnx_export( "::repeat_interleave", _pytorch1111_symbolic_opset9_repeat_interleave, 9, "1.11.1", ) def tearDown(self): unregister_custom_op_onnx_export("::to", 9, "1.11.1") unregister_custom_op_onnx_export("::repeat_interleave", 9, "1.11.1") def _test_model( self, model, inputs, inference_func=None, opset_version=STABLE_ONNX_OPSET_VERSION, save_onnx_graph_path=None, **export_kwargs, ): import onnx # isort:skip f = io.BytesIO() adapter_model = TracingAdapter(model, inputs, inference_func) adapter_model.eval() with torch.no_grad(): try: torch.onnx.enable_log() except AttributeError: # Older ONNX versions does not have this API pass torch.onnx.export( adapter_model, adapter_model.flattened_inputs, f, training=torch.onnx.TrainingMode.EVAL, opset_version=opset_version, verbose=True, **export_kwargs, ) onnx_model = onnx.load_from_string(f.getvalue()) assert onnx_model is not None if save_onnx_graph_path: onnx.save(onnx_model, save_onnx_graph_path) def _test_model_zoo_from_config_path( self, config_path, inference_func, batch=1, opset_version=STABLE_ONNX_OPSET_VERSION, save_onnx_graph_path=None, **export_kwargs, ): model = model_zoo.get(config_path, trained=True) image = get_sample_coco_image() inputs = tuple(image.clone() for _ in range(batch)) return self._test_model( model, inputs, inference_func, opset_version, save_onnx_graph_path, **export_kwargs ) def _test_model_from_config_path( self, config_path, inference_func, batch=1, opset_version=STABLE_ONNX_OPSET_VERSION, save_onnx_graph_path=None, **export_kwargs, ): from projects.PointRend import point_rend # isort:skip cfg = get_cfg() cfg.DATALOADER.NUM_WORKERS = 0 point_rend.add_pointrend_config(cfg) cfg.merge_from_file(config_path) cfg.freeze() model = build_model(cfg) image = get_sample_coco_image() inputs = tuple(image.clone() for _ in range(batch)) return self._test_model( model, inputs, inference_func, opset_version, save_onnx_graph_path, **export_kwargs ) ================================================ FILE: detectron2/tests/test_export_torchscript.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import glob import json import os import random import tempfile import unittest import zipfile import torch from torch import Tensor, nn from detectron2 import model_zoo from detectron2.config import get_cfg from detectron2.config.instantiate import dump_dataclass, instantiate from detectron2.export import dump_torchscript_IR, scripting_with_instances from detectron2.export.flatten import TracingAdapter, flatten_to_tuple from detectron2.export.torchscript_patch import patch_builtin_len from detectron2.layers import ShapeSpec from detectron2.modeling import build_backbone from detectron2.modeling.postprocessing import detector_postprocess from detectron2.modeling.roi_heads import KRCNNConvDeconvUpsampleHead from detectron2.structures import Boxes, Instances from detectron2.utils.env import TORCH_VERSION from detectron2.utils.testing import ( assert_instances_allclose, convert_scripted_instances, get_sample_coco_image, random_boxes, skipIfOnCPUCI, ) """ https://detectron2.readthedocs.io/tutorials/deployment.html contains some explanations of this file. """ class TestScripting(unittest.TestCase): def testMaskRCNNFPN(self): self._test_rcnn_model("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml") @skipIfOnCPUCI def testMaskRCNNC4(self): self._test_rcnn_model("COCO-InstanceSegmentation/mask_rcnn_R_50_C4_3x.yaml") def testRetinaNet(self): self._test_retinanet_model("COCO-Detection/retinanet_R_50_FPN_3x.yaml") def _test_rcnn_model(self, config_path): model = model_zoo.get(config_path, trained=True) model.eval() fields = { "proposal_boxes": Boxes, "objectness_logits": Tensor, "pred_boxes": Boxes, "scores": Tensor, "pred_classes": Tensor, "pred_masks": Tensor, } script_model = scripting_with_instances(model, fields) # Test that batch inference with different shapes are supported image = get_sample_coco_image() small_image = nn.functional.interpolate(image, scale_factor=0.5) inputs = [{"image": image}, {"image": small_image}] with torch.no_grad(): instance = model.inference(inputs, do_postprocess=False)[0] scripted_instance = script_model.inference(inputs, do_postprocess=False)[0] assert_instances_allclose(instance, scripted_instance) def _test_retinanet_model(self, config_path): model = model_zoo.get(config_path, trained=True) model.eval() fields = { "pred_boxes": Boxes, "scores": Tensor, "pred_classes": Tensor, } script_model = scripting_with_instances(model, fields) img = get_sample_coco_image() inputs = [{"image": img}] * 2 with torch.no_grad(): instance = model(inputs)[0]["instances"] scripted_instance = convert_scripted_instances(script_model(inputs)[0]) scripted_instance = detector_postprocess(scripted_instance, img.shape[1], img.shape[2]) assert_instances_allclose(instance, scripted_instance) # Note that the model currently cannot be saved and loaded into a new process: # https://github.com/pytorch/pytorch/issues/46944 # TODO: this test requires manifold access, see: T88318502 class TestTracing(unittest.TestCase): def testMaskRCNNFPN(self): def inference_func(model, image): inputs = [{"image": image}] return model.inference(inputs, do_postprocess=False)[0] self._test_model("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml", inference_func) def testMaskRCNNFPN_with_postproc(self): def inference_func(model, image): inputs = [{"image": image, "height": image.shape[1], "width": image.shape[2]}] return model.inference(inputs, do_postprocess=True)[0]["instances"] self._test_model("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml", inference_func) @skipIfOnCPUCI def testMaskRCNNC4(self): def inference_func(model, image): inputs = [{"image": image}] return model.inference(inputs, do_postprocess=False)[0] self._test_model("COCO-InstanceSegmentation/mask_rcnn_R_50_C4_3x.yaml", inference_func) @skipIfOnCPUCI def testCascadeRCNN(self): def inference_func(model, image): inputs = [{"image": image}] return model.inference(inputs, do_postprocess=False)[0] self._test_model("Misc/cascade_mask_rcnn_R_50_FPN_3x.yaml", inference_func) # bug fixed by https://github.com/pytorch/pytorch/pull/67734 @unittest.skipIf(TORCH_VERSION == (1, 10) and os.environ.get("CI"), "1.10 has bugs.") def testRetinaNet(self): def inference_func(model, image): return model.forward([{"image": image}])[0]["instances"] self._test_model("COCO-Detection/retinanet_R_50_FPN_3x.yaml", inference_func) def _check_torchscript_no_hardcoded_device(self, jitfile, extract_dir, device): zipfile.ZipFile(jitfile).extractall(extract_dir) dir_path = os.path.join(extract_dir, os.path.splitext(os.path.basename(jitfile))[0]) error_files = [] for f in glob.glob(f"{dir_path}/code/**/*.py", recursive=True): content = open(f).read() if device in content: error_files.append((f, content)) if len(error_files): msg = "\n".join(f"{f}\n{content}" for f, content in error_files) raise ValueError(f"Found device '{device}' in following files:\n{msg}") def _get_device_casting_test_cases(self, model): # Indexing operation can causes hardcoded device type before 1.10 if not TORCH_VERSION >= (1, 10) or torch.cuda.device_count() == 0: return [None] testing_devices = ["cpu", "cuda:0"] if torch.cuda.device_count() > 1: testing_devices.append(f"cuda:{torch.cuda.device_count() - 1}") assert str(model.device) in testing_devices testing_devices.remove(str(model.device)) testing_devices = [None] + testing_devices # test no casting first return testing_devices def _test_model(self, config_path, inference_func, batch=1): model = model_zoo.get(config_path, trained=True) image = get_sample_coco_image() inputs = tuple(image.clone() for _ in range(batch)) wrapper = TracingAdapter(model, inputs, inference_func) wrapper.eval() with torch.no_grad(): # trace with smaller images, and the trace must still work trace_inputs = tuple( nn.functional.interpolate(image, scale_factor=random.uniform(0.5, 0.7)) for _ in range(batch) ) traced_model = torch.jit.trace(wrapper, trace_inputs) testing_devices = self._get_device_casting_test_cases(model) # save and load back the model in order to show traceback of TorchScript with tempfile.TemporaryDirectory(prefix="detectron2_test") as d: basename = "model" jitfile = f"{d}/{basename}.jit" torch.jit.save(traced_model, jitfile) traced_model = torch.jit.load(jitfile) if any(device and "cuda" in device for device in testing_devices): self._check_torchscript_no_hardcoded_device(jitfile, d, "cuda") for device in testing_devices: print(f"Testing casting to {device} for inference (traced on {model.device}) ...") with torch.no_grad(): outputs = inference_func(copy.deepcopy(model).to(device), *inputs) traced_outputs = wrapper.outputs_schema(traced_model.to(device)(*inputs)) if batch > 1: for output, traced_output in zip(outputs, traced_outputs): assert_instances_allclose(output, traced_output, size_as_tensor=True) else: assert_instances_allclose(outputs, traced_outputs, size_as_tensor=True) @skipIfOnCPUCI def testMaskRCNNFPN_batched(self): def inference_func(model, image1, image2): inputs = [{"image": image1}, {"image": image2}] return model.inference(inputs, do_postprocess=False) self._test_model( "COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml", inference_func, batch=2 ) def testKeypointHead(self): class M(nn.Module): def __init__(self): super().__init__() self.model = KRCNNConvDeconvUpsampleHead( ShapeSpec(channels=4, height=14, width=14), num_keypoints=17, conv_dims=(4,) ) def forward(self, x, predbox1, predbox2): inst = [ Instances((100, 100), pred_boxes=Boxes(predbox1)), Instances((100, 100), pred_boxes=Boxes(predbox2)), ] ret = self.model(x, inst) return tuple(x.pred_keypoints for x in ret) model = M() model.eval() def gen_input(num1, num2): feat = torch.randn((num1 + num2, 4, 14, 14)) box1 = random_boxes(num1) box2 = random_boxes(num2) return feat, box1, box2 with torch.no_grad(), patch_builtin_len(): trace = torch.jit.trace(model, gen_input(15, 15), check_trace=False) inputs = gen_input(12, 10) trace_outputs = trace(*inputs) true_outputs = model(*inputs) for trace_output, true_output in zip(trace_outputs, true_outputs): self.assertTrue(torch.allclose(trace_output, true_output)) class TestTorchscriptUtils(unittest.TestCase): # TODO: add test to dump scripting def test_dump_IR_tracing(self): cfg = get_cfg() cfg.MODEL.RESNETS.DEPTH = 18 cfg.MODEL.RESNETS.RES2_OUT_CHANNELS = 64 class Mod(nn.Module): def forward(self, x): return tuple(self.m(x).values()) model = Mod() model.m = build_backbone(cfg) model.eval() with torch.no_grad(): ts_model = torch.jit.trace(model, (torch.rand(2, 3, 224, 224),)) with tempfile.TemporaryDirectory(prefix="detectron2_test") as d: dump_torchscript_IR(ts_model, d) # check that the files are created for name in ["model_ts_code", "model_ts_IR", "model_ts_IR_inlined", "model"]: fname = os.path.join(d, name + ".txt") self.assertTrue(os.stat(fname).st_size > 0, fname) def test_dump_IR_function(self): @torch.jit.script def gunc(x, y): return x + y def func(x, y): return x + y + gunc(x, y) ts_model = torch.jit.trace(func, (torch.rand(3), torch.rand(3))) with tempfile.TemporaryDirectory(prefix="detectron2_test") as d: dump_torchscript_IR(ts_model, d) for name in ["model_ts_code", "model_ts_IR", "model_ts_IR_inlined"]: fname = os.path.join(d, name + ".txt") self.assertTrue(os.stat(fname).st_size > 0, fname) def test_flatten_basic(self): obj = [3, ([5, 6], {"name": [7, 9], "name2": 3})] res, schema = flatten_to_tuple(obj) self.assertEqual(res, (3, 5, 6, 7, 9, 3)) new_obj = schema(res) self.assertEqual(new_obj, obj) _, new_schema = flatten_to_tuple(new_obj) self.assertEqual(schema, new_schema) # test __eq__ self._check_schema(schema) def _check_schema(self, schema): dumped_schema = dump_dataclass(schema) # Check that the schema is json-serializable # Although in reality you might want to use yaml because it often has many levels json.dumps(dumped_schema) # Check that the schema can be deserialized new_schema = instantiate(dumped_schema) self.assertEqual(schema, new_schema) def test_flatten_instances_boxes(self): inst = Instances( torch.tensor([5, 8]), pred_masks=torch.tensor([3]), pred_boxes=Boxes(torch.ones((1, 4))) ) obj = [3, ([5, 6], inst)] res, schema = flatten_to_tuple(obj) self.assertEqual(res[:3], (3, 5, 6)) for r, expected in zip(res[3:], (inst.pred_boxes.tensor, inst.pred_masks, inst.image_size)): self.assertIs(r, expected) new_obj = schema(res) assert_instances_allclose(new_obj[1][1], inst, rtol=0.0, size_as_tensor=True) self._check_schema(schema) def test_allow_non_tensor(self): data = (torch.tensor([5, 8]), 3) # contains non-tensor class M(nn.Module): def forward(self, input, number): return input model = M() with self.assertRaisesRegex(ValueError, "must only contain tensors"): adap = TracingAdapter(model, data, allow_non_tensor=False) adap = TracingAdapter(model, data, allow_non_tensor=True) _ = adap(*adap.flattened_inputs) newdata = (data[0].clone(),) with self.assertRaisesRegex(ValueError, "cannot generalize"): _ = adap(*newdata) ================================================ FILE: detectron2/tests/test_model_analysis.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest import torch from torch import nn from detectron2.utils.analysis import find_unused_parameters, flop_count_operators, parameter_count from detectron2.utils.testing import get_model_no_weights class RetinaNetTest(unittest.TestCase): def setUp(self): self.model = get_model_no_weights("COCO-Detection/retinanet_R_50_FPN_1x.yaml") def test_flop(self): # RetinaNet supports flop-counting with random inputs inputs = [{"image": torch.rand(3, 800, 800), "test_unused": "abcd"}] res = flop_count_operators(self.model, inputs) self.assertEqual(int(res["conv"]), 146) # 146B flops def test_param_count(self): res = parameter_count(self.model) self.assertEqual(res[""], 37915572) self.assertEqual(res["backbone"], 31452352) class FasterRCNNTest(unittest.TestCase): def setUp(self): self.model = get_model_no_weights("COCO-Detection/faster_rcnn_R_50_FPN_1x.yaml") def test_flop(self): # Faster R-CNN supports flop-counting with random inputs inputs = [{"image": torch.rand(3, 800, 800)}] res = flop_count_operators(self.model, inputs) # This only checks flops for backbone & proposal generator # Flops for box head is not conv, and depends on #proposals, which is # almost 0 for random inputs. self.assertEqual(int(res["conv"]), 117) def test_flop_with_output_shape(self): inputs = [{"image": torch.rand(3, 800, 800), "height": 700, "width": 700}] res = flop_count_operators(self.model, inputs) self.assertEqual(int(res["conv"]), 117) def test_param_count(self): res = parameter_count(self.model) self.assertEqual(res[""], 41699936) self.assertEqual(res["backbone"], 26799296) class MaskRCNNTest(unittest.TestCase): def setUp(self): self.model = get_model_no_weights("COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml") def test_flop(self): inputs1 = [{"image": torch.rand(3, 800, 800)}] inputs2 = [{"image": torch.rand(3, 800, 800), "height": 700, "width": 700}] for inputs in [inputs1, inputs2]: res = flop_count_operators(self.model, inputs) # The mask head could have extra conv flops, so total >= 117 self.assertGreaterEqual(int(res["conv"]), 117) class UnusedParamTest(unittest.TestCase): def test_unused(self): class TestMod(nn.Module): def __init__(self): super().__init__() self.fc1 = nn.Linear(10, 10) self.t = nn.Linear(10, 10) def forward(self, x): return self.fc1(x).mean() m = TestMod() ret = find_unused_parameters(m, torch.randn(10, 10)) self.assertEqual(set(ret), {"t.weight", "t.bias"}) ================================================ FILE: detectron2/tests/test_model_zoo.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import unittest from detectron2 import model_zoo from detectron2.config import instantiate from detectron2.modeling import FPN, GeneralizedRCNN logger = logging.getLogger(__name__) class TestModelZoo(unittest.TestCase): def test_get_returns_model(self): model = model_zoo.get("Misc/scratch_mask_rcnn_R_50_FPN_3x_gn.yaml", trained=False) self.assertIsInstance(model, GeneralizedRCNN) self.assertIsInstance(model.backbone, FPN) def test_get_invalid_model(self): self.assertRaises(RuntimeError, model_zoo.get, "Invalid/config.yaml") def test_get_url(self): url = model_zoo.get_checkpoint_url("Misc/scratch_mask_rcnn_R_50_FPN_3x_gn.yaml") self.assertEqual( url, "https://dl.fbaipublicfiles.com/detectron2/Misc/scratch_mask_rcnn_R_50_FPN_3x_gn/138602908/model_final_01ca85.pkl", # noqa ) url2 = model_zoo.get_checkpoint_url("Misc/scratch_mask_rcnn_R_50_FPN_3x_gn.py") self.assertEqual(url, url2) def _build_lazy_model(self, name): cfg = model_zoo.get_config("common/models/" + name) instantiate(cfg.model) def test_mask_rcnn_fpn(self): self._build_lazy_model("mask_rcnn_fpn.py") def test_mask_rcnn_c4(self): self._build_lazy_model("mask_rcnn_c4.py") def test_panoptic_fpn(self): self._build_lazy_model("panoptic_fpn.py") def test_schedule(self): cfg = model_zoo.get_config("common/coco_schedule.py") for _, v in cfg.items(): instantiate(v) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/test_packaging.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest from detectron2.utils.collect_env import collect_env_info class TestProjects(unittest.TestCase): def test_import(self): from detectron2.projects import point_rend _ = point_rend.add_pointrend_config import detectron2.projects.deeplab as deeplab _ = deeplab.add_deeplab_config # import detectron2.projects.panoptic_deeplab as panoptic_deeplab # _ = panoptic_deeplab.add_panoptic_deeplab_config class TestCollectEnv(unittest.TestCase): def test(self): _ = collect_env_info() ================================================ FILE: detectron2/tests/test_registry.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import unittest import torch from detectron2.modeling.meta_arch import GeneralizedRCNN from detectron2.utils.registry import _convert_target_to_string, locate class A: class B: pass class TestLocate(unittest.TestCase): def _test_obj(self, obj): name = _convert_target_to_string(obj) newobj = locate(name) self.assertIs(obj, newobj) def test_basic(self): self._test_obj(GeneralizedRCNN) def test_inside_class(self): # requires using __qualname__ instead of __name__ self._test_obj(A.B) def test_builtin(self): self._test_obj(len) self._test_obj(dict) def test_pytorch_optim(self): # pydoc.locate does not work for it self._test_obj(torch.optim.SGD) def test_failure(self): with self.assertRaises(ImportError): locate("asdf") def test_compress_target(self): from detectron2.data.transforms import RandomCrop name = _convert_target_to_string(RandomCrop) # name shouldn't contain 'augmentation_impl' self.assertEqual(name, "detectron2.data.transforms.RandomCrop") self.assertIs(RandomCrop, locate(name)) ================================================ FILE: detectron2/tests/test_scheduler.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import math import numpy as np from unittest import TestCase import torch from fvcore.common.param_scheduler import CosineParamScheduler, MultiStepParamScheduler from torch import nn from detectron2.solver import LRMultiplier, WarmupParamScheduler, build_lr_scheduler class TestScheduler(TestCase): def test_warmup_multistep(self): p = nn.Parameter(torch.zeros(0)) opt = torch.optim.SGD([p], lr=5) multiplier = WarmupParamScheduler( MultiStepParamScheduler( [1, 0.1, 0.01, 0.001], milestones=[10, 15, 20], num_updates=30, ), 0.001, 5 / 30, ) sched = LRMultiplier(opt, multiplier, 30) # This is an equivalent of: # sched = WarmupMultiStepLR( # opt, milestones=[10, 15, 20], gamma=0.1, warmup_factor=0.001, warmup_iters=5) p.sum().backward() opt.step() lrs = [0.005] for _ in range(30): sched.step() lrs.append(opt.param_groups[0]["lr"]) self.assertTrue(np.allclose(lrs[:5], [0.005, 1.004, 2.003, 3.002, 4.001])) self.assertTrue(np.allclose(lrs[5:10], 5.0)) self.assertTrue(np.allclose(lrs[10:15], 0.5)) self.assertTrue(np.allclose(lrs[15:20], 0.05)) self.assertTrue(np.allclose(lrs[20:], 0.005)) def test_warmup_cosine(self): p = nn.Parameter(torch.zeros(0)) opt = torch.optim.SGD([p], lr=5) multiplier = WarmupParamScheduler( CosineParamScheduler(1, 0), 0.001, 5 / 30, ) sched = LRMultiplier(opt, multiplier, 30) p.sum().backward() opt.step() self.assertEqual(opt.param_groups[0]["lr"], 0.005) lrs = [0.005] for _ in range(30): sched.step() lrs.append(opt.param_groups[0]["lr"]) for idx, lr in enumerate(lrs): expected_cosine = 2.5 * (1.0 + math.cos(math.pi * idx / 30)) if idx >= 5: self.assertAlmostEqual(lr, expected_cosine) else: self.assertNotAlmostEqual(lr, expected_cosine) def test_warmup_cosine_end_value(self): from detectron2.config import CfgNode, get_cfg def _test_end_value(cfg_dict): cfg = get_cfg() cfg.merge_from_other_cfg(CfgNode(cfg_dict)) p = nn.Parameter(torch.zeros(0)) opt = torch.optim.SGD([p], lr=cfg.SOLVER.BASE_LR) scheduler = build_lr_scheduler(cfg, opt) p.sum().backward() opt.step() self.assertEqual( opt.param_groups[0]["lr"], cfg.SOLVER.BASE_LR * cfg.SOLVER.WARMUP_FACTOR ) lrs = [] for _ in range(cfg.SOLVER.MAX_ITER): scheduler.step() lrs.append(opt.param_groups[0]["lr"]) self.assertAlmostEqual(lrs[-1], cfg.SOLVER.BASE_LR_END) _test_end_value( { "SOLVER": { "LR_SCHEDULER_NAME": "WarmupCosineLR", "MAX_ITER": 100, "WARMUP_ITERS": 10, "WARMUP_FACTOR": 0.1, "BASE_LR": 5.0, "BASE_LR_END": 0.0, } } ) _test_end_value( { "SOLVER": { "LR_SCHEDULER_NAME": "WarmupCosineLR", "MAX_ITER": 100, "WARMUP_ITERS": 10, "WARMUP_FACTOR": 0.1, "BASE_LR": 5.0, "BASE_LR_END": 0.5, } } ) ================================================ FILE: detectron2/tests/test_solver.py ================================================ import unittest from detectron2.solver.build import _expand_param_groups, reduce_param_groups class TestOptimizer(unittest.TestCase): def testExpandParamsGroups(self): params = [ { "params": ["p1", "p2", "p3", "p4"], "lr": 1.0, "weight_decay": 3.0, }, { "params": ["p2", "p3", "p5"], "lr": 2.0, "momentum": 2.0, }, { "params": ["p1"], "weight_decay": 4.0, }, ] out = _expand_param_groups(params) gt = [ dict(params=["p1"], lr=1.0, weight_decay=4.0), # noqa dict(params=["p2"], lr=2.0, weight_decay=3.0, momentum=2.0), # noqa dict(params=["p3"], lr=2.0, weight_decay=3.0, momentum=2.0), # noqa dict(params=["p4"], lr=1.0, weight_decay=3.0), # noqa dict(params=["p5"], lr=2.0, momentum=2.0), # noqa ] self.assertEqual(out, gt) def testReduceParamGroups(self): params = [ dict(params=["p1"], lr=1.0, weight_decay=4.0), # noqa dict(params=["p2", "p6"], lr=2.0, weight_decay=3.0, momentum=2.0), # noqa dict(params=["p3"], lr=2.0, weight_decay=3.0, momentum=2.0), # noqa dict(params=["p4"], lr=1.0, weight_decay=3.0), # noqa dict(params=["p5"], lr=2.0, momentum=2.0), # noqa ] gt_groups = [ { "lr": 1.0, "weight_decay": 4.0, "params": ["p1"], }, { "lr": 2.0, "weight_decay": 3.0, "momentum": 2.0, "params": ["p2", "p6", "p3"], }, { "lr": 1.0, "weight_decay": 3.0, "params": ["p4"], }, { "lr": 2.0, "momentum": 2.0, "params": ["p5"], }, ] out = reduce_param_groups(params) self.assertEqual(out, gt_groups) ================================================ FILE: detectron2/tests/test_visualizer.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import os import tempfile import unittest import cv2 import torch from detectron2.data import MetadataCatalog from detectron2.structures import BoxMode, Instances, RotatedBoxes from detectron2.utils.visualizer import ColorMode, Visualizer class TestVisualizer(unittest.TestCase): def _random_data(self): H, W = 100, 100 N = 10 img = np.random.rand(H, W, 3) * 255 boxxy = np.random.rand(N, 2) * (H // 2) boxes = np.concatenate((boxxy, boxxy + H // 2), axis=1) def _rand_poly(): return np.random.rand(3, 2).flatten() * H polygons = [[_rand_poly() for _ in range(np.random.randint(1, 5))] for _ in range(N)] mask = np.zeros_like(img[:, :, 0], dtype=np.bool) mask[:40, 10:20] = 1 labels = [str(i) for i in range(N)] return img, boxes, labels, polygons, [mask] * N @property def metadata(self): return MetadataCatalog.get("coco_2017_train") def test_draw_dataset_dict(self): img = np.random.rand(512, 512, 3) * 255 dic = { "annotations": [ { "bbox": [ 368.9946492271106, 330.891438763377, 13.148537455410235, 13.644708680142685, ], "bbox_mode": BoxMode.XYWH_ABS, "category_id": 0, "iscrowd": 1, "segmentation": { "counts": "_jh52m?2N2N2N2O100O10O001N1O2MceP2", "size": [512, 512], }, } ], "height": 512, "image_id": 1, "width": 512, } v = Visualizer(img) v.draw_dataset_dict(dic) v = Visualizer(img, self.metadata) v.draw_dataset_dict(dic) def test_draw_rotated_dataset_dict(self): img = np.random.rand(512, 512, 3) * 255 dic = { "annotations": [ { "bbox": [ 368.9946492271106, 330.891438763377, 13.148537455410235, 13.644708680142685, 45.0, ], "bbox_mode": BoxMode.XYWHA_ABS, "category_id": 0, "iscrowd": 1, } ], "height": 512, "image_id": 1, "width": 512, } v = Visualizer(img, self.metadata) v.draw_dataset_dict(dic) def test_overlay_instances(self): img, boxes, labels, polygons, masks = self._random_data() v = Visualizer(img, self.metadata) output = v.overlay_instances(masks=polygons, boxes=boxes, labels=labels).get_image() self.assertEqual(output.shape, img.shape) # Test 2x scaling v = Visualizer(img, self.metadata, scale=2.0) output = v.overlay_instances(masks=polygons, boxes=boxes, labels=labels).get_image() self.assertEqual(output.shape[0], img.shape[0] * 2) # Test overlay masks v = Visualizer(img, self.metadata) output = v.overlay_instances(masks=masks, boxes=boxes, labels=labels).get_image() self.assertEqual(output.shape, img.shape) def test_overlay_instances_no_boxes(self): img, boxes, labels, polygons, _ = self._random_data() v = Visualizer(img, self.metadata) v.overlay_instances(masks=polygons, boxes=None, labels=labels).get_image() def test_draw_instance_predictions(self): img, boxes, _, _, masks = self._random_data() num_inst = len(boxes) inst = Instances((img.shape[0], img.shape[1])) inst.pred_classes = torch.randint(0, 80, size=(num_inst,)) inst.scores = torch.rand(num_inst) inst.pred_boxes = torch.from_numpy(boxes) inst.pred_masks = torch.from_numpy(np.asarray(masks)) v = Visualizer(img) v.draw_instance_predictions(inst) v = Visualizer(img, self.metadata) v.draw_instance_predictions(inst) def test_BWmode_nomask(self): img, boxes, _, _, masks = self._random_data() num_inst = len(boxes) inst = Instances((img.shape[0], img.shape[1])) inst.pred_classes = torch.randint(0, 80, size=(num_inst,)) inst.scores = torch.rand(num_inst) inst.pred_boxes = torch.from_numpy(boxes) v = Visualizer(img, self.metadata, instance_mode=ColorMode.IMAGE_BW) v.draw_instance_predictions(inst) # check that output is grayscale inst = inst[:0] v = Visualizer(img, self.metadata, instance_mode=ColorMode.IMAGE_BW) output = v.draw_instance_predictions(inst).get_image() self.assertTrue(np.allclose(output[:, :, 0], output[:, :, 1])) self.assertTrue(np.allclose(output[:, :, 0], output[:, :, 2])) def test_draw_empty_mask_predictions(self): img, boxes, _, _, masks = self._random_data() num_inst = len(boxes) inst = Instances((img.shape[0], img.shape[1])) inst.pred_classes = torch.randint(0, 80, size=(num_inst,)) inst.scores = torch.rand(num_inst) inst.pred_boxes = torch.from_numpy(boxes) inst.pred_masks = torch.from_numpy(np.zeros_like(np.asarray(masks))) v = Visualizer(img, self.metadata) v.draw_instance_predictions(inst) def test_correct_output_shape(self): img = np.random.rand(928, 928, 3) * 255 v = Visualizer(img, self.metadata) out = v.output.get_image() self.assertEqual(out.shape, img.shape) def test_overlay_rotated_instances(self): H, W = 100, 150 img = np.random.rand(H, W, 3) * 255 num_boxes = 50 boxes_5d = torch.zeros(num_boxes, 5) boxes_5d[:, 0] = torch.FloatTensor(num_boxes).uniform_(-0.1 * W, 1.1 * W) boxes_5d[:, 1] = torch.FloatTensor(num_boxes).uniform_(-0.1 * H, 1.1 * H) boxes_5d[:, 2] = torch.FloatTensor(num_boxes).uniform_(0, max(W, H)) boxes_5d[:, 3] = torch.FloatTensor(num_boxes).uniform_(0, max(W, H)) boxes_5d[:, 4] = torch.FloatTensor(num_boxes).uniform_(-1800, 1800) rotated_boxes = RotatedBoxes(boxes_5d) labels = [str(i) for i in range(num_boxes)] v = Visualizer(img, self.metadata) output = v.overlay_instances(boxes=rotated_boxes, labels=labels).get_image() self.assertEqual(output.shape, img.shape) def test_draw_no_metadata(self): img, boxes, _, _, masks = self._random_data() num_inst = len(boxes) inst = Instances((img.shape[0], img.shape[1])) inst.pred_classes = torch.randint(0, 80, size=(num_inst,)) inst.scores = torch.rand(num_inst) inst.pred_boxes = torch.from_numpy(boxes) inst.pred_masks = torch.from_numpy(np.asarray(masks)) v = Visualizer(img, MetadataCatalog.get("asdfasdf")) v.draw_instance_predictions(inst) def test_draw_binary_mask(self): img, boxes, _, _, masks = self._random_data() img[:, :, 0] = 0 # remove red color mask = masks[0] mask_with_hole = np.zeros_like(mask).astype("uint8") mask_with_hole = cv2.rectangle(mask_with_hole, (10, 10), (50, 50), 1, 5) for m in [mask, mask_with_hole]: for save in [True, False]: v = Visualizer(img) o = v.draw_binary_mask(m, color="red", text="test") if save: with tempfile.TemporaryDirectory(prefix="detectron2_viz") as d: path = os.path.join(d, "output.png") o.save(path) o = cv2.imread(path)[:, :, ::-1] else: o = o.get_image().astype("float32") # red color is drawn on the image self.assertTrue(o[:, :, 0].sum() > 0) def test_draw_soft_mask(self): img = np.random.rand(100, 100, 3) * 255 img[:, :, 0] = 0 # remove red color mask = np.zeros((100, 100), dtype=np.float32) mask[30:50, 40:50] = 1.0 cv2.GaussianBlur(mask, (21, 21), 10) v = Visualizer(img) o = v.draw_soft_mask(mask, color="red", text="test") o = o.get_image().astype("float32") # red color is drawn on the image self.assertTrue(o[:, :, 0].sum() > 0) # test draw empty mask v = Visualizer(img) o = v.draw_soft_mask(np.zeros((100, 100), dtype=np.float32), color="red", text="test") o = o.get_image().astype("float32") def test_border_mask_with_holes(self): H, W = 200, 200 img = np.zeros((H, W, 3)) img[:, :, 0] = 255.0 v = Visualizer(img, scale=3) mask = np.zeros((H, W)) mask[:, 100:150] = 1 # create a hole, to trigger imshow mask = cv2.rectangle(mask, (110, 110), (130, 130), 0, thickness=-1) output = v.draw_binary_mask(mask, color="blue") output = output.get_image()[:, :, ::-1] first_row = {tuple(x.tolist()) for x in output[0]} last_row = {tuple(x.tolist()) for x in output[-1]} # Check quantization / off-by-1 error: the first and last row must have two colors self.assertEqual(len(last_row), 2) self.assertEqual(len(first_row), 2) self.assertIn((0, 0, 255), last_row) self.assertIn((0, 0, 255), first_row) def test_border_polygons(self): H, W = 200, 200 img = np.zeros((H, W, 3)) img[:, :, 0] = 255.0 v = Visualizer(img, scale=3) mask = np.zeros((H, W)) mask[:, 100:150] = 1 output = v.draw_binary_mask(mask, color="blue") output = output.get_image()[:, :, ::-1] first_row = {tuple(x.tolist()) for x in output[0]} last_row = {tuple(x.tolist()) for x in output[-1]} # Check quantization / off-by-1 error: # the first and last row must have >=2 colors, because the polygon # touches both rows self.assertGreaterEqual(len(last_row), 2) self.assertGreaterEqual(len(first_row), 2) self.assertIn((0, 0, 255), last_row) self.assertIn((0, 0, 255), first_row) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/tracking/__init__.py ================================================ ================================================ FILE: detectron2/tests/tracking/test_bbox_iou_tracker.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import unittest from copy import deepcopy from typing import Dict import torch from detectron2.config import CfgNode as CfgNode_ from detectron2.config import instantiate from detectron2.structures import Boxes, Instances from detectron2.tracking.base_tracker import build_tracker_head from detectron2.tracking.bbox_iou_tracker import BBoxIOUTracker # noqa class TestBBoxIOUTracker(unittest.TestCase): def setUp(self): self._img_size = np.array([600, 800]) self._prev_boxes = np.array( [ [101, 101, 200, 200], [301, 301, 450, 450], ] ).astype(np.float32) self._prev_scores = np.array([0.9, 0.9]) self._prev_classes = np.array([1, 1]) self._prev_masks = np.ones((2, 600, 800)).astype("uint8") self._curr_boxes = np.array( [ [302, 303, 451, 452], [101, 102, 201, 203], ] ).astype(np.float32) self._curr_scores = np.array([0.95, 0.85]) self._curr_classes = np.array([1, 1]) self._curr_masks = np.ones((2, 600, 800)).astype("uint8") self._prev_instances = { "image_size": self._img_size, "pred_boxes": self._prev_boxes, "scores": self._prev_scores, "pred_classes": self._prev_classes, "pred_masks": self._prev_masks, } self._prev_instances = self._convertDictPredictionToInstance(self._prev_instances) self._curr_instances = { "image_size": self._img_size, "pred_boxes": self._curr_boxes, "scores": self._curr_scores, "pred_classes": self._curr_classes, "pred_masks": self._curr_masks, } self._curr_instances = self._convertDictPredictionToInstance(self._curr_instances) self._max_num_instances = 200 self._max_lost_frame_count = 0 self._min_box_rel_dim = 0.02 self._min_instance_period = 1 self._track_iou_threshold = 0.5 def _convertDictPredictionToInstance(self, prediction: Dict) -> Instances: """ convert prediction from Dict to D2 Instances format """ res = Instances( image_size=torch.IntTensor(prediction["image_size"]), pred_boxes=Boxes(torch.FloatTensor(prediction["pred_boxes"])), pred_masks=torch.IntTensor(prediction["pred_masks"]), pred_classes=torch.IntTensor(prediction["pred_classes"]), scores=torch.FloatTensor(prediction["scores"]), ) return res def test_init(self): cfg = { "_target_": "detectron2.tracking.bbox_iou_tracker.BBoxIOUTracker", "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) self.assertTrue(tracker._video_height == self._img_size[0]) def test_from_config(self): cfg = CfgNode_() cfg.TRACKER_HEADS = CfgNode_() cfg.TRACKER_HEADS.TRACKER_NAME = "BBoxIOUTracker" cfg.TRACKER_HEADS.VIDEO_HEIGHT = int(self._img_size[0]) cfg.TRACKER_HEADS.VIDEO_WIDTH = int(self._img_size[1]) cfg.TRACKER_HEADS.MAX_NUM_INSTANCES = self._max_num_instances cfg.TRACKER_HEADS.MAX_LOST_FRAME_COUNT = self._max_lost_frame_count cfg.TRACKER_HEADS.MIN_BOX_REL_DIM = self._min_box_rel_dim cfg.TRACKER_HEADS.MIN_INSTANCE_PERIOD = self._min_instance_period cfg.TRACKER_HEADS.TRACK_IOU_THRESHOLD = self._track_iou_threshold tracker = build_tracker_head(cfg) self.assertTrue(tracker._video_height == self._img_size[0]) def test_initialize_extra_fields(self): cfg = { "_target_": "detectron2.tracking.bbox_iou_tracker.BBoxIOUTracker", "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) instances = tracker._initialize_extra_fields(self._curr_instances) self.assertTrue(instances.has("ID")) self.assertTrue(instances.has("ID_period")) self.assertTrue(instances.has("lost_frame_count")) def test_assign_new_id(self): cfg = { "_target_": "detectron2.tracking.bbox_iou_tracker.BBoxIOUTracker", "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) instances = deepcopy(self._curr_instances) instances = tracker._initialize_extra_fields(instances) instances = tracker._assign_new_id(instances) self.assertTrue(len(instances.ID) == 2) self.assertTrue(instances.ID[0] == 2) self.assertTrue(instances.ID[1] == 3) def test_update(self): cfg = { "_target_": "detectron2.tracking.bbox_iou_tracker.BBoxIOUTracker", "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) prev_instances = tracker.update(self._prev_instances) self.assertTrue(len(prev_instances.ID) == 2) self.assertTrue(prev_instances.ID[0] == 0) self.assertTrue(prev_instances.ID[1] == 1) curr_instances = tracker.update(self._curr_instances) self.assertTrue(len(curr_instances.ID) == 2) self.assertTrue(curr_instances.ID[0] == 1) self.assertTrue(curr_instances.ID[1] == 0) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/tracking/test_hungarian_tracker.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import unittest from typing import Dict import torch from detectron2.config import instantiate from detectron2.structures import Boxes, Instances class TestBaseHungarianTracker(unittest.TestCase): def setUp(self): self._img_size = np.array([600, 800]) self._prev_boxes = np.array( [ [101, 101, 200, 200], [301, 301, 450, 450], ] ).astype(np.float32) self._prev_scores = np.array([0.9, 0.9]) self._prev_classes = np.array([1, 1]) self._prev_masks = np.ones((2, 600, 800)).astype("uint8") self._curr_boxes = np.array( [ [302, 303, 451, 452], [101, 102, 201, 203], ] ).astype(np.float32) self._curr_scores = np.array([0.95, 0.85]) self._curr_classes = np.array([1, 1]) self._curr_masks = np.ones((2, 600, 800)).astype("uint8") self._prev_instances = { "image_size": self._img_size, "pred_boxes": self._prev_boxes, "scores": self._prev_scores, "pred_classes": self._prev_classes, "pred_masks": self._prev_masks, } self._prev_instances = self._convertDictPredictionToInstance(self._prev_instances) self._curr_instances = { "image_size": self._img_size, "pred_boxes": self._curr_boxes, "scores": self._curr_scores, "pred_classes": self._curr_classes, "pred_masks": self._curr_masks, } self._curr_instances = self._convertDictPredictionToInstance(self._curr_instances) self._max_num_instances = 200 self._max_lost_frame_count = 0 self._min_box_rel_dim = 0.02 self._min_instance_period = 1 self._track_iou_threshold = 0.5 def _convertDictPredictionToInstance(self, prediction: Dict) -> Instances: """ convert prediction from Dict to D2 Instances format """ res = Instances( image_size=torch.IntTensor(prediction["image_size"]), pred_boxes=Boxes(torch.FloatTensor(prediction["pred_boxes"])), pred_masks=torch.IntTensor(prediction["pred_masks"]), pred_classes=torch.IntTensor(prediction["pred_classes"]), scores=torch.FloatTensor(prediction["scores"]), ) return res def test_init(self): cfg = { "_target_": "detectron2.tracking.hungarian_tracker.BaseHungarianTracker", "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) self.assertTrue(tracker._video_height == self._img_size[0]) def test_initialize_extra_fields(self): cfg = { "_target_": "detectron2.tracking.hungarian_tracker.BaseHungarianTracker", "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) instances = tracker._initialize_extra_fields(self._curr_instances) self.assertTrue(instances.has("ID")) self.assertTrue(instances.has("ID_period")) self.assertTrue(instances.has("lost_frame_count")) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/tracking/test_iou_weighted_hungarian_bbox_iou_tracker.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import numpy as np import unittest from typing import Dict import torch from detectron2.config import CfgNode as CfgNode_ from detectron2.config import instantiate from detectron2.structures import Boxes, Instances from detectron2.tracking.base_tracker import build_tracker_head from detectron2.tracking.iou_weighted_hungarian_bbox_iou_tracker import ( # noqa IOUWeightedHungarianBBoxIOUTracker, ) class TestIOUWeightedHungarianBBoxIOUTracker(unittest.TestCase): def setUp(self): self._img_size = np.array([600, 800]) self._prev_boxes = np.array( [ [101, 101, 200, 200], [301, 301, 450, 450], ] ).astype(np.float32) self._prev_scores = np.array([0.9, 0.9]) self._prev_classes = np.array([1, 1]) self._prev_masks = np.ones((2, 600, 800)).astype("uint8") self._curr_boxes = np.array( [ [302, 303, 451, 452], [101, 102, 201, 203], ] ).astype(np.float32) self._curr_scores = np.array([0.95, 0.85]) self._curr_classes = np.array([1, 1]) self._curr_masks = np.ones((2, 600, 800)).astype("uint8") self._prev_instances = { "image_size": self._img_size, "pred_boxes": self._prev_boxes, "scores": self._prev_scores, "pred_classes": self._prev_classes, "pred_masks": self._prev_masks, } self._prev_instances = self._convertDictPredictionToInstance(self._prev_instances) self._curr_instances = { "image_size": self._img_size, "pred_boxes": self._curr_boxes, "scores": self._curr_scores, "pred_classes": self._curr_classes, "pred_masks": self._curr_masks, } self._curr_instances = self._convertDictPredictionToInstance(self._curr_instances) self._max_num_instances = 10 self._max_lost_frame_count = 3 self._min_box_rel_dim = 0.02 self._min_instance_period = 1 self._track_iou_threshold = 0.5 def _convertDictPredictionToInstance(self, prediction: Dict) -> Instances: """ convert prediction from Dict to D2 Instances format """ res = Instances( image_size=torch.IntTensor(prediction["image_size"]), pred_boxes=Boxes(torch.FloatTensor(prediction["pred_boxes"])), pred_masks=torch.IntTensor(prediction["pred_masks"]), pred_classes=torch.IntTensor(prediction["pred_classes"]), scores=torch.FloatTensor(prediction["scores"]), ) return res def test_init(self): cfg = { "_target_": "detectron2.tracking.iou_weighted_hungarian_bbox_iou_tracker.IOUWeightedHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) self.assertTrue(tracker._video_height == self._img_size[0]) def test_from_config(self): cfg = CfgNode_() cfg.TRACKER_HEADS = CfgNode_() cfg.TRACKER_HEADS.TRACKER_NAME = "IOUWeightedHungarianBBoxIOUTracker" cfg.TRACKER_HEADS.VIDEO_HEIGHT = int(self._img_size[0]) cfg.TRACKER_HEADS.VIDEO_WIDTH = int(self._img_size[1]) cfg.TRACKER_HEADS.MAX_NUM_INSTANCES = self._max_num_instances cfg.TRACKER_HEADS.MAX_LOST_FRAME_COUNT = self._max_lost_frame_count cfg.TRACKER_HEADS.MIN_BOX_REL_DIM = self._min_box_rel_dim cfg.TRACKER_HEADS.MIN_INSTANCE_PERIOD = self._min_instance_period cfg.TRACKER_HEADS.TRACK_IOU_THRESHOLD = self._track_iou_threshold tracker = build_tracker_head(cfg) self.assertTrue(tracker._video_height == self._img_size[0]) def test_initialize_extra_fields(self): cfg = { "_target_": "detectron2.tracking.iou_weighted_hungarian_bbox_iou_tracker.IOUWeightedHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) instances = tracker._initialize_extra_fields(self._curr_instances) self.assertTrue(instances.has("ID")) self.assertTrue(instances.has("ID_period")) self.assertTrue(instances.has("lost_frame_count")) def test_process_matched_idx(self): cfg = { "_target_": "detectron2.tracking.iou_weighted_hungarian_bbox_iou_tracker.IOUWeightedHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) prev_instances = tracker._initialize_extra_fields(self._prev_instances) tracker._prev_instances = prev_instances curr_instances = tracker._initialize_extra_fields(self._curr_instances) matched_idx = np.array([0]) matched_prev_idx = np.array([1]) curr_instances = tracker._process_matched_idx(curr_instances, matched_idx, matched_prev_idx) self.assertTrue(curr_instances.ID[0] == 1) def test_process_unmatched_idx(self): cfg = { "_target_": "detectron2.tracking.iou_weighted_hungarian_bbox_iou_tracker.IOUWeightedHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) prev_instances = tracker._initialize_extra_fields(self._prev_instances) tracker._prev_instances = prev_instances curr_instances = tracker._initialize_extra_fields(self._curr_instances) matched_idx = np.array([0]) matched_prev_idx = np.array([1]) curr_instances = tracker._process_matched_idx(curr_instances, matched_idx, matched_prev_idx) curr_instances = tracker._process_unmatched_idx(curr_instances, matched_idx) self.assertTrue(curr_instances.ID[1] == 2) def test_process_unmatched_prev_idx(self): cfg = { "_target_": "detectron2.tracking.iou_weighted_hungarian_bbox_iou_tracker.IOUWeightedHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) prev_instances = tracker._initialize_extra_fields(self._prev_instances) prev_instances.ID_period = [3, 3] tracker._prev_instances = prev_instances curr_instances = tracker._initialize_extra_fields(self._curr_instances) matched_idx = np.array([0]) matched_prev_idx = np.array([1]) curr_instances = tracker._process_matched_idx(curr_instances, matched_idx, matched_prev_idx) curr_instances = tracker._process_unmatched_idx(curr_instances, matched_idx) curr_instances = tracker._process_unmatched_prev_idx(curr_instances, matched_prev_idx) self.assertTrue(curr_instances.ID[2] == 0) def test_assign_cost_matrix_values(self): cfg = { "_target_": "detectron2.tracking.iou_weighted_hungarian_bbox_iou_tracker.IOUWeightedHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) pair1 = {"idx": 0, "prev_idx": 1, "IoU": 0.6} pair2 = {"idx": 1, "prev_idx": 0, "IoU": 0.8} bbox_pairs = [pair1, pair2] cost_matrix = np.full((2, 2), np.inf) target_matrix = copy.deepcopy(cost_matrix) target_matrix[0, 1] = -0.6 target_matrix[1, 0] = -0.8 cost_matrix = tracker.assign_cost_matrix_values(cost_matrix, bbox_pairs) self.assertTrue(np.allclose(cost_matrix, target_matrix)) def test_update(self): cfg = { "_target_": "detectron2.tracking.iou_weighted_hungarian_bbox_iou_tracker.IOUWeightedHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) _ = tracker.update(self._prev_instances) curr_instances = tracker.update(self._curr_instances) self.assertTrue(curr_instances.ID[0] == 1) self.assertTrue(curr_instances.ID[1] == 0) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tests/tracking/test_vanilla_hungarian_bbox_iou_tracker.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import numpy as np import unittest from typing import Dict import torch from detectron2.config import CfgNode as CfgNode_ from detectron2.config import instantiate from detectron2.structures import Boxes, Instances from detectron2.tracking.base_tracker import build_tracker_head from detectron2.tracking.vanilla_hungarian_bbox_iou_tracker import ( # noqa VanillaHungarianBBoxIOUTracker, ) class TestVanillaHungarianBBoxIOUTracker(unittest.TestCase): def setUp(self): self._img_size = np.array([600, 800]) self._prev_boxes = np.array( [ [101, 101, 200, 200], [301, 301, 450, 450], ] ).astype(np.float32) self._prev_scores = np.array([0.9, 0.9]) self._prev_classes = np.array([1, 1]) self._prev_masks = np.ones((2, 600, 800)).astype("uint8") self._curr_boxes = np.array( [ [302, 303, 451, 452], [101, 102, 201, 203], ] ).astype(np.float32) self._curr_scores = np.array([0.95, 0.85]) self._curr_classes = np.array([1, 1]) self._curr_masks = np.ones((2, 600, 800)).astype("uint8") self._prev_instances = { "image_size": self._img_size, "pred_boxes": self._prev_boxes, "scores": self._prev_scores, "pred_classes": self._prev_classes, "pred_masks": self._prev_masks, } self._prev_instances = self._convertDictPredictionToInstance(self._prev_instances) self._curr_instances = { "image_size": self._img_size, "pred_boxes": self._curr_boxes, "scores": self._curr_scores, "pred_classes": self._curr_classes, "pred_masks": self._curr_masks, } self._curr_instances = self._convertDictPredictionToInstance(self._curr_instances) self._max_num_instances = 10 self._max_lost_frame_count = 3 self._min_box_rel_dim = 0.02 self._min_instance_period = 1 self._track_iou_threshold = 0.5 def _convertDictPredictionToInstance(self, prediction: Dict) -> Instances: """ convert prediction from Dict to D2 Instances format """ res = Instances( image_size=torch.IntTensor(prediction["image_size"]), pred_boxes=Boxes(torch.FloatTensor(prediction["pred_boxes"])), pred_masks=torch.IntTensor(prediction["pred_masks"]), pred_classes=torch.IntTensor(prediction["pred_classes"]), scores=torch.FloatTensor(prediction["scores"]), ) return res def test_init(self): cfg = { "_target_": "detectron2.tracking.vanilla_hungarian_bbox_iou_tracker.VanillaHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) self.assertTrue(tracker._video_height == self._img_size[0]) def test_from_config(self): cfg = CfgNode_() cfg.TRACKER_HEADS = CfgNode_() cfg.TRACKER_HEADS.TRACKER_NAME = "VanillaHungarianBBoxIOUTracker" cfg.TRACKER_HEADS.VIDEO_HEIGHT = int(self._img_size[0]) cfg.TRACKER_HEADS.VIDEO_WIDTH = int(self._img_size[1]) cfg.TRACKER_HEADS.MAX_NUM_INSTANCES = self._max_num_instances cfg.TRACKER_HEADS.MAX_LOST_FRAME_COUNT = self._max_lost_frame_count cfg.TRACKER_HEADS.MIN_BOX_REL_DIM = self._min_box_rel_dim cfg.TRACKER_HEADS.MIN_INSTANCE_PERIOD = self._min_instance_period cfg.TRACKER_HEADS.TRACK_IOU_THRESHOLD = self._track_iou_threshold tracker = build_tracker_head(cfg) self.assertTrue(tracker._video_height == self._img_size[0]) def test_initialize_extra_fields(self): cfg = { "_target_": "detectron2.tracking.vanilla_hungarian_bbox_iou_tracker.VanillaHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) instances = tracker._initialize_extra_fields(self._curr_instances) self.assertTrue(instances.has("ID")) self.assertTrue(instances.has("ID_period")) self.assertTrue(instances.has("lost_frame_count")) def test_process_matched_idx(self): cfg = { "_target_": "detectron2.tracking.vanilla_hungarian_bbox_iou_tracker.VanillaHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) prev_instances = tracker._initialize_extra_fields(self._prev_instances) tracker._prev_instances = prev_instances curr_instances = tracker._initialize_extra_fields(self._curr_instances) matched_idx = np.array([0]) matched_prev_idx = np.array([1]) curr_instances = tracker._process_matched_idx(curr_instances, matched_idx, matched_prev_idx) self.assertTrue(curr_instances.ID[0] == 1) def test_process_unmatched_idx(self): cfg = { "_target_": "detectron2.tracking.vanilla_hungarian_bbox_iou_tracker.VanillaHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) prev_instances = tracker._initialize_extra_fields(self._prev_instances) tracker._prev_instances = prev_instances curr_instances = tracker._initialize_extra_fields(self._curr_instances) matched_idx = np.array([0]) matched_prev_idx = np.array([1]) curr_instances = tracker._process_matched_idx(curr_instances, matched_idx, matched_prev_idx) curr_instances = tracker._process_unmatched_idx(curr_instances, matched_idx) self.assertTrue(curr_instances.ID[1] == 2) def test_process_unmatched_prev_idx(self): cfg = { "_target_": "detectron2.tracking.vanilla_hungarian_bbox_iou_tracker.VanillaHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) prev_instances = tracker._initialize_extra_fields(self._prev_instances) prev_instances.ID_period = [3, 3] tracker._prev_instances = prev_instances curr_instances = tracker._initialize_extra_fields(self._curr_instances) matched_idx = np.array([0]) matched_prev_idx = np.array([1]) curr_instances = tracker._process_matched_idx(curr_instances, matched_idx, matched_prev_idx) curr_instances = tracker._process_unmatched_idx(curr_instances, matched_idx) curr_instances = tracker._process_unmatched_prev_idx(curr_instances, matched_prev_idx) self.assertTrue(curr_instances.ID[2] == 0) def test_assign_cost_matrix_values(self): cfg = { "_target_": "detectron2.tracking.vanilla_hungarian_bbox_iou_tracker.VanillaHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) pair1 = {"idx": 0, "prev_idx": 1} pair2 = {"idx": 1, "prev_idx": 0} bbox_pairs = [pair1, pair2] cost_matrix = np.full((2, 2), np.inf) target_matrix = copy.deepcopy(cost_matrix) target_matrix[0, 1] = -1 target_matrix[1, 0] = -1 cost_matrix = tracker.assign_cost_matrix_values(cost_matrix, bbox_pairs) self.assertTrue(np.allclose(cost_matrix, target_matrix)) def test_update(self): cfg = { "_target_": "detectron2.tracking.vanilla_hungarian_bbox_iou_tracker.VanillaHungarianBBoxIOUTracker", # noqa "video_height": self._img_size[0], "video_width": self._img_size[1], "max_num_instances": self._max_num_instances, "max_lost_frame_count": self._max_lost_frame_count, "min_box_rel_dim": self._min_box_rel_dim, "min_instance_period": self._min_instance_period, "track_iou_threshold": self._track_iou_threshold, } tracker = instantiate(cfg) _ = tracker.update(self._prev_instances) curr_instances = tracker.update(self._curr_instances) self.assertTrue(curr_instances.ID[0] == 1) self.assertTrue(curr_instances.ID[1] == 0) if __name__ == "__main__": unittest.main() ================================================ FILE: detectron2/tools/README.md ================================================ This directory contains a few example scripts that demonstrate features of detectron2. * `train_net.py` An example training script that's made to train builtin models of detectron2. For usage, see [GETTING_STARTED.md](../GETTING_STARTED.md). * `plain_train_net.py` Similar to `train_net.py`, but implements a training loop instead of using `Trainer`. This script includes fewer features but it may be more friendly to hackers. * `benchmark.py` Benchmark the training speed, inference speed or data loading speed of a given config. Usage: ``` python benchmark.py --config-file config.yaml --task train/eval/data [optional DDP flags] ``` * `analyze_model.py` Analyze FLOPs, parameters, activations of a detectron2 model. See its `--help` for usage. * `visualize_json_results.py` Visualize the json instance detection/segmentation results dumped by `COCOEvalutor` or `LVISEvaluator` Usage: ``` python visualize_json_results.py --input x.json --output dir/ --dataset coco_2017_val ``` If not using a builtin dataset, you'll need your own script or modify this script. * `visualize_data.py` Visualize ground truth raw annotations or training data (after preprocessing/augmentations). Usage: ``` python visualize_data.py --config-file config.yaml --source annotation/dataloader --output-dir dir/ [--show] ``` NOTE: the script does not stop by itself when using `--source dataloader` because a training dataloader is usually infinite. ================================================ FILE: detectron2/tools/__init__.py ================================================ ================================================ FILE: detectron2/tools/analyze_model.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np from collections import Counter import tqdm from fvcore.nn import flop_count_table # can also try flop_count_str from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import CfgNode, LazyConfig, get_cfg, instantiate from detectron2.data import build_detection_test_loader from detectron2.engine import default_argument_parser from detectron2.modeling import build_model from detectron2.utils.analysis import ( FlopCountAnalysis, activation_count_operators, parameter_count_table, ) from detectron2.utils.logger import setup_logger logger = logging.getLogger("detectron2") def setup(args): if args.config_file.endswith(".yaml"): cfg = get_cfg() cfg.merge_from_file(args.config_file) cfg.DATALOADER.NUM_WORKERS = 0 cfg.merge_from_list(args.opts) cfg.freeze() else: cfg = LazyConfig.load(args.config_file) cfg = LazyConfig.apply_overrides(cfg, args.opts) setup_logger(name="fvcore") setup_logger() return cfg def do_flop(cfg): if isinstance(cfg, CfgNode): data_loader = build_detection_test_loader(cfg, cfg.DATASETS.TEST[0]) model = build_model(cfg) DetectionCheckpointer(model).load(cfg.MODEL.WEIGHTS) else: data_loader = instantiate(cfg.dataloader.test) model = instantiate(cfg.model) model.to(cfg.train.device) DetectionCheckpointer(model).load(cfg.train.init_checkpoint) model.eval() counts = Counter() total_flops = [] for idx, data in zip(tqdm.trange(args.num_inputs), data_loader): # noqa flops = FlopCountAnalysis(model, data) if idx > 0: flops.unsupported_ops_warnings(False).uncalled_modules_warnings(False) counts += flops.by_operator() total_flops.append(flops.total()) logger.info("Flops table computed from only one input sample:\n" + flop_count_table(flops)) logger.info( "Average GFlops for each type of operators:\n" + str([(k, v / (idx + 1) / 1e9) for k, v in counts.items()]) ) logger.info( "Total GFlops: {:.1f}±{:.1f}".format(np.mean(total_flops) / 1e9, np.std(total_flops) / 1e9) ) def do_activation(cfg): if isinstance(cfg, CfgNode): data_loader = build_detection_test_loader(cfg, cfg.DATASETS.TEST[0]) model = build_model(cfg) DetectionCheckpointer(model).load(cfg.MODEL.WEIGHTS) else: data_loader = instantiate(cfg.dataloader.test) model = instantiate(cfg.model) model.to(cfg.train.device) DetectionCheckpointer(model).load(cfg.train.init_checkpoint) model.eval() counts = Counter() total_activations = [] for idx, data in zip(tqdm.trange(args.num_inputs), data_loader): # noqa count = activation_count_operators(model, data) counts += count total_activations.append(sum(count.values())) logger.info( "(Million) Activations for Each Type of Operators:\n" + str([(k, v / idx) for k, v in counts.items()]) ) logger.info( "Total (Million) Activations: {}±{}".format( np.mean(total_activations), np.std(total_activations) ) ) def do_parameter(cfg): if isinstance(cfg, CfgNode): model = build_model(cfg) else: model = instantiate(cfg.model) logger.info("Parameter Count:\n" + parameter_count_table(model, max_depth=5)) def do_structure(cfg): if isinstance(cfg, CfgNode): model = build_model(cfg) else: model = instantiate(cfg.model) logger.info("Model Structure:\n" + str(model)) if __name__ == "__main__": parser = default_argument_parser( epilog=""" Examples: To show parameters of a model: $ ./analyze_model.py --tasks parameter \\ --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml Flops and activations are data-dependent, therefore inputs and model weights are needed to count them: $ ./analyze_model.py --num-inputs 100 --tasks flop \\ --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_1x.yaml \\ MODEL.WEIGHTS /path/to/model.pkl """ ) parser.add_argument( "--tasks", choices=["flop", "activation", "parameter", "structure"], required=True, nargs="+", ) parser.add_argument( "-n", "--num-inputs", default=100, type=int, help="number of inputs used to compute statistics for flops/activations, " "both are data dependent.", ) args = parser.parse_args() assert not args.eval_only assert args.num_gpus == 1 cfg = setup(args) for task in args.tasks: { "flop": do_flop, "activation": do_activation, "parameter": do_parameter, "structure": do_structure, }[task](cfg) ================================================ FILE: detectron2/tools/benchmark.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. """ A script to benchmark builtin models. Note: this script has an extra dependency of psutil. """ import itertools import logging import psutil import torch import tqdm from fvcore.common.timer import Timer from torch.nn.parallel import DistributedDataParallel from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import LazyConfig, get_cfg, instantiate from detectron2.data import ( DatasetFromList, build_detection_test_loader, build_detection_train_loader, ) from detectron2.data.benchmark import DataLoaderBenchmark from detectron2.engine import AMPTrainer, SimpleTrainer, default_argument_parser, hooks, launch from detectron2.modeling import build_model from detectron2.solver import build_optimizer from detectron2.utils import comm from detectron2.utils.collect_env import collect_env_info from detectron2.utils.events import CommonMetricPrinter from detectron2.utils.logger import setup_logger logger = logging.getLogger("detectron2") def setup(args): if args.config_file.endswith(".yaml"): cfg = get_cfg() cfg.merge_from_file(args.config_file) cfg.SOLVER.BASE_LR = 0.001 # Avoid NaNs. Not useful in this script anyway. cfg.merge_from_list(args.opts) cfg.freeze() else: cfg = LazyConfig.load(args.config_file) cfg = LazyConfig.apply_overrides(cfg, args.opts) setup_logger(distributed_rank=comm.get_rank()) return cfg def create_data_benchmark(cfg, args): if args.config_file.endswith(".py"): dl_cfg = cfg.dataloader.train dl_cfg._target_ = DataLoaderBenchmark return instantiate(dl_cfg) else: kwargs = build_detection_train_loader.from_config(cfg) kwargs.pop("aspect_ratio_grouping", None) kwargs["_target_"] = DataLoaderBenchmark return instantiate(kwargs) def RAM_msg(): vram = psutil.virtual_memory() return "RAM Usage: {:.2f}/{:.2f} GB".format( (vram.total - vram.available) / 1024**3, vram.total / 1024**3 ) def benchmark_data(args): cfg = setup(args) logger.info("After spawning " + RAM_msg()) benchmark = create_data_benchmark(cfg, args) benchmark.benchmark_distributed(250, 10) # test for a few more rounds for k in range(10): logger.info(f"Iteration {k} " + RAM_msg()) benchmark.benchmark_distributed(250, 1) def benchmark_data_advanced(args): # benchmark dataloader with more details to help analyze performance bottleneck cfg = setup(args) benchmark = create_data_benchmark(cfg, args) if comm.get_rank() == 0: benchmark.benchmark_dataset(100) benchmark.benchmark_mapper(100) benchmark.benchmark_workers(100, warmup=10) benchmark.benchmark_IPC(100, warmup=10) if comm.get_world_size() > 1: benchmark.benchmark_distributed(100) logger.info("Rerun ...") benchmark.benchmark_distributed(100) def benchmark_train(args): cfg = setup(args) model = build_model(cfg) logger.info("Model:\n{}".format(model)) if comm.get_world_size() > 1: model = DistributedDataParallel( model, device_ids=[comm.get_local_rank()], broadcast_buffers=False ) optimizer = build_optimizer(cfg, model) checkpointer = DetectionCheckpointer(model, optimizer=optimizer) checkpointer.load(cfg.MODEL.WEIGHTS) cfg.defrost() cfg.DATALOADER.NUM_WORKERS = 2 data_loader = build_detection_train_loader(cfg) dummy_data = list(itertools.islice(data_loader, 100)) def f(): data = DatasetFromList(dummy_data, copy=False, serialize=False) while True: yield from data max_iter = 400 trainer = (AMPTrainer if cfg.SOLVER.AMP.ENABLED else SimpleTrainer)(model, f(), optimizer) trainer.register_hooks( [ hooks.IterationTimer(), hooks.PeriodicWriter([CommonMetricPrinter(max_iter)]), hooks.TorchProfiler( lambda trainer: trainer.iter == max_iter - 1, cfg.OUTPUT_DIR, save_tensorboard=True ), ] ) trainer.train(1, max_iter) @torch.no_grad() def benchmark_eval(args): cfg = setup(args) if args.config_file.endswith(".yaml"): model = build_model(cfg) DetectionCheckpointer(model).load(cfg.MODEL.WEIGHTS) cfg.defrost() cfg.DATALOADER.NUM_WORKERS = 0 data_loader = build_detection_test_loader(cfg, cfg.DATASETS.TEST[0]) else: model = instantiate(cfg.model) model.to(cfg.train.device) DetectionCheckpointer(model).load(cfg.train.init_checkpoint) cfg.dataloader.num_workers = 0 data_loader = instantiate(cfg.dataloader.test) model.eval() logger.info("Model:\n{}".format(model)) dummy_data = DatasetFromList(list(itertools.islice(data_loader, 100)), copy=False) def f(): while True: yield from dummy_data for k in range(5): # warmup model(dummy_data[k]) max_iter = 300 timer = Timer() with tqdm.tqdm(total=max_iter) as pbar: for idx, d in enumerate(f()): if idx == max_iter: break model(d) pbar.update() logger.info("{} iters in {} seconds.".format(max_iter, timer.seconds())) if __name__ == "__main__": parser = default_argument_parser() parser.add_argument("--task", choices=["train", "eval", "data", "data_advanced"], required=True) args = parser.parse_args() assert not args.eval_only logger.info("Environment info:\n" + collect_env_info()) if "data" in args.task: print("Initial " + RAM_msg()) if args.task == "data": f = benchmark_data if args.task == "data_advanced": f = benchmark_data_advanced elif args.task == "train": """ Note: training speed may not be representative. The training cost of a R-CNN model varies with the content of the data and the quality of the model. """ f = benchmark_train elif args.task == "eval": f = benchmark_eval # only benchmark single-GPU inference. assert args.num_gpus == 1 and args.num_machines == 1 launch(f, args.num_gpus, args.num_machines, args.machine_rank, args.dist_url, args=(args,)) ================================================ FILE: detectron2/tools/convert-torchvision-to-d2.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. import pickle as pkl import sys import torch """ Usage: # download one of the ResNet{18,34,50,101,152} models from torchvision: wget https://download.pytorch.org/models/resnet50-19c8e357.pth -O r50.pth # run the conversion ./convert-torchvision-to-d2.py r50.pth r50.pkl # Then, use r50.pkl with the following changes in config: MODEL: WEIGHTS: "/path/to/r50.pkl" PIXEL_MEAN: [123.675, 116.280, 103.530] PIXEL_STD: [58.395, 57.120, 57.375] RESNETS: DEPTH: 50 STRIDE_IN_1X1: False INPUT: FORMAT: "RGB" These models typically produce slightly worse results than the pre-trained ResNets we use in official configs, which are the original ResNet models released by MSRA. """ if __name__ == "__main__": input = sys.argv[1] obj = torch.load(input, map_location="cpu") newmodel = {} for k in list(obj.keys()): old_k = k if "layer" not in k: k = "stem." + k for t in [1, 2, 3, 4]: k = k.replace("layer{}".format(t), "res{}".format(t + 1)) for t in [1, 2, 3]: k = k.replace("bn{}".format(t), "conv{}.norm".format(t)) k = k.replace("downsample.0", "shortcut") k = k.replace("downsample.1", "shortcut.norm") print(old_k, "->", k) newmodel[k] = obj.pop(old_k).detach().numpy() res = {"model": newmodel, "__author__": "torchvision", "matching_heuristics": True} with open(sys.argv[2], "wb") as f: pkl.dump(res, f) if obj: print("Unconverted keys:", obj.keys()) ================================================ FILE: detectron2/tools/deploy/CMakeLists.txt ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # See https://pytorch.org/tutorials/advanced/cpp_frontend.html cmake_minimum_required(VERSION 3.12 FATAL_ERROR) project(torchscript_mask_rcnn) find_package(Torch REQUIRED) find_package(OpenCV REQUIRED) find_package(TorchVision REQUIRED) # needed by export-method=tracing/scripting add_executable(torchscript_mask_rcnn torchscript_mask_rcnn.cpp) target_link_libraries( torchscript_mask_rcnn -Wl,--no-as-needed TorchVision::TorchVision -Wl,--as-needed "${TORCH_LIBRARIES}" ${OpenCV_LIBS}) set_property(TARGET torchscript_mask_rcnn PROPERTY CXX_STANDARD 14) ================================================ FILE: detectron2/tools/deploy/README.md ================================================ See [deployment tutorial](https://detectron2.readthedocs.io/tutorials/deployment.html) for some high-level background about deployment. This directory contains the following examples: 1. An example script `export_model.py` that exports a detectron2 model for deployment using different methods and formats. 2. A C++ example that runs inference with Mask R-CNN model in TorchScript format. ## Build Deployment depends on libtorch and OpenCV. Some require more dependencies: * Running TorchScript-format models produced by `--export-method=caffe2_tracing` requires libtorch to be built with caffe2 enabled. * Running TorchScript-format models produced by `--export-method=tracing/scripting` requires libtorchvision (C++ library of torchvision). All methods are supported in one C++ file that requires all the above dependencies. Adjust it and remove code you don't need. As a reference, we provide a [Dockerfile](../../docker/deploy.Dockerfile) that installs all the above dependencies and builds the C++ example. ## Use We show a few example commands to export and execute a Mask R-CNN model in C++. * `export-method=tracing, format=torchscript`: ``` ./export_model.py --config-file ../../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml \ --output ./output --export-method tracing --format torchscript \ MODEL.WEIGHTS detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl \ MODEL.DEVICE cuda ./build/torchscript_mask_rcnn output/model.ts input.jpg tracing ``` * `export-method=scripting, format=torchscript`: ``` ./export_model.py --config-file ../../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml \ --output ./output --export-method scripting --format torchscript \ MODEL.WEIGHTS detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl \ ./build/torchscript_mask_rcnn output/model.ts input.jpg scripting ``` * `export-method=caffe2_tracing, format=torchscript`: ``` ./export_model.py --config-file ../../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml \ --output ./output --export-method caffe2_tracing --format torchscript \ MODEL.WEIGHTS detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl \ ./build/torchscript_mask_rcnn output/model.ts input.jpg caffe2_tracing ``` ## Notes: 1. Tracing/Caffe2-tracing requires valid weights & sample inputs. Therefore the above commands require pre-trained models and [COCO dataset](https://detectron2.readthedocs.io/tutorials/builtin_datasets.html). You can modify the script to obtain sample inputs in other ways instead of from COCO. 2. `--run-eval` is implemented only for tracing mode to evaluate the exported model using the dataset in the config. It's recommended to always verify the accuracy in case the conversion is not successful. Evaluation can be slow if model is exported to CPU or dataset is too large ("coco_2017_val_100" is a small subset of COCO useful for evaluation). `caffe2_tracing` accuracy may be slightly different (within 0.1 AP) from original model due to numerical precisions between different runtime. ================================================ FILE: detectron2/tools/deploy/export_model.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. import argparse import os from typing import Dict, List, Tuple import torch from torch import Tensor, nn import detectron2.data.transforms as T from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import get_cfg from detectron2.data import build_detection_test_loader, detection_utils from detectron2.evaluation import COCOEvaluator, inference_on_dataset, print_csv_format from detectron2.export import ( STABLE_ONNX_OPSET_VERSION, TracingAdapter, dump_torchscript_IR, scripting_with_instances, ) from detectron2.modeling import GeneralizedRCNN, RetinaNet, build_model from detectron2.modeling.postprocessing import detector_postprocess from detectron2.projects.point_rend import add_pointrend_config from detectron2.structures import Boxes from detectron2.utils.env import TORCH_VERSION from detectron2.utils.file_io import PathManager from detectron2.utils.logger import setup_logger def setup_cfg(args): cfg = get_cfg() # cuda context is initialized before creating dataloader, so we don't fork anymore cfg.DATALOADER.NUM_WORKERS = 0 add_pointrend_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.freeze() return cfg def export_caffe2_tracing(cfg, torch_model, inputs): from detectron2.export import Caffe2Tracer tracer = Caffe2Tracer(cfg, torch_model, inputs) if args.format == "caffe2": caffe2_model = tracer.export_caffe2() caffe2_model.save_protobuf(args.output) # draw the caffe2 graph caffe2_model.save_graph(os.path.join(args.output, "model.svg"), inputs=inputs) return caffe2_model elif args.format == "onnx": import onnx onnx_model = tracer.export_onnx() onnx.save(onnx_model, os.path.join(args.output, "model.onnx")) elif args.format == "torchscript": ts_model = tracer.export_torchscript() with PathManager.open(os.path.join(args.output, "model.ts"), "wb") as f: torch.jit.save(ts_model, f) dump_torchscript_IR(ts_model, args.output) # experimental. API not yet final def export_scripting(torch_model): assert TORCH_VERSION >= (1, 8) fields = { "proposal_boxes": Boxes, "objectness_logits": Tensor, "pred_boxes": Boxes, "scores": Tensor, "pred_classes": Tensor, "pred_masks": Tensor, "pred_keypoints": torch.Tensor, "pred_keypoint_heatmaps": torch.Tensor, } assert args.format == "torchscript", "Scripting only supports torchscript format." class ScriptableAdapterBase(nn.Module): # Use this adapter to workaround https://github.com/pytorch/pytorch/issues/46944 # by not retuning instances but dicts. Otherwise the exported model is not deployable def __init__(self): super().__init__() self.model = torch_model self.eval() if isinstance(torch_model, GeneralizedRCNN): class ScriptableAdapter(ScriptableAdapterBase): def forward(self, inputs: Tuple[Dict[str, torch.Tensor]]) -> List[Dict[str, Tensor]]: instances = self.model.inference(inputs, do_postprocess=False) return [i.get_fields() for i in instances] else: class ScriptableAdapter(ScriptableAdapterBase): def forward(self, inputs: Tuple[Dict[str, torch.Tensor]]) -> List[Dict[str, Tensor]]: instances = self.model(inputs) return [i.get_fields() for i in instances] ts_model = scripting_with_instances(ScriptableAdapter(), fields) with PathManager.open(os.path.join(args.output, "model.ts"), "wb") as f: torch.jit.save(ts_model, f) dump_torchscript_IR(ts_model, args.output) # TODO inference in Python now missing postprocessing glue code return None # experimental. API not yet final def export_tracing(torch_model, inputs): assert TORCH_VERSION >= (1, 8) image = inputs[0]["image"] inputs = [{"image": image}] # remove other unused keys if isinstance(torch_model, GeneralizedRCNN): def inference(model, inputs): # use do_postprocess=False so it returns ROI mask inst = model.inference(inputs, do_postprocess=False)[0] return [{"instances": inst}] else: inference = None # assume that we just call the model directly traceable_model = TracingAdapter(torch_model, inputs, inference) if args.format == "torchscript": ts_model = torch.jit.trace(traceable_model, (image,)) with PathManager.open(os.path.join(args.output, "model.ts"), "wb") as f: torch.jit.save(ts_model, f) dump_torchscript_IR(ts_model, args.output) elif args.format == "onnx": with PathManager.open(os.path.join(args.output, "model.onnx"), "wb") as f: torch.onnx.export(traceable_model, (image,), f, opset_version=STABLE_ONNX_OPSET_VERSION) logger.info("Inputs schema: " + str(traceable_model.inputs_schema)) logger.info("Outputs schema: " + str(traceable_model.outputs_schema)) if args.format != "torchscript": return None if not isinstance(torch_model, (GeneralizedRCNN, RetinaNet)): return None def eval_wrapper(inputs): """ The exported model does not contain the final resize step, which is typically unused in deployment but needed for evaluation. We add it manually here. """ input = inputs[0] instances = traceable_model.outputs_schema(ts_model(input["image"]))[0]["instances"] postprocessed = detector_postprocess(instances, input["height"], input["width"]) return [{"instances": postprocessed}] return eval_wrapper def get_sample_inputs(args): if args.sample_image is None: # get a first batch from dataset data_loader = build_detection_test_loader(cfg, cfg.DATASETS.TEST[0]) first_batch = next(iter(data_loader)) return first_batch else: # get a sample data original_image = detection_utils.read_image(args.sample_image, format=cfg.INPUT.FORMAT) # Do same preprocessing as DefaultPredictor aug = T.ResizeShortestEdge( [cfg.INPUT.MIN_SIZE_TEST, cfg.INPUT.MIN_SIZE_TEST], cfg.INPUT.MAX_SIZE_TEST ) height, width = original_image.shape[:2] image = aug.get_transform(original_image).apply_image(original_image) image = torch.as_tensor(image.astype("float32").transpose(2, 0, 1)) inputs = {"image": image, "height": height, "width": width} # Sample ready sample_inputs = [inputs] return sample_inputs if __name__ == "__main__": parser = argparse.ArgumentParser(description="Export a model for deployment.") parser.add_argument( "--format", choices=["caffe2", "onnx", "torchscript"], help="output format", default="torchscript", ) parser.add_argument( "--export-method", choices=["caffe2_tracing", "tracing", "scripting"], help="Method to export models", default="tracing", ) parser.add_argument("--config-file", default="", metavar="FILE", help="path to config file") parser.add_argument("--sample-image", default=None, type=str, help="sample image for input") parser.add_argument("--run-eval", action="store_true") parser.add_argument("--output", help="output directory for the converted model") parser.add_argument( "opts", help="Modify config options using the command-line", default=None, nargs=argparse.REMAINDER, ) args = parser.parse_args() logger = setup_logger() logger.info("Command line arguments: " + str(args)) PathManager.mkdirs(args.output) # Disable re-specialization on new shapes. Otherwise --run-eval will be slow torch._C._jit_set_bailout_depth(1) cfg = setup_cfg(args) # create a torch model torch_model = build_model(cfg) DetectionCheckpointer(torch_model).resume_or_load(cfg.MODEL.WEIGHTS) torch_model.eval() # get sample data sample_inputs = get_sample_inputs(args) # convert and save model if args.export_method == "caffe2_tracing": exported_model = export_caffe2_tracing(cfg, torch_model, sample_inputs) elif args.export_method == "scripting": exported_model = export_scripting(torch_model) elif args.export_method == "tracing": exported_model = export_tracing(torch_model, sample_inputs) # run evaluation with the converted model if args.run_eval: assert exported_model is not None, ( "Python inference is not yet implemented for " f"export_method={args.export_method}, format={args.format}." ) logger.info("Running evaluation ... this takes a long time if you export to CPU.") dataset = cfg.DATASETS.TEST[0] data_loader = build_detection_test_loader(cfg, dataset) # NOTE: hard-coded evaluator. change to the evaluator for your dataset evaluator = COCOEvaluator(dataset, output_dir=args.output) metrics = inference_on_dataset(exported_model, data_loader, evaluator) print_csv_format(metrics) logger.info("Success.") ================================================ FILE: detectron2/tools/deploy/torchscript_mask_rcnn.cpp ================================================ // Copyright (c) Facebook, Inc. and its affiliates. // @lint-ignore-every CLANGTIDY // This is an example code that demonstrates how to run inference // with a torchscript format Mask R-CNN model exported by ./export_model.py // using export method=tracing, caffe2_tracing & scripting. #include #include #include #include #include #include #include // only needed for export_method=tracing #include // @oss-only // @fb-only: #include using namespace std; c10::IValue get_caffe2_tracing_inputs(cv::Mat& img, c10::Device device) { const int height = img.rows; const int width = img.cols; // FPN models require divisibility of 32. // Tracing mode does padding inside the graph, but caffe2_tracing does not. assert(height % 32 == 0 && width % 32 == 0); const int channels = 3; auto input = torch::from_blob(img.data, {1, height, width, channels}, torch::kUInt8); // NHWC to NCHW input = input.to(device, torch::kFloat).permute({0, 3, 1, 2}).contiguous(); std::array im_info_data{height * 1.0f, width * 1.0f, 1.0f}; auto im_info = torch::from_blob(im_info_data.data(), {1, 3}).clone().to(device); return std::make_tuple(input, im_info); } c10::IValue get_tracing_inputs(cv::Mat& img, c10::Device device) { const int height = img.rows; const int width = img.cols; const int channels = 3; auto input = torch::from_blob(img.data, {height, width, channels}, torch::kUInt8); // HWC to CHW input = input.to(device, torch::kFloat).permute({2, 0, 1}).contiguous(); return input; } // create a Tuple[Dict[str, Tensor]] which is the input type of scripted model c10::IValue get_scripting_inputs(cv::Mat& img, c10::Device device) { const int height = img.rows; const int width = img.cols; const int channels = 3; auto img_tensor = torch::from_blob(img.data, {height, width, channels}, torch::kUInt8); // HWC to CHW img_tensor = img_tensor.to(device, torch::kFloat).permute({2, 0, 1}).contiguous(); auto dic = c10::Dict(); dic.insert("image", img_tensor); return std::make_tuple(dic); } c10::IValue get_inputs(std::string export_method, cv::Mat& img, c10::Device device) { // Given an image, create inputs in the format required by the model. if (export_method == "tracing") return get_tracing_inputs(img, device); if (export_method == "caffe2_tracing") return get_caffe2_tracing_inputs(img, device); if (export_method == "scripting") return get_scripting_inputs(img, device); abort(); } struct MaskRCNNOutputs { at::Tensor pred_boxes, pred_classes, pred_masks, scores; int num_instances() const { return pred_boxes.sizes()[0]; } }; MaskRCNNOutputs get_outputs(std::string export_method, c10::IValue outputs) { // Given outputs of the model, extract tensors from it to turn into a // common MaskRCNNOutputs format. if (export_method == "tracing") { auto out_tuple = outputs.toTuple()->elements(); // They are ordered alphabetically by their field name in Instances return MaskRCNNOutputs{ out_tuple[0].toTensor(), out_tuple[1].toTensor(), out_tuple[2].toTensor(), out_tuple[3].toTensor()}; } if (export_method == "caffe2_tracing") { auto out_tuple = outputs.toTuple()->elements(); // A legacy order used by caffe2 models return MaskRCNNOutputs{ out_tuple[0].toTensor(), out_tuple[2].toTensor(), out_tuple[3].toTensor(), out_tuple[1].toTensor()}; } if (export_method == "scripting") { // With the ScriptableAdapter defined in export_model.py, the output is // List[Dict[str, Any]]. auto out_dict = outputs.toList().get(0).toGenericDict(); return MaskRCNNOutputs{ out_dict.at("pred_boxes").toTensor(), out_dict.at("pred_classes").toTensor(), out_dict.at("pred_masks").toTensor(), out_dict.at("scores").toTensor()}; } abort(); } int main(int argc, const char* argv[]) { if (argc != 4) { cerr << R"xx( Usage: ./torchscript_mask_rcnn model.ts input.jpg EXPORT_METHOD EXPORT_METHOD can be "tracing", "caffe2_tracing" or "scripting". )xx"; return 1; } std::string image_file = argv[2]; std::string export_method = argv[3]; assert( export_method == "caffe2_tracing" || export_method == "tracing" || export_method == "scripting"); torch::jit::FusionStrategy strat = {{torch::jit::FusionBehavior::DYNAMIC, 1}}; torch::jit::setFusionStrategy(strat); torch::autograd::AutoGradMode guard(false); auto module = torch::jit::load(argv[1]); assert(module.buffers().size() > 0); // Assume that the entire model is on the same device. // We just put input to this device. auto device = (*begin(module.buffers())).device(); cv::Mat input_img = cv::imread(image_file, cv::IMREAD_COLOR); auto inputs = get_inputs(export_method, input_img, device); // Run the network auto output = module.forward({inputs}); if (device.is_cuda()) c10::cuda::getCurrentCUDAStream().synchronize(); // run 3 more times to benchmark int N_benchmark = 3, N_warmup = 1; auto start_time = chrono::high_resolution_clock::now(); for (int i = 0; i < N_benchmark + N_warmup; ++i) { if (i == N_warmup) start_time = chrono::high_resolution_clock::now(); output = module.forward({inputs}); if (device.is_cuda()) c10::cuda::getCurrentCUDAStream().synchronize(); } auto end_time = chrono::high_resolution_clock::now(); auto ms = chrono::duration_cast(end_time - start_time) .count(); cout << "Latency (should vary with different inputs): " << ms * 1.0 / 1e6 / N_benchmark << " seconds" << endl; // Parse Mask R-CNN outputs auto rcnn_outputs = get_outputs(export_method, output); cout << "Number of detected objects: " << rcnn_outputs.num_instances() << endl; cout << "pred_boxes: " << rcnn_outputs.pred_boxes.toString() << " " << rcnn_outputs.pred_boxes.sizes() << endl; cout << "scores: " << rcnn_outputs.scores.toString() << " " << rcnn_outputs.scores.sizes() << endl; cout << "pred_classes: " << rcnn_outputs.pred_classes.toString() << " " << rcnn_outputs.pred_classes.sizes() << endl; cout << "pred_masks: " << rcnn_outputs.pred_masks.toString() << " " << rcnn_outputs.pred_masks.sizes() << endl; cout << rcnn_outputs.pred_boxes << endl; return 0; } ================================================ FILE: detectron2/tools/lazyconfig_train_net.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. """ Training script using the new "LazyConfig" python config files. This scripts reads a given python config file and runs the training or evaluation. It can be used to train any models or dataset as long as they can be instantiated by the recursive construction defined in the given config file. Besides lazy construction of models, dataloader, etc., this scripts expects a few common configuration parameters currently defined in "configs/common/train.py". To add more complicated training logic, you can easily add other configs in the config file and implement a new train_net.py to handle them. """ import logging from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import LazyConfig, instantiate from detectron2.engine import ( AMPTrainer, SimpleTrainer, default_argument_parser, default_setup, default_writers, hooks, launch, ) from detectron2.engine.defaults import create_ddp_model from detectron2.evaluation import inference_on_dataset, print_csv_format from detectron2.utils import comm logger = logging.getLogger("detectron2") def do_test(cfg, model): if "evaluator" in cfg.dataloader: ret = inference_on_dataset( model, instantiate(cfg.dataloader.test), instantiate(cfg.dataloader.evaluator) ) print_csv_format(ret) return ret def do_train(args, cfg): """ Args: cfg: an object with the following attributes: model: instantiate to a module dataloader.{train,test}: instantiate to dataloaders dataloader.evaluator: instantiate to evaluator for test set optimizer: instantaite to an optimizer lr_multiplier: instantiate to a fvcore scheduler train: other misc config defined in `configs/common/train.py`, including: output_dir (str) init_checkpoint (str) amp.enabled (bool) max_iter (int) eval_period, log_period (int) device (str) checkpointer (dict) ddp (dict) """ model = instantiate(cfg.model) logger = logging.getLogger("detectron2") logger.info("Model:\n{}".format(model)) model.to(cfg.train.device) cfg.optimizer.params.model = model optim = instantiate(cfg.optimizer) train_loader = instantiate(cfg.dataloader.train) model = create_ddp_model(model, **cfg.train.ddp) trainer = (AMPTrainer if cfg.train.amp.enabled else SimpleTrainer)(model, train_loader, optim) checkpointer = DetectionCheckpointer( model, cfg.train.output_dir, trainer=trainer, ) trainer.register_hooks( [ hooks.IterationTimer(), hooks.LRScheduler(scheduler=instantiate(cfg.lr_multiplier)), hooks.PeriodicCheckpointer(checkpointer, **cfg.train.checkpointer) if comm.is_main_process() else None, hooks.EvalHook(cfg.train.eval_period, lambda: do_test(cfg, model)), hooks.PeriodicWriter( default_writers(cfg.train.output_dir, cfg.train.max_iter), period=cfg.train.log_period, ) if comm.is_main_process() else None, ] ) checkpointer.resume_or_load(cfg.train.init_checkpoint, resume=args.resume) if args.resume and checkpointer.has_checkpoint(): # The checkpoint stores the training iteration that just finished, thus we start # at the next iteration start_iter = trainer.iter + 1 else: start_iter = 0 trainer.train(start_iter, cfg.train.max_iter) def main(args): cfg = LazyConfig.load(args.config_file) cfg = LazyConfig.apply_overrides(cfg, args.opts) default_setup(cfg, args) if args.eval_only: model = instantiate(cfg.model) model.to(cfg.train.device) model = create_ddp_model(model) DetectionCheckpointer(model).load(cfg.train.init_checkpoint) print(do_test(cfg, model)) else: do_train(args, cfg) if __name__ == "__main__": args = default_argument_parser().parse_args() launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), ) ================================================ FILE: detectron2/tools/lightning_train_net.py ================================================ #!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. # Lightning Trainer should be considered beta at this point # We have confirmed that training and validation run correctly and produce correct results # Depending on how you launch the trainer, there are issues with processes terminating correctly # This module is still dependent on D2 logging, but could be transferred to use Lightning logging import logging import os import time import weakref from collections import OrderedDict from typing import Any, Dict, List import detectron2.utils.comm as comm from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import get_cfg from detectron2.data import build_detection_test_loader, build_detection_train_loader from detectron2.engine import ( DefaultTrainer, SimpleTrainer, default_argument_parser, default_setup, default_writers, hooks, ) from detectron2.evaluation import print_csv_format from detectron2.evaluation.testing import flatten_results_dict from detectron2.modeling import build_model from detectron2.solver import build_lr_scheduler, build_optimizer from detectron2.utils.events import EventStorage from detectron2.utils.logger import setup_logger import pytorch_lightning as pl # type: ignore from pytorch_lightning import LightningDataModule, LightningModule from train_net import build_evaluator logging.basicConfig(level=logging.INFO) logger = logging.getLogger("detectron2") class TrainingModule(LightningModule): def __init__(self, cfg): super().__init__() if not logger.isEnabledFor(logging.INFO): # setup_logger is not called for d2 setup_logger() self.cfg = DefaultTrainer.auto_scale_workers(cfg, comm.get_world_size()) self.storage: EventStorage = None self.model = build_model(self.cfg) self.start_iter = 0 self.max_iter = cfg.SOLVER.MAX_ITER def on_save_checkpoint(self, checkpoint: Dict[str, Any]) -> None: checkpoint["iteration"] = self.storage.iter def on_load_checkpoint(self, checkpointed_state: Dict[str, Any]) -> None: self.start_iter = checkpointed_state["iteration"] self.storage.iter = self.start_iter def setup(self, stage: str): if self.cfg.MODEL.WEIGHTS: self.checkpointer = DetectionCheckpointer( # Assume you want to save checkpoints together with logs/statistics self.model, self.cfg.OUTPUT_DIR, ) logger.info(f"Load model weights from checkpoint: {self.cfg.MODEL.WEIGHTS}.") # Only load weights, use lightning checkpointing if you want to resume self.checkpointer.load(self.cfg.MODEL.WEIGHTS) self.iteration_timer = hooks.IterationTimer() self.iteration_timer.before_train() self.data_start = time.perf_counter() self.writers = None def training_step(self, batch, batch_idx): data_time = time.perf_counter() - self.data_start # Need to manually enter/exit since trainer may launch processes # This ideally belongs in setup, but setup seems to run before processes are spawned if self.storage is None: self.storage = EventStorage(0) self.storage.__enter__() self.iteration_timer.trainer = weakref.proxy(self) self.iteration_timer.before_step() self.writers = ( default_writers(self.cfg.OUTPUT_DIR, self.max_iter) if comm.is_main_process() else {} ) loss_dict = self.model(batch) SimpleTrainer.write_metrics(loss_dict, data_time) opt = self.optimizers() self.storage.put_scalar( "lr", opt.param_groups[self._best_param_group_id]["lr"], smoothing_hint=False ) self.iteration_timer.after_step() self.storage.step() # A little odd to put before step here, but it's the best way to get a proper timing self.iteration_timer.before_step() if self.storage.iter % 20 == 0: for writer in self.writers: writer.write() return sum(loss_dict.values()) def training_step_end(self, training_step_outpus): self.data_start = time.perf_counter() return training_step_outpus def training_epoch_end(self, training_step_outputs): self.iteration_timer.after_train() if comm.is_main_process(): self.checkpointer.save("model_final") for writer in self.writers: writer.write() writer.close() self.storage.__exit__(None, None, None) def _process_dataset_evaluation_results(self) -> OrderedDict: results = OrderedDict() for idx, dataset_name in enumerate(self.cfg.DATASETS.TEST): results[dataset_name] = self._evaluators[idx].evaluate() if comm.is_main_process(): print_csv_format(results[dataset_name]) if len(results) == 1: results = list(results.values())[0] return results def _reset_dataset_evaluators(self): self._evaluators = [] for dataset_name in self.cfg.DATASETS.TEST: evaluator = build_evaluator(self.cfg, dataset_name) evaluator.reset() self._evaluators.append(evaluator) def on_validation_epoch_start(self, _outputs): self._reset_dataset_evaluators() def validation_epoch_end(self, _outputs): results = self._process_dataset_evaluation_results(_outputs) flattened_results = flatten_results_dict(results) for k, v in flattened_results.items(): try: v = float(v) except Exception as e: raise ValueError( "[EvalHook] eval_function should return a nested dict of float. " "Got '{}: {}' instead.".format(k, v) ) from e self.storage.put_scalars(**flattened_results, smoothing_hint=False) def validation_step(self, batch, batch_idx: int, dataloader_idx: int = 0) -> None: if not isinstance(batch, List): batch = [batch] outputs = self.model(batch) self._evaluators[dataloader_idx].process(batch, outputs) def configure_optimizers(self): optimizer = build_optimizer(self.cfg, self.model) self._best_param_group_id = hooks.LRScheduler.get_best_param_group_id(optimizer) scheduler = build_lr_scheduler(self.cfg, optimizer) return [optimizer], [{"scheduler": scheduler, "interval": "step"}] class DataModule(LightningDataModule): def __init__(self, cfg): super().__init__() self.cfg = DefaultTrainer.auto_scale_workers(cfg, comm.get_world_size()) def train_dataloader(self): return build_detection_train_loader(self.cfg) def val_dataloader(self): dataloaders = [] for dataset_name in self.cfg.DATASETS.TEST: dataloaders.append(build_detection_test_loader(self.cfg, dataset_name)) return dataloaders def main(args): cfg = setup(args) train(cfg, args) def train(cfg, args): trainer_params = { # training loop is bounded by max steps, use a large max_epochs to make # sure max_steps is met first "max_epochs": 10**8, "max_steps": cfg.SOLVER.MAX_ITER, "val_check_interval": cfg.TEST.EVAL_PERIOD if cfg.TEST.EVAL_PERIOD > 0 else 10**8, "num_nodes": args.num_machines, "gpus": args.num_gpus, "num_sanity_val_steps": 0, } if cfg.SOLVER.AMP.ENABLED: trainer_params["precision"] = 16 last_checkpoint = os.path.join(cfg.OUTPUT_DIR, "last.ckpt") if args.resume: # resume training from checkpoint trainer_params["resume_from_checkpoint"] = last_checkpoint logger.info(f"Resuming training from checkpoint: {last_checkpoint}.") trainer = pl.Trainer(**trainer_params) logger.info(f"start to train with {args.num_machines} nodes and {args.num_gpus} GPUs") module = TrainingModule(cfg) data_module = DataModule(cfg) if args.eval_only: logger.info("Running inference") trainer.validate(module, data_module) else: logger.info("Running training") trainer.fit(module, data_module) def setup(args): """ Create configs and perform basic setups. """ cfg = get_cfg() cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.freeze() default_setup(cfg, args) return cfg if __name__ == "__main__": parser = default_argument_parser() args = parser.parse_args() logger.info("Command Line Args:", args) main(args) ================================================ FILE: detectron2/tools/plain_train_net.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. """ Detectron2 training script with a plain training loop. This script reads a given config file and runs the training or evaluation. It is an entry point that is able to train standard models in detectron2. In order to let one script support training of many models, this script contains logic that are specific to these built-in models and therefore may not be suitable for your own project. For example, your research project perhaps only needs a single "evaluator". Therefore, we recommend you to use detectron2 as a library and take this file as an example of how to use the library. You may want to write your own script with your datasets and other customizations. Compared to "train_net.py", this script supports fewer default features. It also includes fewer abstraction, therefore is easier to add custom logic. """ import logging import os from collections import OrderedDict import torch from torch.nn.parallel import DistributedDataParallel import detectron2.utils.comm as comm from detectron2.checkpoint import DetectionCheckpointer, PeriodicCheckpointer from detectron2.config import get_cfg from detectron2.data import ( MetadataCatalog, build_detection_test_loader, build_detection_train_loader, ) from detectron2.engine import default_argument_parser, default_setup, default_writers, launch from detectron2.evaluation import ( CityscapesInstanceEvaluator, CityscapesSemSegEvaluator, COCOEvaluator, COCOPanopticEvaluator, DatasetEvaluators, LVISEvaluator, PascalVOCDetectionEvaluator, SemSegEvaluator, inference_on_dataset, print_csv_format, ) from detectron2.modeling import build_model from detectron2.solver import build_lr_scheduler, build_optimizer from detectron2.utils.events import EventStorage logger = logging.getLogger("detectron2") def get_evaluator(cfg, dataset_name, output_folder=None): """ Create evaluator(s) for a given dataset. This uses the special metadata "evaluator_type" associated with each builtin dataset. For your own dataset, you can simply create an evaluator manually in your script and do not have to worry about the hacky if-else logic here. """ if output_folder is None: output_folder = os.path.join(cfg.OUTPUT_DIR, "inference") evaluator_list = [] evaluator_type = MetadataCatalog.get(dataset_name).evaluator_type if evaluator_type in ["sem_seg", "coco_panoptic_seg"]: evaluator_list.append( SemSegEvaluator( dataset_name, distributed=True, output_dir=output_folder, ) ) if evaluator_type in ["coco", "coco_panoptic_seg"]: evaluator_list.append(COCOEvaluator(dataset_name, output_dir=output_folder)) if evaluator_type == "coco_panoptic_seg": evaluator_list.append(COCOPanopticEvaluator(dataset_name, output_folder)) if evaluator_type == "cityscapes_instance": return CityscapesInstanceEvaluator(dataset_name) if evaluator_type == "cityscapes_sem_seg": return CityscapesSemSegEvaluator(dataset_name) if evaluator_type == "pascal_voc": return PascalVOCDetectionEvaluator(dataset_name) if evaluator_type == "lvis": return LVISEvaluator(dataset_name, cfg, True, output_folder) if len(evaluator_list) == 0: raise NotImplementedError( "no Evaluator for the dataset {} with the type {}".format(dataset_name, evaluator_type) ) if len(evaluator_list) == 1: return evaluator_list[0] return DatasetEvaluators(evaluator_list) def do_test(cfg, model): results = OrderedDict() for dataset_name in cfg.DATASETS.TEST: data_loader = build_detection_test_loader(cfg, dataset_name) evaluator = get_evaluator( cfg, dataset_name, os.path.join(cfg.OUTPUT_DIR, "inference", dataset_name) ) results_i = inference_on_dataset(model, data_loader, evaluator) results[dataset_name] = results_i if comm.is_main_process(): logger.info("Evaluation results for {} in csv format:".format(dataset_name)) print_csv_format(results_i) if len(results) == 1: results = list(results.values())[0] return results def do_train(cfg, model, resume=False): model.train() optimizer = build_optimizer(cfg, model) scheduler = build_lr_scheduler(cfg, optimizer) checkpointer = DetectionCheckpointer( model, cfg.OUTPUT_DIR, optimizer=optimizer, scheduler=scheduler ) start_iter = ( checkpointer.resume_or_load(cfg.MODEL.WEIGHTS, resume=resume).get("iteration", -1) + 1 ) max_iter = cfg.SOLVER.MAX_ITER periodic_checkpointer = PeriodicCheckpointer( checkpointer, cfg.SOLVER.CHECKPOINT_PERIOD, max_iter=max_iter ) writers = default_writers(cfg.OUTPUT_DIR, max_iter) if comm.is_main_process() else [] # compared to "train_net.py", we do not support accurate timing and # precise BN here, because they are not trivial to implement in a small training loop data_loader = build_detection_train_loader(cfg) logger.info("Starting training from iteration {}".format(start_iter)) with EventStorage(start_iter) as storage: for data, iteration in zip(data_loader, range(start_iter, max_iter)): storage.iter = iteration loss_dict = model(data) losses = sum(loss_dict.values()) assert torch.isfinite(losses).all(), loss_dict loss_dict_reduced = {k: v.item() for k, v in comm.reduce_dict(loss_dict).items()} losses_reduced = sum(loss for loss in loss_dict_reduced.values()) if comm.is_main_process(): storage.put_scalars(total_loss=losses_reduced, **loss_dict_reduced) optimizer.zero_grad() losses.backward() optimizer.step() storage.put_scalar("lr", optimizer.param_groups[0]["lr"], smoothing_hint=False) scheduler.step() if ( cfg.TEST.EVAL_PERIOD > 0 and (iteration + 1) % cfg.TEST.EVAL_PERIOD == 0 and iteration != max_iter - 1 ): do_test(cfg, model) # Compared to "train_net.py", the test results are not dumped to EventStorage comm.synchronize() if iteration - start_iter > 5 and ( (iteration + 1) % 20 == 0 or iteration == max_iter - 1 ): for writer in writers: writer.write() periodic_checkpointer.step(iteration) def setup(args): """ Create configs and perform basic setups. """ cfg = get_cfg() cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.freeze() default_setup( cfg, args ) # if you don't like any of the default setup, write your own setup code return cfg def main(args): cfg = setup(args) model = build_model(cfg) logger.info("Model:\n{}".format(model)) if args.eval_only: DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load( cfg.MODEL.WEIGHTS, resume=args.resume ) return do_test(cfg, model) distributed = comm.get_world_size() > 1 if distributed: model = DistributedDataParallel( model, device_ids=[comm.get_local_rank()], broadcast_buffers=False ) do_train(cfg, model, resume=args.resume) return do_test(cfg, model) if __name__ == "__main__": args = default_argument_parser().parse_args() print("Command Line Args:", args) launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), ) ================================================ FILE: detectron2/tools/train_net.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. """ A main training script. This scripts reads a given config file and runs the training or evaluation. It is an entry point that is made to train standard models in detectron2. In order to let one script support training of many models, this script contains logic that are specific to these built-in models and therefore may not be suitable for your own project. For example, your research project perhaps only needs a single "evaluator". Therefore, we recommend you to use detectron2 as an library and take this file as an example of how to use the library. You may want to write your own script with your datasets and other customizations. """ import logging import os from collections import OrderedDict import detectron2.utils.comm as comm from detectron2.checkpoint import DetectionCheckpointer from detectron2.config import get_cfg from detectron2.data import MetadataCatalog from detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, hooks, launch from detectron2.evaluation import ( CityscapesInstanceEvaluator, CityscapesSemSegEvaluator, COCOEvaluator, COCOPanopticEvaluator, DatasetEvaluators, LVISEvaluator, PascalVOCDetectionEvaluator, SemSegEvaluator, verify_results, ) from detectron2.modeling import GeneralizedRCNNWithTTA def build_evaluator(cfg, dataset_name, output_folder=None): """ Create evaluator(s) for a given dataset. This uses the special metadata "evaluator_type" associated with each builtin dataset. For your own dataset, you can simply create an evaluator manually in your script and do not have to worry about the hacky if-else logic here. """ if output_folder is None: output_folder = os.path.join(cfg.OUTPUT_DIR, "inference") evaluator_list = [] evaluator_type = MetadataCatalog.get(dataset_name).evaluator_type if evaluator_type in ["sem_seg", "coco_panoptic_seg"]: evaluator_list.append( SemSegEvaluator( dataset_name, distributed=True, output_dir=output_folder, ) ) if evaluator_type in ["coco", "coco_panoptic_seg"]: evaluator_list.append(COCOEvaluator(dataset_name, output_dir=output_folder)) if evaluator_type == "coco_panoptic_seg": evaluator_list.append(COCOPanopticEvaluator(dataset_name, output_folder)) if evaluator_type == "cityscapes_instance": return CityscapesInstanceEvaluator(dataset_name) if evaluator_type == "cityscapes_sem_seg": return CityscapesSemSegEvaluator(dataset_name) elif evaluator_type == "pascal_voc": return PascalVOCDetectionEvaluator(dataset_name) elif evaluator_type == "lvis": return LVISEvaluator(dataset_name, output_dir=output_folder) if len(evaluator_list) == 0: raise NotImplementedError( "no Evaluator for the dataset {} with the type {}".format(dataset_name, evaluator_type) ) elif len(evaluator_list) == 1: return evaluator_list[0] return DatasetEvaluators(evaluator_list) class Trainer(DefaultTrainer): """ We use the "DefaultTrainer" which contains pre-defined default logic for standard training workflow. They may not work for you, especially if you are working on a new research project. In that case you can write your own training loop. You can use "tools/plain_train_net.py" as an example. """ @classmethod def build_evaluator(cls, cfg, dataset_name, output_folder=None): return build_evaluator(cfg, dataset_name, output_folder) @classmethod def test_with_TTA(cls, cfg, model): logger = logging.getLogger("detectron2.trainer") # In the end of training, run an evaluation with TTA # Only support some R-CNN models. logger.info("Running inference with test-time augmentation ...") model = GeneralizedRCNNWithTTA(cfg, model) evaluators = [ cls.build_evaluator( cfg, name, output_folder=os.path.join(cfg.OUTPUT_DIR, "inference_TTA") ) for name in cfg.DATASETS.TEST ] res = cls.test(cfg, model, evaluators) res = OrderedDict({k + "_TTA": v for k, v in res.items()}) return res def setup(args): """ Create configs and perform basic setups. """ cfg = get_cfg() cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.freeze() default_setup(cfg, args) return cfg def main(args): cfg = setup(args) if args.eval_only: model = Trainer.build_model(cfg) DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load( cfg.MODEL.WEIGHTS, resume=args.resume ) res = Trainer.test(cfg, model) if cfg.TEST.AUG.ENABLED: res.update(Trainer.test_with_TTA(cfg, model)) if comm.is_main_process(): verify_results(cfg, res) return res """ If you'd like to do anything fancier than the standard training logic, consider writing your own training loop (see plain_train_net.py) or subclassing the trainer. """ trainer = Trainer(cfg) trainer.resume_or_load(resume=args.resume) if cfg.TEST.AUG.ENABLED: trainer.register_hooks( [hooks.EvalHook(0, lambda: trainer.test_with_TTA(cfg, trainer.model))] ) return trainer.train() if __name__ == "__main__": args = default_argument_parser().parse_args() print("Command Line Args:", args) launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), ) ================================================ FILE: detectron2/tools/visualize_data.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. import argparse import os from itertools import chain import cv2 import tqdm from detectron2.config import get_cfg from detectron2.data import DatasetCatalog, MetadataCatalog, build_detection_train_loader from detectron2.data import detection_utils as utils from detectron2.data.build import filter_images_with_few_keypoints from detectron2.utils.logger import setup_logger from detectron2.utils.visualizer import Visualizer def setup(args): cfg = get_cfg() if args.config_file: cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.DATALOADER.NUM_WORKERS = 0 cfg.freeze() return cfg def parse_args(in_args=None): parser = argparse.ArgumentParser(description="Visualize ground-truth data") parser.add_argument( "--source", choices=["annotation", "dataloader"], required=True, help="visualize the annotations or the data loader (with pre-processing)", ) parser.add_argument("--config-file", metavar="FILE", help="path to config file") parser.add_argument("--output-dir", default="./", help="path to output directory") parser.add_argument("--show", action="store_true", help="show output in a window") parser.add_argument( "opts", help="Modify config options using the command-line", default=None, nargs=argparse.REMAINDER, ) return parser.parse_args(in_args) if __name__ == "__main__": args = parse_args() logger = setup_logger() logger.info("Arguments: " + str(args)) cfg = setup(args) dirname = args.output_dir os.makedirs(dirname, exist_ok=True) metadata = MetadataCatalog.get(cfg.DATASETS.TRAIN[0]) def output(vis, fname): if args.show: print(fname) cv2.imshow("window", vis.get_image()[:, :, ::-1]) cv2.waitKey() else: filepath = os.path.join(dirname, fname) print("Saving to {} ...".format(filepath)) vis.save(filepath) scale = 1.0 if args.source == "dataloader": train_data_loader = build_detection_train_loader(cfg) for batch in train_data_loader: for per_image in batch: # Pytorch tensor is in (C, H, W) format img = per_image["image"].permute(1, 2, 0).cpu().detach().numpy() img = utils.convert_image_to_rgb(img, cfg.INPUT.FORMAT) visualizer = Visualizer(img, metadata=metadata, scale=scale) target_fields = per_image["instances"].get_fields() labels = [metadata.thing_classes[i] for i in target_fields["gt_classes"]] vis = visualizer.overlay_instances( labels=labels, boxes=target_fields.get("gt_boxes", None), masks=target_fields.get("gt_masks", None), keypoints=target_fields.get("gt_keypoints", None), ) output(vis, str(per_image["image_id"]) + ".jpg") else: dicts = list(chain.from_iterable([DatasetCatalog.get(k) for k in cfg.DATASETS.TRAIN])) if cfg.MODEL.KEYPOINT_ON: dicts = filter_images_with_few_keypoints(dicts, 1) for dic in tqdm.tqdm(dicts): img = utils.read_image(dic["file_name"], "RGB") visualizer = Visualizer(img, metadata=metadata, scale=scale) vis = visualizer.draw_dataset_dict(dic) output(vis, os.path.basename(dic["file_name"])) ================================================ FILE: detectron2/tools/visualize_json_results.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. import argparse import json import numpy as np import os from collections import defaultdict import cv2 import tqdm from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.structures import Boxes, BoxMode, Instances from detectron2.utils.file_io import PathManager from detectron2.utils.logger import setup_logger from detectron2.utils.visualizer import Visualizer def create_instances(predictions, image_size): ret = Instances(image_size) score = np.asarray([x["score"] for x in predictions]) chosen = (score > args.conf_threshold).nonzero()[0] score = score[chosen] bbox = np.asarray([predictions[i]["bbox"] for i in chosen]).reshape(-1, 4) bbox = BoxMode.convert(bbox, BoxMode.XYWH_ABS, BoxMode.XYXY_ABS) labels = np.asarray([dataset_id_map(predictions[i]["category_id"]) for i in chosen]) ret.scores = score ret.pred_boxes = Boxes(bbox) ret.pred_classes = labels try: ret.pred_masks = [predictions[i]["segmentation"] for i in chosen] except KeyError: pass return ret if __name__ == "__main__": parser = argparse.ArgumentParser( description="A script that visualizes the json predictions from COCO or LVIS dataset." ) parser.add_argument("--input", required=True, help="JSON file produced by the model") parser.add_argument("--output", required=True, help="output directory") parser.add_argument("--dataset", help="name of the dataset", default="coco_2017_val") parser.add_argument("--conf-threshold", default=0.5, type=float, help="confidence threshold") args = parser.parse_args() logger = setup_logger() with PathManager.open(args.input, "r") as f: predictions = json.load(f) pred_by_image = defaultdict(list) for p in predictions: pred_by_image[p["image_id"]].append(p) dicts = list(DatasetCatalog.get(args.dataset)) metadata = MetadataCatalog.get(args.dataset) if hasattr(metadata, "thing_dataset_id_to_contiguous_id"): def dataset_id_map(ds_id): return metadata.thing_dataset_id_to_contiguous_id[ds_id] elif "lvis" in args.dataset: # LVIS results are in the same format as COCO results, but have a different # mapping from dataset category id to contiguous category id in [0, #categories - 1] def dataset_id_map(ds_id): return ds_id - 1 else: raise ValueError("Unsupported dataset: {}".format(args.dataset)) os.makedirs(args.output, exist_ok=True) for dic in tqdm.tqdm(dicts): img = cv2.imread(dic["file_name"], cv2.IMREAD_COLOR)[:, :, ::-1] basename = os.path.basename(dic["file_name"]) predictions = create_instances(pred_by_image[dic["image_id"]], img.shape[:2]) vis = Visualizer(img, metadata) vis_pred = vis.draw_instance_predictions(predictions).get_image() vis = Visualizer(img, metadata) vis_gt = vis.draw_dataset_dict(dic).get_image() concat = np.concatenate((vis_pred, vis_gt), axis=1) cv2.imwrite(os.path.join(args.output, basename), concat[:, :, ::-1]) ================================================ FILE: prepare_datasets.md ================================================ # Prepare datasets The training of our work is on two benchmark datasets: [COCO](https://cocodataset.org/) and [COCOCaption](https://cocodataset.org/), [LVIS](https://www.lvisdataset.org/) and [Conceptual Caption (CC3M)](https://ai.google.com/research/ConceptualCaptions/). Please orignize the datasets as following. ``` $VLDet_ROOT/datasets/ lvis/ coco/ cc3m/ ``` Please follow the following instruction to pre-process individual datasets. ### COCO and COCO Caption First, download COCO data place them in the following way: ``` coco/ train2017/ val2017/ annotations/ captions_train2017.json instances_train2017.json instances_val2017.json ``` We first follow [OVR-CNN](https://github.com/alirezazareian/ovr-cnn/blob/master/ipynb/003.ipynb) to create the open-vocabulary COCO split. The converted files should be like: ``` coco/ zero-shot/ instances_train2017_seen_2.json instances_val2017_all_2.json ``` We further preprocess the annotation format for easier evaluation: ``` python tools/get_coco_zeroshot.py --data_path datasets/coco/zero-shot/instances_train2017_seen_2.json python tools/get_coco_zeroshot.py --data_path datasets/coco/zero-shot/instances_val2017_all_2.json ``` And process the category infomation: ``` python tools/get_lvis_cat_info.py --ann datasets/coco/zero-shot/instances_train2017_seen_2_del.json ``` Next, we prepare the class and concept embedding following RegionCLIP. Download the embedding file from [here](https://drive.google.com/drive/folders/1_HKaLSyA9fIjTS0BXxT15NRac6uRoyIj) or generate it with tools from [RegionCLIP](https://github.com/microsoft/RegionCLIP/tree/9fd374015db384bc0548b4af85446b90e13d2ae1#extract-concept-features). The files should be like: ``` coco/ VLDet/ coco_65_cls_emb.pth coco_65_concepts.txt coco_nouns_4764_emb.pth coco_nouns_4764.txt ``` Then preprocess the COCO caption data with the predefined concepts: ``` python tools/get_tags_for_VLDet_concepts.py --cc_ann datasets/coco/annotations/captions_train2017.json --allcaps --cat_path datasets/coco/VLDet/coco_nouns_4764.txt --convert_caption --out_path datasets/coco/VLDet/nouns_captions_train2017_4764tags_allcaps.json ``` This creates `datasets/coco/VLDet/nouns_captions_train2017_4764tags_allcaps.json`. ### LVIS First, download LVIS data place them in the following way: ``` lvis/ lvis_v1_train.json lvis_v1_val.json ``` Next, prepare the open-vocabulary LVIS training set using ``` python tools/remove_lvis_rare.py --ann datasets/lvis/lvis_v1_train.json ``` This will generate `datasets/lvis/lvis_v1_train_norare.json`. `lvis_v1_train_cat_info.json` is used by the Federated loss. This is created by ~~~ python tools/get_lvis_cat_info.py --ann datasets/lvis/lvis_v1_train.json ~~~ ### Conceptual Caption Download the dataset from [this](https://ai.google.com/research/ConceptualCaptions/download) page and place them as: ``` cc3m/ GCC-training.tsv ``` Run the following command to download the images and convert the annotations to LVIS format (Note: download images takes long). ~~~ python tools/download_cc.py --ann datasets/cc3m/GCC-training.tsv --save_image_path datasets/cc3m/training/ --out_path datasets/cc3m/train_image_info_tags.json ~~~ This creates `datasets/cc3m/train_image_info_tags.json`. Next, we prepare the class and concept embedding following RegionCLIP. Download the embedding file from [here](https://drive.google.com/drive/folders/1_HKaLSyA9fIjTS0BXxT15NRac6uRoyIj) or generate it with tools from [RegionCLIP](https://github.com/microsoft/RegionCLIP/tree/9fd374015db384bc0548b4af85446b90e13d2ae1#extract-concept-features). The files should be like: ``` cc3m/ VLDet/ googlecc_nouns_6250_emb.pth googlecc_nouns_6250.txt lvis_1203_cls_emb.pth ``` Run the following command to convert the annotations to LVIS format: ``` python tools/get_tags_for_VLDet_concepts.py ``` This creates `datasets/cc3m/VLDet/nouns_train_image_info_6250tags.json` ================================================ FILE: requirements.txt ================================================ opencv-python timm dataclasses ftfy regex fasttext scikit-learn lvis nltk git+https://github.com/openai/CLIP.git ================================================ FILE: tools/convert-thirdparty-pretrained-model-to-d2.py ================================================ #!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import argparse import pickle import torch """ Usage: cd DETIC_ROOT/models/ wget https://miil-public-eu.oss-eu-central-1.aliyuncs.com/model-zoo/ImageNet_21K_P/models/resnet50_miil_21k.pth python ../tools/convert-thirdparty-pretrained-model-to-d2.py --path resnet50_miil_21k.pth wget https://github.com/SwinTransformer/storage/releases/download/v1.0.0/swin_base_patch4_window7_224_22k.pth python ../tools/convert-thirdparty-pretrained-model-to-d2.py --path swin_base_patch4_window7_224_22k.pth """ if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('--path', default='') args = parser.parse_args() print('Loading', args.path) model = torch.load(args.path, map_location="cpu") # import pdb; pdb.set_trace() if 'model' in model: model = model['model'] if 'state_dict' in model: model = model['state_dict'] ret = { "model": model, "__author__": "third_party", "matching_heuristics": True } out_path = args.path.replace('.pth', '.pkl') print('Saving to', out_path) pickle.dump(ret, open(out_path, "wb")) ================================================ FILE: tools/download_cc.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import os import json import argparse from PIL import Image import numpy as np if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--ann', default='datasets/cc3m/Train_GCC-training.tsv') parser.add_argument('--save_image_path', default='datasets/cc3m/training/') parser.add_argument('--cat_info', default='datasets/lvis/lvis_v1_val.json') parser.add_argument('--out_path', default='datasets/cc3m/train_image_info.json') parser.add_argument('--not_download_image', action='store_true') args = parser.parse_args() categories = json.load(open(args.cat_info, 'r'))['categories'] images = [] if not os.path.exists(args.save_image_path): os.makedirs(args.save_image_path) f = open(args.ann) for i, line in enumerate(f): cap, path = line[:-1].split('\t') print(i, cap, path) if not args.not_download_image: os.system( 'wget {} -O {}/{}.jpg'.format( path, args.save_image_path, i + 1)) try: img = Image.open( open('{}/{}.jpg'.format(args.save_image_path, i + 1), "rb")) img = np.asarray(img.convert("RGB")) h, w = img.shape[:2] except: continue image_info = { 'id': i + 1, 'file_name': '{}.jpg'.format(i + 1), 'height': h, 'width': w, 'captions': [cap], } images.append(image_info) data = {'categories': categories, 'images': images, 'annotations': []} for k, v in data.items(): print(k, len(v)) print('Saving to', args.out_path) json.dump(data, open(args.out_path, 'w')) ================================================ FILE: tools/get_cc_tags.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import argparse import json from collections import defaultdict from detectron2.data.datasets.lvis_v1_categories import LVIS_CATEGORIES # This mapping is extracted from the official LVIS mapping: # https://github.com/lvis-dataset/lvis-api/blob/master/data/coco_to_synset.json COCO_SYNSET_CATEGORIES = [ {"synset": "person.n.01", "coco_cat_id": 1}, {"synset": "bicycle.n.01", "coco_cat_id": 2}, {"synset": "car.n.01", "coco_cat_id": 3}, {"synset": "motorcycle.n.01", "coco_cat_id": 4}, {"synset": "airplane.n.01", "coco_cat_id": 5}, {"synset": "bus.n.01", "coco_cat_id": 6}, {"synset": "train.n.01", "coco_cat_id": 7}, {"synset": "truck.n.01", "coco_cat_id": 8}, {"synset": "boat.n.01", "coco_cat_id": 9}, {"synset": "traffic_light.n.01", "coco_cat_id": 10}, {"synset": "fireplug.n.01", "coco_cat_id": 11}, {"synset": "stop_sign.n.01", "coco_cat_id": 13}, {"synset": "parking_meter.n.01", "coco_cat_id": 14}, {"synset": "bench.n.01", "coco_cat_id": 15}, {"synset": "bird.n.01", "coco_cat_id": 16}, {"synset": "cat.n.01", "coco_cat_id": 17}, {"synset": "dog.n.01", "coco_cat_id": 18}, {"synset": "horse.n.01", "coco_cat_id": 19}, {"synset": "sheep.n.01", "coco_cat_id": 20}, {"synset": "beef.n.01", "coco_cat_id": 21}, {"synset": "elephant.n.01", "coco_cat_id": 22}, {"synset": "bear.n.01", "coco_cat_id": 23}, {"synset": "zebra.n.01", "coco_cat_id": 24}, {"synset": "giraffe.n.01", "coco_cat_id": 25}, {"synset": "backpack.n.01", "coco_cat_id": 27}, {"synset": "umbrella.n.01", "coco_cat_id": 28}, {"synset": "bag.n.04", "coco_cat_id": 31}, {"synset": "necktie.n.01", "coco_cat_id": 32}, {"synset": "bag.n.06", "coco_cat_id": 33}, {"synset": "frisbee.n.01", "coco_cat_id": 34}, {"synset": "ski.n.01", "coco_cat_id": 35}, {"synset": "snowboard.n.01", "coco_cat_id": 36}, {"synset": "ball.n.06", "coco_cat_id": 37}, {"synset": "kite.n.03", "coco_cat_id": 38}, {"synset": "baseball_bat.n.01", "coco_cat_id": 39}, {"synset": "baseball_glove.n.01", "coco_cat_id": 40}, {"synset": "skateboard.n.01", "coco_cat_id": 41}, {"synset": "surfboard.n.01", "coco_cat_id": 42}, {"synset": "tennis_racket.n.01", "coco_cat_id": 43}, {"synset": "bottle.n.01", "coco_cat_id": 44}, {"synset": "wineglass.n.01", "coco_cat_id": 46}, {"synset": "cup.n.01", "coco_cat_id": 47}, {"synset": "fork.n.01", "coco_cat_id": 48}, {"synset": "knife.n.01", "coco_cat_id": 49}, {"synset": "spoon.n.01", "coco_cat_id": 50}, {"synset": "bowl.n.03", "coco_cat_id": 51}, {"synset": "banana.n.02", "coco_cat_id": 52}, {"synset": "apple.n.01", "coco_cat_id": 53}, {"synset": "sandwich.n.01", "coco_cat_id": 54}, {"synset": "orange.n.01", "coco_cat_id": 55}, {"synset": "broccoli.n.01", "coco_cat_id": 56}, {"synset": "carrot.n.01", "coco_cat_id": 57}, # {"synset": "frank.n.02", "coco_cat_id": 58}, {"synset": "sausage.n.01", "coco_cat_id": 58}, {"synset": "pizza.n.01", "coco_cat_id": 59}, {"synset": "doughnut.n.02", "coco_cat_id": 60}, {"synset": "cake.n.03", "coco_cat_id": 61}, {"synset": "chair.n.01", "coco_cat_id": 62}, {"synset": "sofa.n.01", "coco_cat_id": 63}, {"synset": "pot.n.04", "coco_cat_id": 64}, {"synset": "bed.n.01", "coco_cat_id": 65}, {"synset": "dining_table.n.01", "coco_cat_id": 67}, {"synset": "toilet.n.02", "coco_cat_id": 70}, {"synset": "television_receiver.n.01", "coco_cat_id": 72}, {"synset": "laptop.n.01", "coco_cat_id": 73}, {"synset": "mouse.n.04", "coco_cat_id": 74}, {"synset": "remote_control.n.01", "coco_cat_id": 75}, {"synset": "computer_keyboard.n.01", "coco_cat_id": 76}, {"synset": "cellular_telephone.n.01", "coco_cat_id": 77}, {"synset": "microwave.n.02", "coco_cat_id": 78}, {"synset": "oven.n.01", "coco_cat_id": 79}, {"synset": "toaster.n.02", "coco_cat_id": 80}, {"synset": "sink.n.01", "coco_cat_id": 81}, {"synset": "electric_refrigerator.n.01", "coco_cat_id": 82}, {"synset": "book.n.01", "coco_cat_id": 84}, {"synset": "clock.n.01", "coco_cat_id": 85}, {"synset": "vase.n.01", "coco_cat_id": 86}, {"synset": "scissors.n.01", "coco_cat_id": 87}, {"synset": "teddy.n.01", "coco_cat_id": 88}, {"synset": "hand_blower.n.01", "coco_cat_id": 89}, {"synset": "toothbrush.n.01", "coco_cat_id": 90}, ] COCO_DEL_SYNSET_CATEGORIES = [ {"synset": "person.n.01", "coco_cat_id": 1}, {"synset": "bicycle.n.01", "coco_cat_id": 2}, {"synset": "car.n.01", "coco_cat_id": 3}, {"synset": "motorcycle.n.01", "coco_cat_id": 4}, {"synset": "airplane.n.01", "coco_cat_id": 5}, {"synset": "bus.n.01", "coco_cat_id": 6}, {"synset": "train.n.01", "coco_cat_id": 7}, {"synset": "truck.n.01", "coco_cat_id": 8}, {"synset": "boat.n.01", "coco_cat_id": 9}, {"synset": "bench.n.01", "coco_cat_id": 15}, {"synset": "bird.n.01", "coco_cat_id": 16}, {"synset": "cat.n.01", "coco_cat_id": 17}, {"synset": "dog.n.01", "coco_cat_id": 18}, {"synset": "horse.n.01", "coco_cat_id": 19}, {"synset": "sheep.n.01", "coco_cat_id": 20}, {"synset": "beef.n.01", "coco_cat_id": 21}, {"synset": "elephant.n.01", "coco_cat_id": 22}, {"synset": "bear.n.01", "coco_cat_id": 23}, {"synset": "zebra.n.01", "coco_cat_id": 24}, {"synset": "giraffe.n.01", "coco_cat_id": 25}, {"synset": "backpack.n.01", "coco_cat_id": 27}, {"synset": "umbrella.n.01", "coco_cat_id": 28}, {"synset": "bag.n.04", "coco_cat_id": 31}, {"synset": "necktie.n.01", "coco_cat_id": 32}, {"synset": "bag.n.06", "coco_cat_id": 33}, {"synset": "frisbee.n.01", "coco_cat_id": 34}, {"synset": "ski.n.01", "coco_cat_id": 35}, {"synset": "snowboard.n.01", "coco_cat_id": 36}, {"synset": "kite.n.03", "coco_cat_id": 38}, {"synset": "skateboard.n.01", "coco_cat_id": 41}, {"synset": "surfboard.n.01", "coco_cat_id": 42}, {"synset": "bottle.n.01", "coco_cat_id": 44}, {"synset": "cup.n.01", "coco_cat_id": 47}, {"synset": "fork.n.01", "coco_cat_id": 48}, {"synset": "knife.n.01", "coco_cat_id": 49}, {"synset": "spoon.n.01", "coco_cat_id": 50}, {"synset": "bowl.n.03", "coco_cat_id": 51}, {"synset": "banana.n.02", "coco_cat_id": 52}, {"synset": "apple.n.01", "coco_cat_id": 53}, {"synset": "sandwich.n.01", "coco_cat_id": 54}, {"synset": "orange.n.01", "coco_cat_id": 55}, {"synset": "broccoli.n.01", "coco_cat_id": 56}, {"synset": "carrot.n.01", "coco_cat_id": 57}, # {"synset": "frank.n.02", "coco_cat_id": 58}, {"synset": "pizza.n.01", "coco_cat_id": 59}, {"synset": "doughnut.n.02", "coco_cat_id": 60}, {"synset": "cake.n.03", "coco_cat_id": 61}, {"synset": "chair.n.01", "coco_cat_id": 62}, {"synset": "sofa.n.01", "coco_cat_id": 63}, {"synset": "bed.n.01", "coco_cat_id": 65}, {"synset": "toilet.n.02", "coco_cat_id": 70}, {"synset": "television_receiver.n.01", "coco_cat_id": 72}, {"synset": "laptop.n.01", "coco_cat_id": 73}, {"synset": "mouse.n.04", "coco_cat_id": 74}, {"synset": "remote_control.n.01", "coco_cat_id": 75}, {"synset": "computer_keyboard.n.01", "coco_cat_id": 76}, {"synset": "microwave.n.02", "coco_cat_id": 78}, {"synset": "oven.n.01", "coco_cat_id": 79}, {"synset": "toaster.n.02", "coco_cat_id": 80}, {"synset": "sink.n.01", "coco_cat_id": 81}, {"synset": "electric_refrigerator.n.01", "coco_cat_id": 82}, {"synset": "book.n.01", "coco_cat_id": 84}, {"synset": "clock.n.01", "coco_cat_id": 85}, {"synset": "vase.n.01", "coco_cat_id": 86}, {"synset": "scissors.n.01", "coco_cat_id": 87}, {"synset": "toothbrush.n.01", "coco_cat_id": 90}, ] def map_name(x): x = x.replace('_', ' ') if '(' in x: x = x[:x.find('(')] return x.lower().strip() if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--cc_ann', default='datasets/cc3m/train_image_info.json') parser.add_argument('--out_path', default='datasets/cc3m/train_image_info_tags.json') parser.add_argument('--keep_images', action='store_true') parser.add_argument('--allcaps', action='store_true') parser.add_argument('--cat_path', default='') parser.add_argument('--convert_caption', action='store_true') # parser.add_argument('--lvis_ann', default='datasets/lvis/lvis_v1_val.json') args = parser.parse_args() # lvis_data = json.load(open(args.lvis_ann, 'r')) cc_data = json.load(open(args.cc_ann, 'r')) if args.convert_caption: num_caps = 0 caps = defaultdict(list) for x in cc_data['annotations']: caps[x['image_id']].append(x['caption']) for x in cc_data['images']: x['captions'] = caps[x['id']] num_caps += len(x['captions']) print('# captions', num_caps) if args.cat_path != '': print('Loading', args.cat_path) cats = json.load(open(args.cat_path))['categories'] if 'synonyms' not in cats[0]: cocoid2synset = {i+1: COCO_DEL_SYNSET_CATEGORIES[i]['synset'] \ for i in range(len(COCO_DEL_SYNSET_CATEGORIES))} synset2synonyms = {x['synset']: x['synonyms'] \ for x in LVIS_CATEGORIES} for x in cats: if x['id'] <66: synonyms = synset2synonyms[cocoid2synset[x['id']]] x['synonyms'] = synonyms else: x['synonyms'] = [x['name']] x['frequency'] = 'f' cc_data['categories'] = cats id2cat = {x['id']: x for x in cc_data['categories']} class_count = {x['id']: 0 for x in cc_data['categories']} class_data = {x['id']: [' ' + map_name(xx) + ' ' for xx in x['synonyms']] \ for x in cc_data['categories']} num_examples = 5 examples = {x['id']: [] for x in cc_data['categories']} print('class_data', class_data) images = [] for i, x in enumerate(cc_data['images']): if i % 10000 == 0: print(i, len(cc_data['images'])) if args.allcaps: caption = (' '.join(x['captions'])).lower() else: caption = x['captions'][0].lower() x['pos_category_ids'] = [] for cat_id, cat_names in class_data.items(): find = False for c in cat_names: if c in caption or caption.startswith(c[1:]) \ or caption.endswith(c[:-1]): find = True break if find: x['pos_category_ids'].append(cat_id) class_count[cat_id] += 1 if len(examples[cat_id]) < num_examples: examples[cat_id].append(caption) if len(x['pos_category_ids']) > 0 or args.keep_images: images.append(x) zero_class = [] for cat_id, count in class_count.items(): print(id2cat[cat_id]['name'], count, end=', ') if count == 0: zero_class.append(id2cat[cat_id]) print('==') print('zero class', zero_class) # for freq in ['r', 'c', 'f']: # print('#cats', freq, len([x for x in cc_data['categories'] \ # if x['frequency'] == freq] and class_count[x['id']] > 0)) for freq in ['r', 'c', 'f']: print('#Images', freq, sum([v for k, v in class_count.items() \ if id2cat[k]['frequency'] == freq])) try: out_data = {'images': images, 'categories': cc_data['categories'], \ 'annotations': []} for k, v in out_data.items(): print(k, len(v)) if args.keep_images and not args.out_path.endswith('_full.json'): args.out_path = args.out_path[:-5] + '_full.json' print('Writing to', args.out_path) json.dump(out_data, open(args.out_path, 'w')) except: pass ================================================ FILE: tools/get_coco_zeroshot.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import argparse import json if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--data_path', default='datasets/coco/zero-shot/instances_train2017_seen_2.json') parser.add_argument('--cat_path', default='datasets/coco/annotations/instances_val2017.json') args = parser.parse_args() print('Loading', args.cat_path) unseen_set = [ "umbrella", "cow", "cup", "bus", "keyboard", "skateboard", "dog", "couch", "tie", "snowboard", "sink", "elephant", "cake", "scissors", "airplane", "cat", "knife" ] seen_set = [ "toilet", "bicycle", "apple", "train", "laptop", "carrot", "motorcycle", "oven", "chair", "mouse", "boat", "kite", "sheep", "horse", "sandwich", "clock", "tv", "backpack", "toaster", "bowl", "microwave", "bench", "book", "orange", "bird", "pizza", "fork", "frisbee", "bear", "vase", "toothbrush", "spoon", "giraffe", "handbag", "broccoli", "refrigerator", "remote", "surfboard", "car", "bed", "banana", "donut", "skis", "person", "truck", "bottle", "suitcase", "zebra", "background" ] cates_80 = json.load(open(args.cat_path, 'r'))['categories'] cates_65 = [] id_map = {} new_id = 1 for cate in cates_80: if cate['name'] in seen_set or cate['name'] in unseen_set: id_map[cate['id']] = new_id cate['id'] = new_id new_id = new_id + 1 cates_65.append(cate) print('Loading', args.data_path) data = json.load(open(args.data_path, 'r')) data['categories'] = cates_65 for anno in data['annotations']: anno['category_id'] = id_map[anno['category_id']] out_path = args.data_path[:-5] + '_del.json' print('Saving to', out_path) json.dump(data, open(out_path, 'w')) ================================================ FILE: tools/get_lvis_cat_info.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import argparse import json if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument("--ann", default='datasets/lvis/lvis_v1_train.json') parser.add_argument("--add_freq", action='store_true') parser.add_argument("--r_thresh", type=int, default=10) parser.add_argument("--c_thresh", type=int, default=100) args = parser.parse_args() print('Loading', args.ann) data = json.load(open(args.ann, 'r')) cats = data['categories'] image_count = {x['id']: set() for x in cats} ann_count = {x['id']: 0 for x in cats} for x in data['annotations']: image_count[x['category_id']].add(x['image_id']) ann_count[x['category_id']] += 1 num_freqs = {x: 0 for x in ['r', 'f', 'c']} for x in cats: x['image_count'] = len(image_count[x['id']]) x['instance_count'] = ann_count[x['id']] if args.add_freq: freq = 'f' if x['image_count'] < args.c_thresh: freq = 'c' if x['image_count'] < args.r_thresh: freq = 'r' x['frequency'] = freq num_freqs[freq] += 1 print(cats) image_counts = sorted([x['image_count'] for x in cats]) # print('image count', image_counts) # import pdb; pdb.set_trace() if args.add_freq: for x in ['r', 'c', 'f']: print(x, num_freqs[x]) out = cats # {'categories': cats} out_path = args.ann[:-5] + '_cat_info.json' print('Saving to', out_path) json.dump(out, open(out_path, 'w')) ================================================ FILE: tools/get_tags_for_VLDet_concepts.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import argparse import json from collections import defaultdict from detectron2.data.datasets.lvis_v1_categories import LVIS_CATEGORIES from nltk import word_tokenize, pos_tag, ne_chunk import re def map_name(x): x = x.replace('_', ' ') if '(' in x: x = x[:x.find('(')] return x.lower().strip() if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--cc_ann', default='datasets/cc3m/train_image_info_tags.json') parser.add_argument('--out_path', default='datasets/cc3m/VLDet/nouns_train_image_info_6250tags.json') parser.add_argument('--keep_images', action='store_true') parser.add_argument('--allcaps', action='store_true') parser.add_argument('--cat_path', default='datasets/cc3m/VLDet/googlecc_nouns_6250.txt') parser.add_argument('--convert_caption', action='store_true') # parser.add_argument('--lvis_ann', default='datasets/lvis/lvis_v1_val.json') args = parser.parse_args() # lvis_data = json.load(open(args.lvis_ann, 'r')) cc_data = json.load(open(args.cc_ann, 'r')) if args.convert_caption: num_caps = 0 caps = defaultdict(list) for x in cc_data['annotations']: caps[x['image_id']].append(x['caption']) for x in cc_data['images']: x['captions'] = caps[x['id']] num_caps += len(x['captions']) print('# captions', num_caps) if args.cat_path != '': print('Loading', args.cat_path) ff = open(args.cat_path) cats = [] line = ff.readline() ii = 1 while line: concept = line.split(',')[0] cats.append({'id': ii, 'name': concept, 'synonyms': [concept], 'frequency': 'f', 'supercategory': concept} ) ii = ii+1 line = ff.readline() ff.close() cc_data['categories'] = cats id2cat = {x['id']: x for x in cc_data['categories']} class_count = {x['id']: 0 for x in cc_data['categories']} class_data = {x['id']: [' ' + map_name(xx) + ' ' for xx in x['synonyms']] \ for x in cc_data['categories']} num_examples = 5 examples = {x['id']: [] for x in cc_data['categories']} print('class_data', class_data) images = [] for i, x in enumerate(cc_data['images']): if i % 10000 == 0: print(i, len(cc_data['images'])) new_caption = ' ' if args.allcaps: new_caption = (' '.join(x['captions'])).lower() else: caption = x['captions'][0].lower() pos = pos_tag(word_tokenize(caption)) new_caption = ' ' for word, tag in pos: if re.match(r"NN.*", tag): new_caption = new_caption + word + ' ' x['pos_category_ids'] = [] for cat_id, cat_names in class_data.items(): find = False for c in cat_names: if c in new_caption or new_caption.startswith(c[1:]) \ or new_caption.endswith(c[:-1]): find = True break if find: x['pos_category_ids'].append(cat_id) class_count[cat_id] += 1 if len(x['pos_category_ids']) > 0 or args.keep_images: images.append(x) zero_class = [] for cat_id, count in class_count.items(): print(id2cat[cat_id]['name'], count, end=', ') if count == 0: zero_class.append(id2cat[cat_id]) print('==') print('zero class', zero_class) # for freq in ['r', 'c', 'f']: # print('#cats', freq, len([x for x in cc_data['categories'] \ # if x['frequency'] == freq] and class_count[x['id']] > 0)) for freq in ['r', 'c', 'f']: print('#Images', freq, sum([v for k, v in class_count.items() \ if id2cat[k]['frequency'] == freq])) try: out_data = {'images': images, 'categories': cc_data['categories'], \ 'annotations': []} for k, v in out_data.items(): print(k, len(v)) if args.keep_images and not args.out_path.endswith('_full.json'): args.out_path = args.out_path[:-5] + '_full.json' print('Writing to', args.out_path) json.dump(out_data, open(args.out_path, 'w')) except: pass ================================================ FILE: tools/remove_lvis_rare.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import argparse import json if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--ann', default='datasets/lvis/lvis_v1_train.json') args = parser.parse_args() print('Loading', args.ann) data = json.load(open(args.ann, 'r')) catid2freq = {x['id']: x['frequency'] for x in data['categories']} print('ori #anns', len(data['annotations'])) exclude = ['r'] data['annotations'] = [x for x in data['annotations'] \ if catid2freq[x['category_id']] not in exclude] print('filtered #anns', len(data['annotations'])) out_path = args.ann[:-5] + '_norare.json' print('Saving to', out_path) json.dump(data, open(out_path, 'w')) ================================================ FILE: train_net.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import os import sys from collections import OrderedDict import torch from torch.nn.parallel import DistributedDataParallel import time import datetime from fvcore.common.timer import Timer import detectron2.utils.comm as comm from detectron2.checkpoint import DetectionCheckpointer, PeriodicCheckpointer from detectron2.config import get_cfg from detectron2.data import ( MetadataCatalog, build_detection_test_loader, ) from detectron2.engine import default_argument_parser, default_setup, launch from detectron2.evaluation import ( inference_on_dataset, print_csv_format, LVISEvaluator, COCOEvaluator, ) from detectron2.modeling import build_model from detectron2.solver import build_lr_scheduler, build_optimizer from detectron2.utils.events import ( CommonMetricPrinter, EventStorage, JSONWriter, TensorboardXWriter, ) from detectron2.data.dataset_mapper import DatasetMapper from detectron2.data.build import build_detection_train_loader from detectron2.utils.logger import setup_logger from torch.cuda.amp import GradScaler sys.path.insert(0, 'CenterNet2/projects/CenterNet2/') from centernet.config import add_centernet_config from vldet.config import add_vldet_config from vldet.data.custom_build_augmentation import build_custom_augmentation from vldet.data.custom_dataset_dataloader import build_custom_train_loader from vldet.data.custom_dataset_mapper import CustomDatasetMapper, DetrDatasetMapper from vldet.custom_solver import build_custom_optimizer from vldet.evaluation.oideval import OIDEvaluator from vldet.evaluation.custom_coco_eval import CustomCOCOEvaluator from vldet.modeling.utils import reset_cls_test logger = logging.getLogger("detectron2") def do_test(cfg, model): results = OrderedDict() for d, dataset_name in enumerate(cfg.DATASETS.TEST): if cfg.MODEL.RESET_CLS_TESTS: reset_cls_test( model, cfg.MODEL.TEST_CLASSIFIERS[d], cfg.MODEL.TEST_NUM_CLASSES[d]) mapper = None if cfg.INPUT.TEST_INPUT_TYPE == 'default' \ else DatasetMapper( cfg, False, augmentations=build_custom_augmentation(cfg, False)) data_loader = build_detection_test_loader(cfg, dataset_name, mapper=mapper) output_folder = os.path.join( cfg.OUTPUT_DIR, "inference_{}".format(dataset_name)) evaluator_type = MetadataCatalog.get(dataset_name).evaluator_type if evaluator_type == "lvis" or cfg.GEN_PSEDO_LABELS: evaluator = LVISEvaluator(dataset_name, cfg, True, output_folder) elif evaluator_type == 'coco': if dataset_name == 'coco_generalized_zeroshot_val' or 'coco_generalized_del_val': # Additionally plot mAP for 'seen classes' and 'unseen classes' evaluator = CustomCOCOEvaluator(dataset_name, cfg, True, output_folder) else: evaluator = COCOEvaluator(dataset_name, cfg, True, output_folder) elif evaluator_type == 'oid': evaluator = OIDEvaluator(dataset_name, cfg, True, output_folder) else: assert 0, evaluator_type results[dataset_name] = inference_on_dataset( model, data_loader, evaluator) if comm.is_main_process(): logger.info("Evaluation results for {} in csv format:".format( dataset_name)) print_csv_format(results[dataset_name]) if len(results) == 1: results = list(results.values())[0] return results def do_train(cfg, model, resume=False): model.train() if cfg.SOLVER.USE_CUSTOM_SOLVER: optimizer = build_custom_optimizer(cfg, model) else: assert cfg.SOLVER.OPTIMIZER == 'SGD' assert cfg.SOLVER.CLIP_GRADIENTS.CLIP_TYPE != 'full_model' assert cfg.SOLVER.BACKBONE_MULTIPLIER == 1. optimizer = build_optimizer(cfg, model) scheduler = build_lr_scheduler(cfg, optimizer) checkpointer = DetectionCheckpointer( model, cfg.OUTPUT_DIR, optimizer=optimizer, scheduler=scheduler ) start_iter = checkpointer.resume_or_load( cfg.MODEL.WEIGHTS, resume=resume).get("iteration", -1) + 1 if not resume: start_iter = 0 max_iter = cfg.SOLVER.MAX_ITER if cfg.SOLVER.TRAIN_ITER < 0 else cfg.SOLVER.TRAIN_ITER periodic_checkpointer = PeriodicCheckpointer( checkpointer, cfg.SOLVER.CHECKPOINT_PERIOD, max_iter=max_iter ) writers = ( [ CommonMetricPrinter(max_iter), JSONWriter(os.path.join(cfg.OUTPUT_DIR, "metrics.json")), TensorboardXWriter(cfg.OUTPUT_DIR), ] if comm.is_main_process() else [] ) use_custom_mapper = cfg.WITH_IMAGE_LABELS MapperClass = CustomDatasetMapper if use_custom_mapper else DatasetMapper mapper = MapperClass(cfg, True) if cfg.INPUT.CUSTOM_AUG == '' else \ DetrDatasetMapper(cfg, True) if cfg.INPUT.CUSTOM_AUG == 'DETR' else \ MapperClass(cfg, True, augmentations=build_custom_augmentation(cfg, True)) if cfg.DATALOADER.SAMPLER_TRAIN in ['TrainingSampler', 'RepeatFactorTrainingSampler']: data_loader = build_detection_train_loader(cfg, mapper=mapper) else: data_loader = build_custom_train_loader(cfg, mapper=mapper) if cfg.FP16: scaler = GradScaler() logger.info("Starting training from iteration {}".format(start_iter)) with EventStorage(start_iter) as storage: step_timer = Timer() data_timer = Timer() start_time = time.perf_counter() for data, iteration in zip(data_loader, range(start_iter, max_iter)): data_time = data_timer.seconds() storage.put_scalars(data_time=data_time) step_timer.reset() iteration = iteration + 1 storage.step() loss_dict = model(data) losses = sum( loss for k, loss in loss_dict.items()) assert torch.isfinite(losses).all(), loss_dict loss_dict_reduced = {k: v.item() \ for k, v in comm.reduce_dict(loss_dict).items()} losses_reduced = sum(loss for loss in loss_dict_reduced.values()) if comm.is_main_process(): storage.put_scalars( total_loss=losses_reduced, **loss_dict_reduced) optimizer.zero_grad() if cfg.FP16: scaler.scale(losses).backward() scaler.step(optimizer) scaler.update() else: losses.backward() optimizer.step() storage.put_scalar( "lr", optimizer.param_groups[0]["lr"], smoothing_hint=False) step_time = step_timer.seconds() storage.put_scalars(time=step_time) data_timer.reset() scheduler.step() if (cfg.TEST.EVAL_PERIOD > 0 and iteration % cfg.TEST.EVAL_PERIOD == 0 and iteration != max_iter): do_test(cfg, model) comm.synchronize() if iteration - start_iter > 5 and \ (iteration % 20 == 0 or iteration == max_iter): for writer in writers: writer.write() periodic_checkpointer.step(iteration) total_time = time.perf_counter() - start_time logger.info( "Total training time: {}".format( str(datetime.timedelta(seconds=int(total_time))))) def setup(args): """ Create configs and perform basic setups. """ cfg = get_cfg() add_centernet_config(cfg) add_vldet_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) if '/auto' in cfg.OUTPUT_DIR: file_name = os.path.basename(args.config_file)[:-5] cfg.OUTPUT_DIR = cfg.OUTPUT_DIR.replace('/auto', '/{}'.format(file_name)) logger.info('OUTPUT_DIR: {}'.format(cfg.OUTPUT_DIR)) cfg.freeze() default_setup(cfg, args) setup_logger(output=cfg.OUTPUT_DIR, \ distributed_rank=comm.get_rank(), name="vldet") return cfg def main(args): cfg = setup(args) seed=1 torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) model = build_model(cfg) logger.info("Model:\n{}".format(model)) if args.eval_only: DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load( cfg.MODEL.WEIGHTS, resume=args.resume ) return do_test(cfg, model) distributed = comm.get_world_size() > 1 if distributed: model = DistributedDataParallel( model, device_ids=[comm.get_local_rank()], broadcast_buffers=False, find_unused_parameters=cfg.FIND_UNUSED_PARAM ) do_train(cfg, model, resume=args.resume) do_test(cfg, model) return #do_test(cfg, model) if __name__ == "__main__": args = default_argument_parser() args = args.parse_args() if args.num_machines == 1: args.dist_url = 'tcp://127.0.0.1:{}'.format( torch.randint(11111, 60000, (1,))[0].item()) else: if args.dist_url == 'host': args.dist_url = 'tcp://{}:12345'.format( os.environ['SLURM_JOB_NODELIST']) elif not args.dist_url.startswith('tcp'): tmp = os.popen( 'echo $(scontrol show job {} | grep BatchHost)'.format( args.dist_url) ).read() tmp = tmp[tmp.find('=') + 1: -1] args.dist_url = 'tcp://{}:12345'.format(tmp) print("Command Line Args:", args) launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), ) ================================================ FILE: vldet/__init__.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from .modeling.meta_arch import custom_rcnn from .modeling.roi_heads import vldet_roi_heads from .modeling.roi_heads import res5_roi_heads from .modeling.backbone import swintransformer from .modeling.backbone import timm from .data.datasets import lvis_v1 from .data.datasets import imagenet from .data.datasets import cc from .data.datasets import objects365 from .data.datasets import oid from .data.datasets import coco_zeroshot ================================================ FILE: vldet/config.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.config import CfgNode as CN def add_vldet_config(cfg): _C = cfg _C.WITH_IMAGE_LABELS = False # Turn on co-training with classification data # Open-vocabulary classifier _C.MODEL.ROI_BOX_HEAD.USE_ZEROSHOT_CLS = False # Use fixed classifier for open-vocabulary detection _C.MODEL.ROI_BOX_HEAD.ZEROSHOT_WEIGHT_PATH = 'datasets/metadata/lvis_v1_clip_a+cname.npy' _C.MODEL.ROI_BOX_HEAD.DETECTION_WEIGHT_PATH = 'datasets/metadata/lvis_v1_clip_a+cname.npy' _C.MODEL.ROI_BOX_HEAD.ZEROSHOT_WEIGHT_DIM = 512 _C.MODEL.ROI_BOX_HEAD.NORM_WEIGHT = True _C.MODEL.ROI_BOX_HEAD.NORM_TEMP = 50.0 _C.MODEL.ROI_BOX_HEAD.IGNORE_ZERO_CATS = False _C.MODEL.ROI_BOX_HEAD.USE_BIAS = 0.0 # >= 0: not use _C.MODEL.ROI_BOX_HEAD.USE_CAPTION = True _C.MODEL.ROI_BOX_HEAD.DIS_LOSS_WEIGHT = 0.001 _C.MODEL.ROI_BOX_HEAD.USE_OT = 'BCE' _C.MODEL.ROI_BOX_HEAD.USE_DISTILL = True _C.MODEL.ROI_BOX_HEAD.REPEAT_K = 3 _C.MODEL.ROI_BOX_HEAD.REPEAT_FREQ = 0.5 _C.MODEL.ROI_BOX_HEAD.MULT_PROPOSAL_SCORE = False # CenterNet2 _C.MODEL.ROI_BOX_HEAD.USE_SIGMOID_CE = False _C.MODEL.ROI_BOX_HEAD.PRIOR_PROB = 0.01 _C.MODEL.ROI_BOX_HEAD.USE_FED_LOSS = False # Federated Loss _C.MODEL.ROI_BOX_HEAD.CAT_FREQ_PATH = \ 'datasets/lvis/lvis_v1_train_norare_cat_info.json' _C.MODEL.ROI_BOX_HEAD.FED_LOSS_NUM_CAT = 50 _C.MODEL.ROI_BOX_HEAD.FED_LOSS_FREQ_WEIGHT = 0.5 # Classification data configs _C.MODEL.ROI_BOX_HEAD.IMAGE_LABEL_LOSS = 'max_size' # max, softmax, sum _C.MODEL.ROI_BOX_HEAD.IMAGE_LOSS_WEIGHT = 0.1 _C.MODEL.ROI_BOX_HEAD.IMAGE_BOX_SIZE = 1.0 _C.MODEL.ROI_BOX_HEAD.ADD_IMAGE_BOX = False # Used for image-box loss and caption loss _C.MODEL.ROI_BOX_HEAD.WS_NUM_PROPS = 128 # num proposals for image-labeled data _C.MODEL.ROI_BOX_HEAD.ONETOONE_NUM_PROPS = 5 _C.MODEL.ROI_BOX_HEAD.WITH_SOFTMAX_PROP = False # Used for WSDDN _C.MODEL.ROI_BOX_HEAD.CAPTION_WEIGHT = 1.0 # Caption loss weight _C.MODEL.ROI_BOX_HEAD.NEG_CAP_WEIGHT = 0.125 # Caption loss hyper-parameter _C.MODEL.ROI_BOX_HEAD.ADD_FEATURE_TO_PROP = False # Used for WSDDN _C.MODEL.ROI_BOX_HEAD.SOFTMAX_WEAK_LOSS = False # Used when USE_SIGMOID_CE is False _C.MODEL.ROI_BOX_HEAD.OT_LOSS_WEIGHT = 0.1 _C.MODEL.ROI_BOX_HEAD.SCALE = 100 _C.MODEL.ROI_BOX_HEAD.TAG_CONFIDENCE = 0.3 _C.MODEL.ROI_BOX_HEAD.TOP_TAGS = 10 _C.MODEL.ROI_BOX_HEAD.NEAR_CLASS_THRESH = 0.95 _C.MODEL.ROI_HEADS.MASK_WEIGHT = 1.0 _C.MODEL.ROI_HEADS.GRID_FEATURE_LAYER = 0 _C.MODEL.ROI_HEADS.ONE_CLASS_PER_PROPOSAL = False # For demo only _C.MODEL.GRID_ALIGN = CN() _C.MODEL.GRID_ALIGN.spatial_dropout = 100 _C.MODEL.GRID_ALIGN.grid_ot_weight = 0.1 _C.MODEL.GRID_ALIGN.USE_GROUNDING = False _C.MODEL.SHARE_PROJ_V_DIM = 1024 _C.MODEL.SHARE_PROJ_L_DIM = 512 # Caption losses _C.MODEL.CAP_BATCH_RATIO = 4 # Ratio between detection data and caption data _C.MODEL.WITH_CAPTION = False _C.MODEL.SYNC_CAPTION_BATCH = False # synchronize across GPUs to enlarge # "classes" # dynamic class sampling when training with 21K classes _C.MODEL.DYNAMIC_CLASSIFIER = False _C.MODEL.NUM_SAMPLE_CATS = 50 # Different classifiers in testing, used in cross-dataset evaluation _C.MODEL.RESET_CLS_TESTS = False _C.MODEL.TEST_CLASSIFIERS = [] _C.MODEL.TEST_NUM_CLASSES = [] # Backbones _C.MODEL.SWIN = CN() _C.MODEL.SWIN.SIZE = 'T' # 'T', 'S', 'B' _C.MODEL.SWIN.USE_CHECKPOINT = False _C.MODEL.SWIN.OUT_FEATURES = (1, 2, 3) # FPN stride 8 - 32 _C.MODEL.TIMM = CN() _C.MODEL.TIMM.BASE_NAME = 'resnet50' _C.MODEL.TIMM.OUT_LEVELS = (3, 4, 5) _C.MODEL.TIMM.NORM = 'FrozenBN' _C.MODEL.TIMM.FREEZE_AT = 0 _C.MODEL.DATASET_LOSS_WEIGHT = [] # Multi-dataset dataloader _C.DATALOADER.DATASET_RATIO = [1, 1] # sample ratio _C.DATALOADER.USE_RFS = [False, False] _C.DATALOADER.MULTI_DATASET_GROUPING = False # Always true when multi-dataset is enabled _C.DATALOADER.DATASET_ANN = ['box', 'box'] # Annotation type of each dataset _C.DATALOADER.USE_DIFF_BS_SIZE = False # Use different batchsize for each dataset _C.DATALOADER.DATASET_BS = [8, 32] # Used when USE_DIFF_BS_SIZE is on _C.DATALOADER.DATASET_INPUT_SIZE = [896, 384] # Used when USE_DIFF_BS_SIZE is on _C.DATALOADER.DATASET_INPUT_SCALE = [(0.1, 2.0), (0.5, 1.5)] # Used when USE_DIFF_BS_SIZE is on _C.DATALOADER.DATASET_MIN_SIZES = [(640, 800), (320, 400)] # Used when USE_DIFF_BS_SIZE is on _C.DATALOADER.DATASET_MAX_SIZES = [1333, 667] # Used when USE_DIFF_BS_SIZE is on _C.DATALOADER.USE_TAR_DATASET = False # for ImageNet-21K, directly reading from unziped files _C.DATALOADER.TARFILE_PATH = 'datasets/imagenet/metadata-22k/tar_files.npy' _C.DATALOADER.TAR_INDEX_DIR = 'datasets/imagenet/metadata-22k/tarindex_npy' _C.SOLVER.USE_CUSTOM_SOLVER = False _C.SOLVER.OPTIMIZER = 'SGD' _C.SOLVER.BACKBONE_MULTIPLIER = 1.0 # Used in DETR _C.SOLVER.CUSTOM_MULTIPLIER = 1.0 # Used in DETR _C.SOLVER.CUSTOM_MULTIPLIER_NAME = [] # Used in DETR # Deformable DETR _C.MODEL.DETR = CN() _C.MODEL.DETR.NUM_CLASSES = 80 _C.MODEL.DETR.FROZEN_WEIGHTS = '' # For Segmentation _C.MODEL.DETR.GIOU_WEIGHT = 2.0 _C.MODEL.DETR.L1_WEIGHT = 5.0 _C.MODEL.DETR.DEEP_SUPERVISION = True _C.MODEL.DETR.NO_OBJECT_WEIGHT = 0.1 _C.MODEL.DETR.CLS_WEIGHT = 2.0 _C.MODEL.DETR.NUM_FEATURE_LEVELS = 4 _C.MODEL.DETR.TWO_STAGE = False _C.MODEL.DETR.WITH_BOX_REFINE = False _C.MODEL.DETR.FOCAL_ALPHA = 0.25 _C.MODEL.DETR.NHEADS = 8 _C.MODEL.DETR.DROPOUT = 0.1 _C.MODEL.DETR.DIM_FEEDFORWARD = 2048 _C.MODEL.DETR.ENC_LAYERS = 6 _C.MODEL.DETR.DEC_LAYERS = 6 _C.MODEL.DETR.PRE_NORM = False _C.MODEL.DETR.HIDDEN_DIM = 256 _C.MODEL.DETR.NUM_OBJECT_QUERIES = 100 _C.MODEL.DETR.USE_FED_LOSS = False _C.MODEL.DETR.WEAK_WEIGHT = 0.1 _C.INPUT.CUSTOM_AUG = '' _C.INPUT.TRAIN_SIZE = 640 _C.INPUT.TEST_SIZE = 640 _C.INPUT.SCALE_RANGE = (0.1, 2.) # 'default' for fixed short/ long edge, 'square' for max size=INPUT.SIZE _C.INPUT.TEST_INPUT_TYPE = 'default' _C.FIND_UNUSED_PARAM = True _C.EVAL_PRED_AR = False _C.EVAL_PROPOSAL_AR = False _C.EVAL_CAT_SPEC_AR = False _C.IS_DEBUG = False _C.QUICK_DEBUG = False _C.FP16 = False _C.EVAL_AP_FIX = False _C.GEN_PSEDO_LABELS = False _C.SAVE_DEBUG_PATH = 'output/save_debug/' ================================================ FILE: vldet/custom_solver.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved from enum import Enum import itertools from typing import Any, Callable, Dict, Iterable, List, Set, Type, Union import torch from detectron2.config import CfgNode from detectron2.solver.build import maybe_add_gradient_clipping def match_name_keywords(n, name_keywords): out = False for b in name_keywords: if b in n: out = True break return out def build_custom_optimizer(cfg: CfgNode, model: torch.nn.Module) -> torch.optim.Optimizer: """ Build an optimizer from config. """ params: List[Dict[str, Any]] = [] memo: Set[torch.nn.parameter.Parameter] = set() custom_multiplier_name = cfg.SOLVER.CUSTOM_MULTIPLIER_NAME optimizer_type = cfg.SOLVER.OPTIMIZER for key, value in model.named_parameters(recurse=True): if not value.requires_grad: continue # Avoid duplicating parameters if value in memo: continue memo.add(value) lr = cfg.SOLVER.BASE_LR weight_decay = cfg.SOLVER.WEIGHT_DECAY if "backbone" in key: lr = lr * cfg.SOLVER.BACKBONE_MULTIPLIER if match_name_keywords(key, custom_multiplier_name): lr = lr * cfg.SOLVER.CUSTOM_MULTIPLIER print('Costum LR', key, lr) param = {"params": [value], "lr": lr} if optimizer_type != 'ADAMW': param['weight_decay'] = weight_decay params += [param] def maybe_add_full_model_gradient_clipping(optim): # optim: the optimizer class # detectron2 doesn't have full model gradient clipping now clip_norm_val = cfg.SOLVER.CLIP_GRADIENTS.CLIP_VALUE enable = ( cfg.SOLVER.CLIP_GRADIENTS.ENABLED and cfg.SOLVER.CLIP_GRADIENTS.CLIP_TYPE == "full_model" and clip_norm_val > 0.0 ) class FullModelGradientClippingOptimizer(optim): def step(self, closure=None): all_params = itertools.chain(*[x["params"] for x in self.param_groups]) torch.nn.utils.clip_grad_norm_(all_params, clip_norm_val) super().step(closure=closure) return FullModelGradientClippingOptimizer if enable else optim if optimizer_type == 'SGD': optimizer = maybe_add_full_model_gradient_clipping(torch.optim.SGD)( params, cfg.SOLVER.BASE_LR, momentum=cfg.SOLVER.MOMENTUM, nesterov=cfg.SOLVER.NESTEROV ) elif optimizer_type == 'ADAMW': optimizer = maybe_add_full_model_gradient_clipping(torch.optim.AdamW)( params, cfg.SOLVER.BASE_LR, weight_decay=cfg.SOLVER.WEIGHT_DECAY ) else: raise NotImplementedError(f"no optimizer type {optimizer_type}") if not cfg.SOLVER.CLIP_GRADIENTS.CLIP_TYPE == "full_model": optimizer = maybe_add_gradient_clipping(cfg, optimizer) return optimizer ================================================ FILE: vldet/data/custom_build_augmentation.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import numpy as np import pycocotools.mask as mask_util import torch from fvcore.common.file_io import PathManager from PIL import Image from detectron2.data import transforms as T from .transforms.custom_augmentation_impl import EfficientDetResizeCrop def build_custom_augmentation(cfg, is_train, scale=None, size=None, \ min_size=None, max_size=None): """ Create a list of default :class:`Augmentation` from config. Now it includes resizing and flipping. Returns: list[Augmentation] """ if cfg.INPUT.CUSTOM_AUG == 'ResizeShortestEdge': if is_train: min_size = cfg.INPUT.MIN_SIZE_TRAIN if min_size is None else min_size max_size = cfg.INPUT.MAX_SIZE_TRAIN if max_size is None else max_size sample_style = cfg.INPUT.MIN_SIZE_TRAIN_SAMPLING else: min_size = cfg.INPUT.MIN_SIZE_TEST max_size = cfg.INPUT.MAX_SIZE_TEST sample_style = "choice" augmentation = [T.ResizeShortestEdge(min_size, max_size, sample_style)] elif cfg.INPUT.CUSTOM_AUG == 'EfficientDetResizeCrop': if is_train: scale = cfg.INPUT.SCALE_RANGE if scale is None else scale size = cfg.INPUT.TRAIN_SIZE if size is None else size else: scale = (1, 1) size = cfg.INPUT.TEST_SIZE augmentation = [EfficientDetResizeCrop(size, scale)] else: assert 0, cfg.INPUT.CUSTOM_AUG if is_train: augmentation.append(T.RandomFlip()) return augmentation build_custom_transform_gen = build_custom_augmentation """ Alias for backward-compatibility. """ ================================================ FILE: vldet/data/custom_dataset_dataloader.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # Part of the code is from https://github.com/xingyizhou/UniDet/blob/master/projects/UniDet/unidet/data/multi_dataset_dataloader.py (Apache-2.0 License) import copy import logging import numpy as np import operator import torch import torch.utils.data import json from detectron2.utils.comm import get_world_size from detectron2.utils.logger import _log_api_usage, log_first_n from detectron2.config import configurable from detectron2.data import samplers from torch.utils.data.sampler import BatchSampler, Sampler from detectron2.data.common import DatasetFromList, MapDataset from detectron2.data.dataset_mapper import DatasetMapper from detectron2.data.build import get_detection_dataset_dicts, build_batch_data_loader from detectron2.data.samplers import TrainingSampler, RepeatFactorTrainingSampler from detectron2.data.build import worker_init_reset_seed, print_instances_class_histogram from detectron2.data.build import filter_images_with_only_crowd_annotations from detectron2.data.build import filter_images_with_few_keypoints from detectron2.data.build import check_metadata_consistency from detectron2.data.catalog import MetadataCatalog, DatasetCatalog from detectron2.utils import comm import itertools import math from collections import defaultdict from typing import Optional def _custom_train_loader_from_config(cfg, mapper=None, *, dataset=None, sampler=None): sampler_name = cfg.DATALOADER.SAMPLER_TRAIN if 'MultiDataset' in sampler_name: dataset_dicts = get_detection_dataset_dicts_with_source( cfg.DATASETS.TRAIN, filter_empty=cfg.DATALOADER.FILTER_EMPTY_ANNOTATIONS, min_keypoints=cfg.MODEL.ROI_KEYPOINT_HEAD.MIN_KEYPOINTS_PER_IMAGE if cfg.MODEL.KEYPOINT_ON else 0, proposal_files=cfg.DATASETS.PROPOSAL_FILES_TRAIN if cfg.MODEL.LOAD_PROPOSALS else None, ) else: dataset_dicts = get_detection_dataset_dicts( cfg.DATASETS.TRAIN, filter_empty=cfg.DATALOADER.FILTER_EMPTY_ANNOTATIONS, min_keypoints=cfg.MODEL.ROI_KEYPOINT_HEAD.MIN_KEYPOINTS_PER_IMAGE if cfg.MODEL.KEYPOINT_ON else 0, proposal_files=cfg.DATASETS.PROPOSAL_FILES_TRAIN if cfg.MODEL.LOAD_PROPOSALS else None, ) if mapper is None: mapper = DatasetMapper(cfg, True) if sampler is not None: pass elif sampler_name == "TrainingSampler": sampler = TrainingSampler(len(dataset)) elif sampler_name == "MultiDatasetSampler": sampler = MultiDatasetSampler( dataset_dicts, dataset_ratio = cfg.DATALOADER.DATASET_RATIO, use_rfs = cfg.DATALOADER.USE_RFS, dataset_ann = cfg.DATALOADER.DATASET_ANN, repeat_threshold = cfg.DATALOADER.REPEAT_THRESHOLD, ) elif sampler_name == "RepeatFactorTrainingSampler": repeat_factors = RepeatFactorTrainingSampler.repeat_factors_from_category_frequency( dataset_dicts, cfg.DATALOADER.REPEAT_THRESHOLD ) sampler = RepeatFactorTrainingSampler(repeat_factors) else: raise ValueError("Unknown training sampler: {}".format(sampler_name)) return { "dataset": dataset_dicts, "sampler": sampler, "mapper": mapper, "total_batch_size": cfg.SOLVER.IMS_PER_BATCH, "aspect_ratio_grouping": cfg.DATALOADER.ASPECT_RATIO_GROUPING, "num_workers": cfg.DATALOADER.NUM_WORKERS, 'multi_dataset_grouping': cfg.DATALOADER.MULTI_DATASET_GROUPING, 'use_diff_bs_size': cfg.DATALOADER.USE_DIFF_BS_SIZE, 'dataset_bs': cfg.DATALOADER.DATASET_BS, 'num_datasets': len(cfg.DATASETS.TRAIN) } @configurable(from_config=_custom_train_loader_from_config) def build_custom_train_loader( dataset, *, mapper, sampler, total_batch_size=16, aspect_ratio_grouping=True, num_workers=0, num_datasets=1, multi_dataset_grouping=False, use_diff_bs_size=False, dataset_bs=[] ): """ Modified from detectron2.data.build.build_custom_train_loader, but supports different samplers """ if isinstance(dataset, list): dataset = DatasetFromList(dataset, copy=False) if mapper is not None: dataset = MapDataset(dataset, mapper) if sampler is None: sampler = TrainingSampler(len(dataset)) assert isinstance(sampler, torch.utils.data.sampler.Sampler) if multi_dataset_grouping: return build_multi_dataset_batch_data_loader( use_diff_bs_size, dataset_bs, dataset, sampler, total_batch_size, num_datasets=num_datasets, num_workers=num_workers, ) else: return build_batch_data_loader( dataset, sampler, total_batch_size, aspect_ratio_grouping=aspect_ratio_grouping, num_workers=num_workers, ) def build_multi_dataset_batch_data_loader( use_diff_bs_size, dataset_bs, dataset, sampler, total_batch_size, num_datasets, num_workers=0 ): """ """ world_size = get_world_size() assert ( total_batch_size > 0 and total_batch_size % world_size == 0 ), "Total batch size ({}) must be divisible by the number of gpus ({}).".format( total_batch_size, world_size ) batch_size = total_batch_size // world_size data_loader = torch.utils.data.DataLoader( dataset, sampler=sampler, num_workers=num_workers, batch_sampler=None, collate_fn=operator.itemgetter(0), # don't batch, but yield individual elements worker_init_fn=worker_init_reset_seed, ) # yield individual mapped dict if use_diff_bs_size: return DIFFMDAspectRatioGroupedDataset( data_loader, dataset_bs, num_datasets) else: return MDAspectRatioGroupedDataset( data_loader, batch_size, num_datasets) def get_detection_dataset_dicts_with_source( dataset_names, filter_empty=True, min_keypoints=0, proposal_files=None ): assert len(dataset_names) dataset_dicts = [DatasetCatalog.get(dataset_name) for dataset_name in dataset_names] for dataset_name, dicts in zip(dataset_names, dataset_dicts): assert len(dicts), "Dataset '{}' is empty!".format(dataset_name) for source_id, (dataset_name, dicts) in \ enumerate(zip(dataset_names, dataset_dicts)): assert len(dicts), "Dataset '{}' is empty!".format(dataset_name) for d in dicts: d['dataset_source'] = source_id if "annotations" in dicts[0]: try: class_names = MetadataCatalog.get(dataset_name).thing_classes check_metadata_consistency("thing_classes", dataset_name) print_instances_class_histogram(dicts, class_names) except AttributeError: # class names are not available for this dataset pass assert proposal_files is None dataset_dicts = list(itertools.chain.from_iterable(dataset_dicts)) has_instances = "annotations" in dataset_dicts[0] if filter_empty and has_instances: dataset_dicts = filter_images_with_only_crowd_annotations(dataset_dicts) if min_keypoints > 0 and has_instances: dataset_dicts = filter_images_with_few_keypoints(dataset_dicts, min_keypoints) return dataset_dicts class MultiDatasetSampler(Sampler): def __init__( self, dataset_dicts, dataset_ratio, use_rfs, dataset_ann, repeat_threshold=0.001, seed: Optional[int] = None, ): """ """ sizes = [0 for _ in range(len(dataset_ratio))] for d in dataset_dicts: sizes[d['dataset_source']] += 1 print('dataset sizes', sizes) self.sizes = sizes assert len(dataset_ratio) == len(sizes), \ 'length of dataset ratio {} should be equal to number if dataset {}'.format( len(dataset_ratio), len(sizes) ) if seed is None: seed = comm.shared_random_seed() self._seed = int(seed) self._rank = comm.get_rank() self._world_size = comm.get_world_size() self.dataset_ids = torch.tensor( [d['dataset_source'] for d in dataset_dicts], dtype=torch.long) dataset_weight = [torch.ones(s) * max(sizes) / s * r / sum(dataset_ratio) \ for i, (r, s) in enumerate(zip(dataset_ratio, sizes))] dataset_weight = torch.cat(dataset_weight) rfs_factors = [] st = 0 for i, s in enumerate(sizes): if use_rfs[i]: if dataset_ann[i] == 'box': rfs_func = RepeatFactorTrainingSampler.repeat_factors_from_category_frequency else: rfs_func = repeat_factors_from_tag_frequency rfs_factor = rfs_func( dataset_dicts[st: st + s], repeat_thresh=repeat_threshold) rfs_factor = rfs_factor * (s / rfs_factor.sum()) else: rfs_factor = torch.ones(s) rfs_factors.append(rfs_factor) st = st + s rfs_factors = torch.cat(rfs_factors) self.weights = dataset_weight * rfs_factors self.sample_epoch_size = len(self.weights) def __iter__(self): start = self._rank yield from itertools.islice( self._infinite_indices(), start, None, self._world_size) def _infinite_indices(self): g = torch.Generator() g.manual_seed(self._seed) while True: ids = torch.multinomial( self.weights, self.sample_epoch_size, generator=g, replacement=True) nums = [(self.dataset_ids[ids] == i).sum().int().item() \ for i in range(len(self.sizes))] yield from ids class MDAspectRatioGroupedDataset(torch.utils.data.IterableDataset): def __init__(self, dataset, batch_size, num_datasets): """ """ self.dataset = dataset self.batch_size = batch_size self._buckets = [[] for _ in range(2 * num_datasets)] def __iter__(self): for d in self.dataset: w, h = d["width"], d["height"] aspect_ratio_bucket_id = 0 if w > h else 1 bucket_id = d['dataset_source'] * 2 + aspect_ratio_bucket_id bucket = self._buckets[bucket_id] bucket.append(d) if len(bucket) == self.batch_size: yield bucket[:] del bucket[:] class DIFFMDAspectRatioGroupedDataset(torch.utils.data.IterableDataset): def __init__(self, dataset, batch_sizes, num_datasets): """ """ self.dataset = dataset self.batch_sizes = batch_sizes self._buckets = [[] for _ in range(2 * num_datasets)] def __iter__(self): for d in self.dataset: w, h = d["width"], d["height"] aspect_ratio_bucket_id = 0 if w > h else 1 bucket_id = d['dataset_source'] * 2 + aspect_ratio_bucket_id bucket = self._buckets[bucket_id] bucket.append(d) if len(bucket) == self.batch_sizes[d['dataset_source']]: yield bucket[:] del bucket[:] def repeat_factors_from_tag_frequency(dataset_dicts, repeat_thresh): """ """ category_freq = defaultdict(int) for dataset_dict in dataset_dicts: cat_ids = dataset_dict['pos_category_ids'] for cat_id in cat_ids: category_freq[cat_id] += 1 num_images = len(dataset_dicts) for k, v in category_freq.items(): category_freq[k] = v / num_images category_rep = { cat_id: max(1.0, math.sqrt(repeat_thresh / cat_freq)) for cat_id, cat_freq in category_freq.items() } rep_factors = [] for dataset_dict in dataset_dicts: cat_ids = dataset_dict['pos_category_ids'] rep_factor = max({category_rep[cat_id] for cat_id in cat_ids}, default=1.0) rep_factors.append(rep_factor) return torch.tensor(rep_factors, dtype=torch.float32) ================================================ FILE: vldet/data/custom_dataset_mapper.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved import copy import logging import numpy as np from typing import List, Optional, Union import torch import pycocotools.mask as mask_util from detectron2.config import configurable from detectron2.data import detection_utils as utils from detectron2.data.detection_utils import transform_keypoint_annotations from detectron2.data import transforms as T from detectron2.data.dataset_mapper import DatasetMapper from detectron2.structures import Boxes, BoxMode, Instances from detectron2.structures import Keypoints, PolygonMasks, BitMasks from fvcore.transforms.transform import TransformList from .custom_build_augmentation import build_custom_augmentation from .tar_dataset import DiskTarDataset import clip from torchvision.transforms import Compose, Resize, CenterCrop, ToTensor, Normalize __all__ = ["CustomDatasetMapper"] class CustomDatasetMapper(DatasetMapper): @configurable def __init__(self, is_train: bool, with_ann_type=False, dataset_ann=[], use_diff_bs_size=False, dataset_augs=[], is_debug=False, use_tar_dataset=False, tarfile_path='', tar_index_dir='', **kwargs): """ add image labels """ self.with_ann_type = with_ann_type self.dataset_ann = dataset_ann self.use_diff_bs_size = use_diff_bs_size if self.use_diff_bs_size and is_train: self.dataset_augs = [T.AugmentationList(x) for x in dataset_augs] self.is_debug = is_debug self.use_tar_dataset = use_tar_dataset if self.use_tar_dataset: print('Using tar dataset') self.tar_dataset = DiskTarDataset(tarfile_path, tar_index_dir) def _transform(): return Compose([ ToTensor(), Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711)), ]) self.clip_transform = _transform() super().__init__(is_train, **kwargs) @classmethod def from_config(cls, cfg, is_train: bool = True): ret = super().from_config(cfg, is_train) ret.update({ 'with_ann_type': cfg.WITH_IMAGE_LABELS, 'dataset_ann': cfg.DATALOADER.DATASET_ANN, 'use_diff_bs_size': cfg.DATALOADER.USE_DIFF_BS_SIZE, 'is_debug': cfg.IS_DEBUG, 'use_tar_dataset': cfg.DATALOADER.USE_TAR_DATASET, 'tarfile_path': cfg.DATALOADER.TARFILE_PATH, 'tar_index_dir': cfg.DATALOADER.TAR_INDEX_DIR, }) if ret['use_diff_bs_size'] and is_train: if cfg.INPUT.CUSTOM_AUG == 'EfficientDetResizeCrop': dataset_scales = cfg.DATALOADER.DATASET_INPUT_SCALE dataset_sizes = cfg.DATALOADER.DATASET_INPUT_SIZE ret['dataset_augs'] = [ build_custom_augmentation(cfg, True, scale, size) \ for scale, size in zip(dataset_scales, dataset_sizes)] else: assert cfg.INPUT.CUSTOM_AUG == 'ResizeShortestEdge' min_sizes = cfg.DATALOADER.DATASET_MIN_SIZES max_sizes = cfg.DATALOADER.DATASET_MAX_SIZES ret['dataset_augs'] = [ build_custom_augmentation( cfg, True, min_size=mi, max_size=ma) \ for mi, ma in zip(min_sizes, max_sizes)] else: ret['dataset_augs'] = [] return ret def __call__(self, dataset_dict): """ include image labels """ dataset_dict = copy.deepcopy(dataset_dict) # it will be modified by code below # USER: Write your own image loading if it's not from a file if 'file_name' in dataset_dict: ori_image = utils.read_image( dataset_dict["file_name"], format=self.image_format) else: ori_image, _, _ = self.tar_dataset[dataset_dict["tar_index"]] ori_image = utils._apply_exif_orientation(ori_image) ori_image = utils.convert_PIL_to_numpy(ori_image, self.image_format) utils.check_image_size(dataset_dict, ori_image) # USER: Remove if you don't do semantic/panoptic segmentation. if "sem_seg_file_name" in dataset_dict: sem_seg_gt = utils.read_image( dataset_dict.pop("sem_seg_file_name"), "L").squeeze(2) else: sem_seg_gt = None if self.is_debug: dataset_dict['dataset_source'] = 0 not_full_labeled = 'dataset_source' in dataset_dict and \ self.with_ann_type and \ self.dataset_ann[dataset_dict['dataset_source']] != 'box' aug_input = T.AugInput(copy.deepcopy(ori_image), sem_seg=sem_seg_gt) if self.use_diff_bs_size and self.is_train: transforms = \ self.dataset_augs[dataset_dict['dataset_source']](aug_input) else: transforms = self.augmentations(aug_input) image, sem_seg_gt = aug_input.image, aug_input.sem_seg image_shape = image.shape[:2] # h, w dataset_dict["image"] = torch.as_tensor( np.ascontiguousarray(image.transpose(2, 0, 1))) image = np.ascontiguousarray(image) dataset_dict["clip_image"] = self.clip_transform(image) if sem_seg_gt is not None: dataset_dict["sem_seg"] = torch.as_tensor(sem_seg_gt.astype("long")) # USER: Remove if you don't use pre-computed proposals. # Most users would not need this feature. if self.proposal_topk is not None: utils.transform_proposals( dataset_dict, image_shape, transforms, proposal_topk=self.proposal_topk ) if not self.is_train: # USER: Modify this if you want to keep them for some reason. dataset_dict.pop("annotations", None) dataset_dict.pop("sem_seg_file_name", None) return dataset_dict if "annotations" in dataset_dict: # USER: Modify this if you want to keep them for some reason. for anno in dataset_dict["annotations"]: if not self.use_instance_mask: anno.pop("segmentation", None) if not self.use_keypoint: anno.pop("keypoints", None) # USER: Implement additional transformations if you have other types of data all_annos = [ (utils.transform_instance_annotations( obj, transforms, image_shape, keypoint_hflip_indices=self.keypoint_hflip_indices, ), obj.get("iscrowd", 0)) for obj in dataset_dict.pop("annotations") ] annos = [ann[0] for ann in all_annos if ann[1] == 0] instances = utils.annotations_to_instances( annos, image_shape, mask_format=self.instance_mask_format ) del all_annos if self.recompute_boxes: instances.gt_boxes = instances.gt_masks.get_bounding_boxes() dataset_dict["instances"] = utils.filter_empty_instances(instances) if self.with_ann_type: dataset_dict["pos_category_ids"] = dataset_dict.get( 'pos_category_ids', []) dataset_dict["ann_type"] = \ self.dataset_ann[dataset_dict['dataset_source']] if self.is_debug and (('pos_category_ids' not in dataset_dict) or \ (dataset_dict['pos_category_ids'] == [])): dataset_dict['pos_category_ids'] = [x for x in sorted(set( dataset_dict['instances'].gt_classes.tolist() ))] return dataset_dict # DETR augmentation def build_transform_gen(cfg, is_train): """ """ if is_train: min_size = cfg.INPUT.MIN_SIZE_TRAIN max_size = cfg.INPUT.MAX_SIZE_TRAIN sample_style = cfg.INPUT.MIN_SIZE_TRAIN_SAMPLING else: min_size = cfg.INPUT.MIN_SIZE_TEST max_size = cfg.INPUT.MAX_SIZE_TEST sample_style = "choice" if sample_style == "range": assert len(min_size) == 2, "more than 2 ({}) min_size(s) are provided for ranges".format(len(min_size)) logger = logging.getLogger(__name__) tfm_gens = [] if is_train: tfm_gens.append(T.RandomFlip()) tfm_gens.append(T.ResizeShortestEdge(min_size, max_size, sample_style)) if is_train: logger.info("TransformGens used in training: " + str(tfm_gens)) return tfm_gens class DetrDatasetMapper: """ A callable which takes a dataset dict in Detectron2 Dataset format, and map it into a format used by DETR. The callable currently does the following: 1. Read the image from "file_name" 2. Applies geometric transforms to the image and annotation 3. Find and applies suitable cropping to the image and annotation 4. Prepare image and annotation to Tensors """ def __init__(self, cfg, is_train=True): if cfg.INPUT.CROP.ENABLED and is_train: self.crop_gen = [ T.ResizeShortestEdge([400, 500, 600], sample_style="choice"), T.RandomCrop(cfg.INPUT.CROP.TYPE, cfg.INPUT.CROP.SIZE), ] else: self.crop_gen = None self.mask_on = cfg.MODEL.MASK_ON self.tfm_gens = build_transform_gen(cfg, is_train) logging.getLogger(__name__).info( "Full TransformGens used in training: {}, crop: {}".format(str(self.tfm_gens), str(self.crop_gen)) ) self.img_format = cfg.INPUT.FORMAT self.is_train = is_train def __call__(self, dataset_dict): """ Args: dataset_dict (dict): Metadata of one image, in Detectron2 Dataset format. Returns: dict: a format that builtin models in detectron2 accept """ dataset_dict = copy.deepcopy(dataset_dict) # it will be modified by code below image = utils.read_image(dataset_dict["file_name"], format=self.img_format) utils.check_image_size(dataset_dict, image) if self.crop_gen is None: image, transforms = T.apply_transform_gens(self.tfm_gens, image) else: if np.random.rand() > 0.5: image, transforms = T.apply_transform_gens(self.tfm_gens, image) else: image, transforms = T.apply_transform_gens( self.tfm_gens[:-1] + self.crop_gen + self.tfm_gens[-1:], image ) image_shape = image.shape[:2] # h, w # Pytorch's dataloader is efficient on torch.Tensor due to shared-memory, # but not efficient on large generic data structures due to the use of pickle & mp.Queue. # Therefore it's important to use torch.Tensor. dataset_dict["image"] = torch.as_tensor(np.ascontiguousarray(image.transpose(2, 0, 1))) if not self.is_train: # USER: Modify this if you want to keep them for some reason. dataset_dict.pop("annotations", None) return dataset_dict if "annotations" in dataset_dict: # USER: Modify this if you want to keep them for some reason. for anno in dataset_dict["annotations"]: if not self.mask_on: anno.pop("segmentation", None) anno.pop("keypoints", None) # USER: Implement additional transformations if you have other types of data annos = [ utils.transform_instance_annotations(obj, transforms, image_shape) for obj in dataset_dict.pop("annotations") if obj.get("iscrowd", 0) == 0 ] instances = utils.annotations_to_instances(annos, image_shape) dataset_dict["instances"] = utils.filter_empty_instances(instances) return dataset_dict ================================================ FILE: vldet/data/datasets/cc.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import os from detectron2.data.datasets.builtin_meta import _get_builtin_metadata from detectron2.data.datasets.lvis import get_lvis_instances_meta from .lvis_v1 import custom_register_lvis_instances _CUSTOM_SPLITS = { "cc3m_v1_val": ("cc3m/validation/", "cc3m/val_image_info.json"), "cc3m_v1_train": ("cc3m/training/", "cc3m/train_image_info.json"), "cc3m_v1_train_tags": ("cc3m/training/", "cc3m/train_image_info_tags.json"), "all_cc3m_v1_train_tags": ("cc3m/training/", "cc3m/all_train_image_info_tags_full.json"), "cc3m_v1_nouns_train_6250tags": ("cc3m/training/", "cc3m/VLDet/nouns_train_image_info_6250tags.json"), } for key, (image_root, json_file) in _CUSTOM_SPLITS.items(): custom_register_lvis_instances( key, get_lvis_instances_meta('lvis_v1'), os.path.join("datasets", json_file) if "://" not in json_file else json_file, os.path.join("datasets", image_root), ) ================================================ FILE: vldet/data/datasets/coco_zeroshot.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import os from detectron2.data.datasets.register_coco import register_coco_instances from detectron2.data.datasets.builtin_meta import _get_builtin_metadata from .lvis_v1 import custom_register_lvis_instances import torch import json categories_seen = [ {'id': 1, 'name': 'person'}, {'id': 2, 'name': 'bicycle'}, {'id': 3, 'name': 'car'}, {'id': 4, 'name': 'motorcycle'}, {'id': 7, 'name': 'train'}, {'id': 8, 'name': 'truck'}, {'id': 9, 'name': 'boat'}, {'id': 15, 'name': 'bench'}, {'id': 16, 'name': 'bird'}, {'id': 19, 'name': 'horse'}, {'id': 20, 'name': 'sheep'}, {'id': 23, 'name': 'bear'}, {'id': 24, 'name': 'zebra'}, {'id': 25, 'name': 'giraffe'}, {'id': 27, 'name': 'backpack'}, {'id': 31, 'name': 'handbag'}, {'id': 33, 'name': 'suitcase'}, {'id': 34, 'name': 'frisbee'}, {'id': 35, 'name': 'skis'}, {'id': 38, 'name': 'kite'}, {'id': 42, 'name': 'surfboard'}, {'id': 44, 'name': 'bottle'}, {'id': 48, 'name': 'fork'}, {'id': 50, 'name': 'spoon'}, {'id': 51, 'name': 'bowl'}, {'id': 52, 'name': 'banana'}, {'id': 53, 'name': 'apple'}, {'id': 54, 'name': 'sandwich'}, {'id': 55, 'name': 'orange'}, {'id': 56, 'name': 'broccoli'}, {'id': 57, 'name': 'carrot'}, {'id': 59, 'name': 'pizza'}, {'id': 60, 'name': 'donut'}, {'id': 62, 'name': 'chair'}, {'id': 65, 'name': 'bed'}, {'id': 70, 'name': 'toilet'}, {'id': 72, 'name': 'tv'}, {'id': 73, 'name': 'laptop'}, {'id': 74, 'name': 'mouse'}, {'id': 75, 'name': 'remote'}, {'id': 78, 'name': 'microwave'}, {'id': 79, 'name': 'oven'}, {'id': 80, 'name': 'toaster'}, {'id': 82, 'name': 'refrigerator'}, {'id': 84, 'name': 'book'}, {'id': 85, 'name': 'clock'}, {'id': 86, 'name': 'vase'}, {'id': 90, 'name': 'toothbrush'}, ] categories_unseen = [ {'id': 5, 'name': 'airplane'}, {'id': 6, 'name': 'bus'}, {'id': 17, 'name': 'cat'}, {'id': 18, 'name': 'dog'}, {'id': 21, 'name': 'cow'}, {'id': 22, 'name': 'elephant'}, {'id': 28, 'name': 'umbrella'}, {'id': 32, 'name': 'tie'}, {'id': 36, 'name': 'snowboard'}, {'id': 41, 'name': 'skateboard'}, {'id': 47, 'name': 'cup'}, {'id': 49, 'name': 'knife'}, {'id': 61, 'name': 'cake'}, {'id': 63, 'name': 'couch'}, {'id': 76, 'name': 'keyboard'}, {'id': 81, 'name': 'sink'}, {'id': 87, 'name': 'scissors'}, ] _zero_shot_setting_weight_mask = [ 1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 1., 0., 0., 1., 1., 0., 1., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 1., 1., 1., 1., 1., 0., 1., 0., 1., 1., 1., 1., 1., 1., 0., 1., 1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 1.] def _zero_shot_setting_weight(): aa = open('datasets/coco/zero-shot/instances_train2017_seen_2_oriorder.json') category = json.load(aa) category = category['categories'] new_cate = {} for ind, cate in enumerate(category): new_cate[cate['id']] = ind weight_mask = torch.zeros(80) for cate in categories_seen+categories_unseen: cate_id = cate['id'] ind = new_cate[cate_id] weight_mask[ind] = 1. import ipdb; ipdb.set_trace() return weight_mask def _get_metadata(cat): if cat == 'all': return _get_builtin_metadata('coco') elif cat == 'seen': id_to_name = {x['id']: x['name'] for x in categories_seen} elif cat == 'unseen': # assert cat == 'unseen' id_to_name = {x['id']: x['name'] for x in categories_unseen} else: categories_name = json.load(open('datasets/coco/zero-shot/instances_val2017_all_2_del.json'))['categories'] id_to_name = {x['id']: x['name'] for x in categories_name} thing_dataset_id_to_contiguous_id = { x: i for i, x in enumerate(sorted(id_to_name))} thing_classes = [id_to_name[k] for k in sorted(id_to_name)] return { "thing_dataset_id_to_contiguous_id": thing_dataset_id_to_contiguous_id, "thing_classes": thing_classes} _PREDEFINED_SPLITS_COCO = { "coco_generalized_del_val": ("coco/val2017", "coco/zero-shot/instances_val2017_all_2_del.json", 'generalize'), "coco_zeroshot_train_del": ("coco/train2017", "coco/zero-shot/instances_train2017_seen_2_del.json", 'generalize'), } for key, (image_root, json_file, cat) in _PREDEFINED_SPLITS_COCO.items(): register_coco_instances( key, _get_metadata(cat), os.path.join("datasets", json_file) if "://" not in json_file else json_file, os.path.join("datasets", image_root), ) _CUSTOM_SPLITS_COCO = { "cc3m_coco_train_tags": ("cc3m/training/", "cc3m/train_image_for_coco_info_tags.json"), "sbu_coco_train_tags": ("sbu/training/", "sbu/train_image_for_coco_info_tags.json"), "sbu_train_4764tags": ("sbu/training/", "sbu/regionclip_captions_train2017_4764tags.json"), "coco_caption_nouns_train_4764tags": ("coco/train2017", "coco/VLDet/nouns_captions_train2017_4764tags_allcaps.json"),} for key, (image_root, json_file) in _CUSTOM_SPLITS_COCO.items(): custom_register_lvis_instances( key, _get_builtin_metadata('coco'), os.path.join("datasets", json_file) if "://" not in json_file else json_file, os.path.join("datasets", image_root), ) ================================================ FILE: vldet/data/datasets/imagenet.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import os from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.data.datasets.lvis import get_lvis_instances_meta from .lvis_v1 import custom_load_lvis_json, get_lvis_22k_meta def custom_register_imagenet_instances(name, metadata, json_file, image_root): """ """ DatasetCatalog.register(name, lambda: custom_load_lvis_json( json_file, image_root, name)) MetadataCatalog.get(name).set( json_file=json_file, image_root=image_root, evaluator_type="imagenet", **metadata ) _CUSTOM_SPLITS_IMAGENET = { "imagenet_lvis_v1": ("imagenet/ImageNet-LVIS/", "imagenet/annotations/imagenet_lvis_image_info.json"), } for key, (image_root, json_file) in _CUSTOM_SPLITS_IMAGENET.items(): custom_register_imagenet_instances( key, get_lvis_instances_meta('lvis_v1'), os.path.join("datasets", json_file) if "://" not in json_file else json_file, os.path.join("datasets", image_root), ) _CUSTOM_SPLITS_IMAGENET_22K = { "imagenet_lvis-22k": ("imagenet/ImageNet-LVIS/", "imagenet/annotations/imagenet-22k_image_info_lvis-22k.json"), } for key, (image_root, json_file) in _CUSTOM_SPLITS_IMAGENET_22K.items(): custom_register_imagenet_instances( key, get_lvis_22k_meta(), os.path.join("datasets", json_file) if "://" not in json_file else json_file, os.path.join("datasets", image_root), ) ================================================ FILE: vldet/data/datasets/lvis_22k_categories.py ================================================ CATEGORIES = [{'name': 'aerosol_can', 'id': 1, 'frequency': 'c', 'synset': 'aerosol.n.02'}, {'name': 'air_conditioner', 'id': 2, 'frequency': 'f', 'synset': 'air_conditioner.n.01'}, {'name': 'airplane', 'id': 3, 'frequency': 'f', 'synset': 'airplane.n.01'}, {'name': 'alarm_clock', 'id': 4, 'frequency': 'f', 'synset': 'alarm_clock.n.01'}, {'name': 'alcohol', 'id': 5, 'frequency': 'c', 'synset': 'alcohol.n.01'}, {'name': 'alligator', 'id': 6, 'frequency': 'c', 'synset': 'alligator.n.02'}, {'name': 'almond', 'id': 7, 'frequency': 'c', 'synset': 'almond.n.02'}, {'name': 'ambulance', 'id': 8, 'frequency': 'c', 'synset': 'ambulance.n.01'}, {'name': 'amplifier', 'id': 9, 'frequency': 'c', 'synset': 'amplifier.n.01'}, {'name': 'anklet', 'id': 10, 'frequency': 'c', 'synset': 'anklet.n.03'}, {'name': 'antenna', 'id': 11, 'frequency': 'f', 'synset': 'antenna.n.01'}, {'name': 'apple', 'id': 12, 'frequency': 'f', 'synset': 'apple.n.01'}, {'name': 'applesauce', 'id': 13, 'frequency': 'r', 'synset': 'applesauce.n.01'}, {'name': 'apricot', 'id': 14, 'frequency': 'r', 'synset': 'apricot.n.02'}, {'name': 'apron', 'id': 15, 'frequency': 'f', 'synset': 'apron.n.01'}, {'name': 'aquarium', 'id': 16, 'frequency': 'c', 'synset': 'aquarium.n.01'}, {'name': 'arctic_(type_of_shoe)', 'id': 17, 'frequency': 'r', 'synset': 'arctic.n.02'}, {'name': 'armband', 'id': 18, 'frequency': 'c', 'synset': 'armband.n.02'}, {'name': 'armchair', 'id': 19, 'frequency': 'f', 'synset': 'armchair.n.01'}, {'name': 'armoire', 'id': 20, 'frequency': 'r', 'synset': 'armoire.n.01'}, {'name': 'armor', 'id': 21, 'frequency': 'r', 'synset': 'armor.n.01'}, {'name': 'artichoke', 'id': 22, 'frequency': 'c', 'synset': 'artichoke.n.02'}, {'name': 'trash_can', 'id': 23, 'frequency': 'f', 'synset': 'ashcan.n.01'}, {'name': 'ashtray', 'id': 24, 'frequency': 'c', 'synset': 'ashtray.n.01'}, {'name': 'asparagus', 'id': 25, 'frequency': 'c', 'synset': 'asparagus.n.02'}, {'name': 'atomizer', 'id': 26, 'frequency': 'c', 'synset': 'atomizer.n.01'}, {'name': 'avocado', 'id': 27, 'frequency': 'f', 'synset': 'avocado.n.01'}, {'name': 'award', 'id': 28, 'frequency': 'c', 'synset': 'award.n.02'}, {'name': 'awning', 'id': 29, 'frequency': 'f', 'synset': 'awning.n.01'}, {'name': 'ax', 'id': 30, 'frequency': 'r', 'synset': 'ax.n.01'}, {'name': 'baboon', 'id': 31, 'frequency': 'r', 'synset': 'baboon.n.01'}, {'name': 'baby_buggy', 'id': 32, 'frequency': 'f', 'synset': 'baby_buggy.n.01'}, {'name': 'basketball_backboard', 'id': 33, 'frequency': 'c', 'synset': 'backboard.n.01'}, {'name': 'backpack', 'id': 34, 'frequency': 'f', 'synset': 'backpack.n.01'}, {'name': 'handbag', 'id': 35, 'frequency': 'f', 'synset': 'bag.n.04'}, {'name': 'suitcase', 'id': 36, 'frequency': 'f', 'synset': 'bag.n.06'}, {'name': 'bagel', 'id': 37, 'frequency': 'c', 'synset': 'bagel.n.01'}, {'name': 'bagpipe', 'id': 38, 'frequency': 'r', 'synset': 'bagpipe.n.01'}, {'name': 'baguet', 'id': 39, 'frequency': 'r', 'synset': 'baguet.n.01'}, {'name': 'bait', 'id': 40, 'frequency': 'r', 'synset': 'bait.n.02'}, {'name': 'ball', 'id': 41, 'frequency': 'f', 'synset': 'ball.n.06'}, {'name': 'ballet_skirt', 'id': 42, 'frequency': 'r', 'synset': 'ballet_skirt.n.01'}, {'name': 'balloon', 'id': 43, 'frequency': 'f', 'synset': 'balloon.n.01'}, {'name': 'bamboo', 'id': 44, 'frequency': 'c', 'synset': 'bamboo.n.02'}, {'name': 'banana', 'id': 45, 'frequency': 'f', 'synset': 'banana.n.02'}, {'name': 'Band_Aid', 'id': 46, 'frequency': 'c', 'synset': 'band_aid.n.01'}, {'name': 'bandage', 'id': 47, 'frequency': 'c', 'synset': 'bandage.n.01'}, {'name': 'bandanna', 'id': 48, 'frequency': 'f', 'synset': 'bandanna.n.01'}, {'name': 'banjo', 'id': 49, 'frequency': 'r', 'synset': 'banjo.n.01'}, {'name': 'banner', 'id': 50, 'frequency': 'f', 'synset': 'banner.n.01'}, {'name': 'barbell', 'id': 51, 'frequency': 'r', 'synset': 'barbell.n.01'}, {'name': 'barge', 'id': 52, 'frequency': 'r', 'synset': 'barge.n.01'}, {'name': 'barrel', 'id': 53, 'frequency': 'f', 'synset': 'barrel.n.02'}, {'name': 'barrette', 'id': 54, 'frequency': 'c', 'synset': 'barrette.n.01'}, {'name': 'barrow', 'id': 55, 'frequency': 'c', 'synset': 'barrow.n.03'}, {'name': 'baseball_base', 'id': 56, 'frequency': 'f', 'synset': 'base.n.03'}, {'name': 'baseball', 'id': 57, 'frequency': 'f', 'synset': 'baseball.n.02'}, {'name': 'baseball_bat', 'id': 58, 'frequency': 'f', 'synset': 'baseball_bat.n.01'}, {'name': 'baseball_cap', 'id': 59, 'frequency': 'f', 'synset': 'baseball_cap.n.01'}, {'name': 'baseball_glove', 'id': 60, 'frequency': 'f', 'synset': 'baseball_glove.n.01'}, {'name': 'basket', 'id': 61, 'frequency': 'f', 'synset': 'basket.n.01'}, {'name': 'basketball', 'id': 62, 'frequency': 'c', 'synset': 'basketball.n.02'}, {'name': 'bass_horn', 'id': 63, 'frequency': 'r', 'synset': 'bass_horn.n.01'}, {'name': 'bat_(animal)', 'id': 64, 'frequency': 'c', 'synset': 'bat.n.01'}, {'name': 'bath_mat', 'id': 65, 'frequency': 'f', 'synset': 'bath_mat.n.01'}, {'name': 'bath_towel', 'id': 66, 'frequency': 'f', 'synset': 'bath_towel.n.01'}, {'name': 'bathrobe', 'id': 67, 'frequency': 'c', 'synset': 'bathrobe.n.01'}, {'name': 'bathtub', 'id': 68, 'frequency': 'f', 'synset': 'bathtub.n.01'}, {'name': 'batter_(food)', 'id': 69, 'frequency': 'r', 'synset': 'batter.n.02'}, {'name': 'battery', 'id': 70, 'frequency': 'c', 'synset': 'battery.n.02'}, {'name': 'beachball', 'id': 71, 'frequency': 'r', 'synset': 'beach_ball.n.01'}, {'name': 'bead', 'id': 72, 'frequency': 'c', 'synset': 'bead.n.01'}, {'name': 'bean_curd', 'id': 73, 'frequency': 'c', 'synset': 'bean_curd.n.01'}, {'name': 'beanbag', 'id': 74, 'frequency': 'c', 'synset': 'beanbag.n.01'}, {'name': 'beanie', 'id': 75, 'frequency': 'f', 'synset': 'beanie.n.01'}, {'name': 'bear', 'id': 76, 'frequency': 'f', 'synset': 'bear.n.01'}, {'name': 'bed', 'id': 77, 'frequency': 'f', 'synset': 'bed.n.01'}, {'name': 'bedpan', 'id': 78, 'frequency': 'r', 'synset': 'bedpan.n.01'}, {'name': 'bedspread', 'id': 79, 'frequency': 'f', 'synset': 'bedspread.n.01'}, {'name': 'cow', 'id': 80, 'frequency': 'f', 'synset': 'beef.n.01'}, {'name': 'beef_(food)', 'id': 81, 'frequency': 'f', 'synset': 'beef.n.02'}, {'name': 'beeper', 'id': 82, 'frequency': 'r', 'synset': 'beeper.n.01'}, {'name': 'beer_bottle', 'id': 83, 'frequency': 'f', 'synset': 'beer_bottle.n.01'}, {'name': 'beer_can', 'id': 84, 'frequency': 'c', 'synset': 'beer_can.n.01'}, {'name': 'beetle', 'id': 85, 'frequency': 'r', 'synset': 'beetle.n.01'}, {'name': 'bell', 'id': 86, 'frequency': 'f', 'synset': 'bell.n.01'}, {'name': 'bell_pepper', 'id': 87, 'frequency': 'f', 'synset': 'bell_pepper.n.02'}, {'name': 'belt', 'id': 88, 'frequency': 'f', 'synset': 'belt.n.02'}, {'name': 'belt_buckle', 'id': 89, 'frequency': 'f', 'synset': 'belt_buckle.n.01'}, {'name': 'bench', 'id': 90, 'frequency': 'f', 'synset': 'bench.n.01'}, {'name': 'beret', 'id': 91, 'frequency': 'c', 'synset': 'beret.n.01'}, {'name': 'bib', 'id': 92, 'frequency': 'c', 'synset': 'bib.n.02'}, {'name': 'Bible', 'id': 93, 'frequency': 'r', 'synset': 'bible.n.01'}, {'name': 'bicycle', 'id': 94, 'frequency': 'f', 'synset': 'bicycle.n.01'}, {'name': 'visor', 'id': 95, 'frequency': 'f', 'synset': 'bill.n.09'}, {'name': 'billboard', 'id': 96, 'frequency': 'f', 'synset': 'billboard.n.01'}, {'name': 'binder', 'id': 97, 'frequency': 'c', 'synset': 'binder.n.03'}, {'name': 'binoculars', 'id': 98, 'frequency': 'c', 'synset': 'binoculars.n.01'}, {'name': 'bird', 'id': 99, 'frequency': 'f', 'synset': 'bird.n.01'}, {'name': 'birdfeeder', 'id': 100, 'frequency': 'c', 'synset': 'bird_feeder.n.01'}, {'name': 'birdbath', 'id': 101, 'frequency': 'c', 'synset': 'birdbath.n.01'}, {'name': 'birdcage', 'id': 102, 'frequency': 'c', 'synset': 'birdcage.n.01'}, {'name': 'birdhouse', 'id': 103, 'frequency': 'c', 'synset': 'birdhouse.n.01'}, {'name': 'birthday_cake', 'id': 104, 'frequency': 'f', 'synset': 'birthday_cake.n.01'}, {'name': 'birthday_card', 'id': 105, 'frequency': 'r', 'synset': 'birthday_card.n.01'}, {'name': 'pirate_flag', 'id': 106, 'frequency': 'r', 'synset': 'black_flag.n.01'}, {'name': 'black_sheep', 'id': 107, 'frequency': 'c', 'synset': 'black_sheep.n.02'}, {'name': 'blackberry', 'id': 108, 'frequency': 'c', 'synset': 'blackberry.n.01'}, {'name': 'blackboard', 'id': 109, 'frequency': 'f', 'synset': 'blackboard.n.01'}, {'name': 'blanket', 'id': 110, 'frequency': 'f', 'synset': 'blanket.n.01'}, {'name': 'blazer', 'id': 111, 'frequency': 'c', 'synset': 'blazer.n.01'}, {'name': 'blender', 'id': 112, 'frequency': 'f', 'synset': 'blender.n.01'}, {'name': 'blimp', 'id': 113, 'frequency': 'r', 'synset': 'blimp.n.02'}, {'name': 'blinker', 'id': 114, 'frequency': 'f', 'synset': 'blinker.n.01'}, {'name': 'blouse', 'id': 115, 'frequency': 'f', 'synset': 'blouse.n.01'}, {'name': 'blueberry', 'id': 116, 'frequency': 'f', 'synset': 'blueberry.n.02'}, {'name': 'gameboard', 'id': 117, 'frequency': 'r', 'synset': 'board.n.09'}, {'name': 'boat', 'id': 118, 'frequency': 'f', 'synset': 'boat.n.01'}, {'name': 'bob', 'id': 119, 'frequency': 'r', 'synset': 'bob.n.05'}, {'name': 'bobbin', 'id': 120, 'frequency': 'c', 'synset': 'bobbin.n.01'}, {'name': 'bobby_pin', 'id': 121, 'frequency': 'c', 'synset': 'bobby_pin.n.01'}, {'name': 'boiled_egg', 'id': 122, 'frequency': 'c', 'synset': 'boiled_egg.n.01'}, {'name': 'bolo_tie', 'id': 123, 'frequency': 'r', 'synset': 'bolo_tie.n.01'}, {'name': 'deadbolt', 'id': 124, 'frequency': 'c', 'synset': 'bolt.n.03'}, {'name': 'bolt', 'id': 125, 'frequency': 'f', 'synset': 'bolt.n.06'}, {'name': 'bonnet', 'id': 126, 'frequency': 'r', 'synset': 'bonnet.n.01'}, {'name': 'book', 'id': 127, 'frequency': 'f', 'synset': 'book.n.01'}, {'name': 'bookcase', 'id': 128, 'frequency': 'c', 'synset': 'bookcase.n.01'}, {'name': 'booklet', 'id': 129, 'frequency': 'c', 'synset': 'booklet.n.01'}, {'name': 'bookmark', 'id': 130, 'frequency': 'r', 'synset': 'bookmark.n.01'}, {'name': 'boom_microphone', 'id': 131, 'frequency': 'r', 'synset': 'boom.n.04'}, {'name': 'boot', 'id': 132, 'frequency': 'f', 'synset': 'boot.n.01'}, {'name': 'bottle', 'id': 133, 'frequency': 'f', 'synset': 'bottle.n.01'}, {'name': 'bottle_opener', 'id': 134, 'frequency': 'c', 'synset': 'bottle_opener.n.01'}, {'name': 'bouquet', 'id': 135, 'frequency': 'c', 'synset': 'bouquet.n.01'}, {'name': 'bow_(weapon)', 'id': 136, 'frequency': 'r', 'synset': 'bow.n.04'}, {'name': 'bow_(decorative_ribbons)', 'id': 137, 'frequency': 'f', 'synset': 'bow.n.08'}, {'name': 'bow-tie', 'id': 138, 'frequency': 'f', 'synset': 'bow_tie.n.01'}, {'name': 'bowl', 'id': 139, 'frequency': 'f', 'synset': 'bowl.n.03'}, {'name': 'pipe_bowl', 'id': 140, 'frequency': 'r', 'synset': 'bowl.n.08'}, {'name': 'bowler_hat', 'id': 141, 'frequency': 'c', 'synset': 'bowler_hat.n.01'}, {'name': 'bowling_ball', 'id': 142, 'frequency': 'r', 'synset': 'bowling_ball.n.01'}, {'name': 'box', 'id': 143, 'frequency': 'f', 'synset': 'box.n.01'}, {'name': 'boxing_glove', 'id': 144, 'frequency': 'r', 'synset': 'boxing_glove.n.01'}, {'name': 'suspenders', 'id': 145, 'frequency': 'c', 'synset': 'brace.n.06'}, {'name': 'bracelet', 'id': 146, 'frequency': 'f', 'synset': 'bracelet.n.02'}, {'name': 'brass_plaque', 'id': 147, 'frequency': 'r', 'synset': 'brass.n.07'}, {'name': 'brassiere', 'id': 148, 'frequency': 'c', 'synset': 'brassiere.n.01'}, {'name': 'bread-bin', 'id': 149, 'frequency': 'c', 'synset': 'bread-bin.n.01'}, {'name': 'bread', 'id': 150, 'frequency': 'f', 'synset': 'bread.n.01'}, {'name': 'breechcloth', 'id': 151, 'frequency': 'r', 'synset': 'breechcloth.n.01'}, {'name': 'bridal_gown', 'id': 152, 'frequency': 'f', 'synset': 'bridal_gown.n.01'}, {'name': 'briefcase', 'id': 153, 'frequency': 'c', 'synset': 'briefcase.n.01'}, {'name': 'broccoli', 'id': 154, 'frequency': 'f', 'synset': 'broccoli.n.01'}, {'name': 'broach', 'id': 155, 'frequency': 'r', 'synset': 'brooch.n.01'}, {'name': 'broom', 'id': 156, 'frequency': 'c', 'synset': 'broom.n.01'}, {'name': 'brownie', 'id': 157, 'frequency': 'c', 'synset': 'brownie.n.03'}, {'name': 'brussels_sprouts', 'id': 158, 'frequency': 'c', 'synset': 'brussels_sprouts.n.01'}, {'name': 'bubble_gum', 'id': 159, 'frequency': 'r', 'synset': 'bubble_gum.n.01'}, {'name': 'bucket', 'id': 160, 'frequency': 'f', 'synset': 'bucket.n.01'}, {'name': 'horse_buggy', 'id': 161, 'frequency': 'r', 'synset': 'buggy.n.01'}, {'name': 'bull', 'id': 162, 'frequency': 'c', 'synset': 'bull.n.11'}, {'name': 'bulldog', 'id': 163, 'frequency': 'c', 'synset': 'bulldog.n.01'}, {'name': 'bulldozer', 'id': 164, 'frequency': 'r', 'synset': 'bulldozer.n.01'}, {'name': 'bullet_train', 'id': 165, 'frequency': 'c', 'synset': 'bullet_train.n.01'}, {'name': 'bulletin_board', 'id': 166, 'frequency': 'c', 'synset': 'bulletin_board.n.02'}, {'name': 'bulletproof_vest', 'id': 167, 'frequency': 'r', 'synset': 'bulletproof_vest.n.01'}, {'name': 'bullhorn', 'id': 168, 'frequency': 'c', 'synset': 'bullhorn.n.01'}, {'name': 'bun', 'id': 169, 'frequency': 'f', 'synset': 'bun.n.01'}, {'name': 'bunk_bed', 'id': 170, 'frequency': 'c', 'synset': 'bunk_bed.n.01'}, {'name': 'buoy', 'id': 171, 'frequency': 'f', 'synset': 'buoy.n.01'}, {'name': 'burrito', 'id': 172, 'frequency': 'r', 'synset': 'burrito.n.01'}, {'name': 'bus_(vehicle)', 'id': 173, 'frequency': 'f', 'synset': 'bus.n.01'}, {'name': 'business_card', 'id': 174, 'frequency': 'c', 'synset': 'business_card.n.01'}, {'name': 'butter', 'id': 175, 'frequency': 'f', 'synset': 'butter.n.01'}, {'name': 'butterfly', 'id': 176, 'frequency': 'c', 'synset': 'butterfly.n.01'}, {'name': 'button', 'id': 177, 'frequency': 'f', 'synset': 'button.n.01'}, {'name': 'cab_(taxi)', 'id': 178, 'frequency': 'f', 'synset': 'cab.n.03'}, {'name': 'cabana', 'id': 179, 'frequency': 'r', 'synset': 'cabana.n.01'}, {'name': 'cabin_car', 'id': 180, 'frequency': 'c', 'synset': 'cabin_car.n.01'}, {'name': 'cabinet', 'id': 181, 'frequency': 'f', 'synset': 'cabinet.n.01'}, {'name': 'locker', 'id': 182, 'frequency': 'r', 'synset': 'cabinet.n.03'}, {'name': 'cake', 'id': 183, 'frequency': 'f', 'synset': 'cake.n.03'}, {'name': 'calculator', 'id': 184, 'frequency': 'c', 'synset': 'calculator.n.02'}, {'name': 'calendar', 'id': 185, 'frequency': 'f', 'synset': 'calendar.n.02'}, {'name': 'calf', 'id': 186, 'frequency': 'c', 'synset': 'calf.n.01'}, {'name': 'camcorder', 'id': 187, 'frequency': 'c', 'synset': 'camcorder.n.01'}, {'name': 'camel', 'id': 188, 'frequency': 'c', 'synset': 'camel.n.01'}, {'name': 'camera', 'id': 189, 'frequency': 'f', 'synset': 'camera.n.01'}, {'name': 'camera_lens', 'id': 190, 'frequency': 'c', 'synset': 'camera_lens.n.01'}, {'name': 'camper_(vehicle)', 'id': 191, 'frequency': 'c', 'synset': 'camper.n.02'}, {'name': 'can', 'id': 192, 'frequency': 'f', 'synset': 'can.n.01'}, {'name': 'can_opener', 'id': 193, 'frequency': 'c', 'synset': 'can_opener.n.01'}, {'name': 'candle', 'id': 194, 'frequency': 'f', 'synset': 'candle.n.01'}, {'name': 'candle_holder', 'id': 195, 'frequency': 'f', 'synset': 'candlestick.n.01'}, {'name': 'candy_bar', 'id': 196, 'frequency': 'r', 'synset': 'candy_bar.n.01'}, {'name': 'candy_cane', 'id': 197, 'frequency': 'c', 'synset': 'candy_cane.n.01'}, {'name': 'walking_cane', 'id': 198, 'frequency': 'c', 'synset': 'cane.n.01'}, {'name': 'canister', 'id': 199, 'frequency': 'c', 'synset': 'canister.n.02'}, {'name': 'canoe', 'id': 200, 'frequency': 'c', 'synset': 'canoe.n.01'}, {'name': 'cantaloup', 'id': 201, 'frequency': 'c', 'synset': 'cantaloup.n.02'}, {'name': 'canteen', 'id': 202, 'frequency': 'r', 'synset': 'canteen.n.01'}, {'name': 'cap_(headwear)', 'id': 203, 'frequency': 'f', 'synset': 'cap.n.01'}, {'name': 'bottle_cap', 'id': 204, 'frequency': 'f', 'synset': 'cap.n.02'}, {'name': 'cape', 'id': 205, 'frequency': 'c', 'synset': 'cape.n.02'}, {'name': 'cappuccino', 'id': 206, 'frequency': 'c', 'synset': 'cappuccino.n.01'}, {'name': 'car_(automobile)', 'id': 207, 'frequency': 'f', 'synset': 'car.n.01'}, {'name': 'railcar_(part_of_a_train)', 'id': 208, 'frequency': 'f', 'synset': 'car.n.02'}, {'name': 'elevator_car', 'id': 209, 'frequency': 'r', 'synset': 'car.n.04'}, {'name': 'car_battery', 'id': 210, 'frequency': 'r', 'synset': 'car_battery.n.01'}, {'name': 'identity_card', 'id': 211, 'frequency': 'c', 'synset': 'card.n.02'}, {'name': 'card', 'id': 212, 'frequency': 'c', 'synset': 'card.n.03'}, {'name': 'cardigan', 'id': 213, 'frequency': 'c', 'synset': 'cardigan.n.01'}, {'name': 'cargo_ship', 'id': 214, 'frequency': 'r', 'synset': 'cargo_ship.n.01'}, {'name': 'carnation', 'id': 215, 'frequency': 'r', 'synset': 'carnation.n.01'}, {'name': 'horse_carriage', 'id': 216, 'frequency': 'c', 'synset': 'carriage.n.02'}, {'name': 'carrot', 'id': 217, 'frequency': 'f', 'synset': 'carrot.n.01'}, {'name': 'tote_bag', 'id': 218, 'frequency': 'f', 'synset': 'carryall.n.01'}, {'name': 'cart', 'id': 219, 'frequency': 'c', 'synset': 'cart.n.01'}, {'name': 'carton', 'id': 220, 'frequency': 'c', 'synset': 'carton.n.02'}, {'name': 'cash_register', 'id': 221, 'frequency': 'c', 'synset': 'cash_register.n.01'}, {'name': 'casserole', 'id': 222, 'frequency': 'r', 'synset': 'casserole.n.01'}, {'name': 'cassette', 'id': 223, 'frequency': 'r', 'synset': 'cassette.n.01'}, {'name': 'cast', 'id': 224, 'frequency': 'c', 'synset': 'cast.n.05'}, {'name': 'cat', 'id': 225, 'frequency': 'f', 'synset': 'cat.n.01'}, {'name': 'cauliflower', 'id': 226, 'frequency': 'f', 'synset': 'cauliflower.n.02'}, {'name': 'cayenne_(spice)', 'id': 227, 'frequency': 'c', 'synset': 'cayenne.n.02'}, {'name': 'CD_player', 'id': 228, 'frequency': 'c', 'synset': 'cd_player.n.01'}, {'name': 'celery', 'id': 229, 'frequency': 'f', 'synset': 'celery.n.01'}, {'name': 'cellular_telephone', 'id': 230, 'frequency': 'f', 'synset': 'cellular_telephone.n.01'}, {'name': 'chain_mail', 'id': 231, 'frequency': 'r', 'synset': 'chain_mail.n.01'}, {'name': 'chair', 'id': 232, 'frequency': 'f', 'synset': 'chair.n.01'}, {'name': 'chaise_longue', 'id': 233, 'frequency': 'r', 'synset': 'chaise_longue.n.01'}, {'name': 'chalice', 'id': 234, 'frequency': 'r', 'synset': 'chalice.n.01'}, {'name': 'chandelier', 'id': 235, 'frequency': 'f', 'synset': 'chandelier.n.01'}, {'name': 'chap', 'id': 236, 'frequency': 'r', 'synset': 'chap.n.04'}, {'name': 'checkbook', 'id': 237, 'frequency': 'r', 'synset': 'checkbook.n.01'}, {'name': 'checkerboard', 'id': 238, 'frequency': 'r', 'synset': 'checkerboard.n.01'}, {'name': 'cherry', 'id': 239, 'frequency': 'c', 'synset': 'cherry.n.03'}, {'name': 'chessboard', 'id': 240, 'frequency': 'r', 'synset': 'chessboard.n.01'}, {'name': 'chicken_(animal)', 'id': 241, 'frequency': 'c', 'synset': 'chicken.n.02'}, {'name': 'chickpea', 'id': 242, 'frequency': 'c', 'synset': 'chickpea.n.01'}, {'name': 'chili_(vegetable)', 'id': 243, 'frequency': 'c', 'synset': 'chili.n.02'}, {'name': 'chime', 'id': 244, 'frequency': 'r', 'synset': 'chime.n.01'}, {'name': 'chinaware', 'id': 245, 'frequency': 'r', 'synset': 'chinaware.n.01'}, {'name': 'crisp_(potato_chip)', 'id': 246, 'frequency': 'c', 'synset': 'chip.n.04'}, {'name': 'poker_chip', 'id': 247, 'frequency': 'r', 'synset': 'chip.n.06'}, {'name': 'chocolate_bar', 'id': 248, 'frequency': 'c', 'synset': 'chocolate_bar.n.01'}, {'name': 'chocolate_cake', 'id': 249, 'frequency': 'c', 'synset': 'chocolate_cake.n.01'}, {'name': 'chocolate_milk', 'id': 250, 'frequency': 'r', 'synset': 'chocolate_milk.n.01'}, {'name': 'chocolate_mousse', 'id': 251, 'frequency': 'r', 'synset': 'chocolate_mousse.n.01'}, {'name': 'choker', 'id': 252, 'frequency': 'f', 'synset': 'choker.n.03'}, {'name': 'chopping_board', 'id': 253, 'frequency': 'f', 'synset': 'chopping_board.n.01'}, {'name': 'chopstick', 'id': 254, 'frequency': 'f', 'synset': 'chopstick.n.01'}, {'name': 'Christmas_tree', 'id': 255, 'frequency': 'f', 'synset': 'christmas_tree.n.05'}, {'name': 'slide', 'id': 256, 'frequency': 'c', 'synset': 'chute.n.02'}, {'name': 'cider', 'id': 257, 'frequency': 'r', 'synset': 'cider.n.01'}, {'name': 'cigar_box', 'id': 258, 'frequency': 'r', 'synset': 'cigar_box.n.01'}, {'name': 'cigarette', 'id': 259, 'frequency': 'f', 'synset': 'cigarette.n.01'}, {'name': 'cigarette_case', 'id': 260, 'frequency': 'c', 'synset': 'cigarette_case.n.01'}, {'name': 'cistern', 'id': 261, 'frequency': 'f', 'synset': 'cistern.n.02'}, {'name': 'clarinet', 'id': 262, 'frequency': 'r', 'synset': 'clarinet.n.01'}, {'name': 'clasp', 'id': 263, 'frequency': 'c', 'synset': 'clasp.n.01'}, {'name': 'cleansing_agent', 'id': 264, 'frequency': 'c', 'synset': 'cleansing_agent.n.01'}, {'name': 'cleat_(for_securing_rope)', 'id': 265, 'frequency': 'r', 'synset': 'cleat.n.02'}, {'name': 'clementine', 'id': 266, 'frequency': 'r', 'synset': 'clementine.n.01'}, {'name': 'clip', 'id': 267, 'frequency': 'c', 'synset': 'clip.n.03'}, {'name': 'clipboard', 'id': 268, 'frequency': 'c', 'synset': 'clipboard.n.01'}, {'name': 'clippers_(for_plants)', 'id': 269, 'frequency': 'r', 'synset': 'clipper.n.03'}, {'name': 'cloak', 'id': 270, 'frequency': 'r', 'synset': 'cloak.n.02'}, {'name': 'clock', 'id': 271, 'frequency': 'f', 'synset': 'clock.n.01'}, {'name': 'clock_tower', 'id': 272, 'frequency': 'f', 'synset': 'clock_tower.n.01'}, {'name': 'clothes_hamper', 'id': 273, 'frequency': 'c', 'synset': 'clothes_hamper.n.01'}, {'name': 'clothespin', 'id': 274, 'frequency': 'c', 'synset': 'clothespin.n.01'}, {'name': 'clutch_bag', 'id': 275, 'frequency': 'r', 'synset': 'clutch_bag.n.01'}, {'name': 'coaster', 'id': 276, 'frequency': 'f', 'synset': 'coaster.n.03'}, {'name': 'coat', 'id': 277, 'frequency': 'f', 'synset': 'coat.n.01'}, {'name': 'coat_hanger', 'id': 278, 'frequency': 'c', 'synset': 'coat_hanger.n.01'}, {'name': 'coatrack', 'id': 279, 'frequency': 'c', 'synset': 'coatrack.n.01'}, {'name': 'cock', 'id': 280, 'frequency': 'c', 'synset': 'cock.n.04'}, {'name': 'cockroach', 'id': 281, 'frequency': 'r', 'synset': 'cockroach.n.01'}, {'name': 'cocoa_(beverage)', 'id': 282, 'frequency': 'r', 'synset': 'cocoa.n.01'}, {'name': 'coconut', 'id': 283, 'frequency': 'c', 'synset': 'coconut.n.02'}, {'name': 'coffee_maker', 'id': 284, 'frequency': 'f', 'synset': 'coffee_maker.n.01'}, {'name': 'coffee_table', 'id': 285, 'frequency': 'f', 'synset': 'coffee_table.n.01'}, {'name': 'coffeepot', 'id': 286, 'frequency': 'c', 'synset': 'coffeepot.n.01'}, {'name': 'coil', 'id': 287, 'frequency': 'r', 'synset': 'coil.n.05'}, {'name': 'coin', 'id': 288, 'frequency': 'c', 'synset': 'coin.n.01'}, {'name': 'colander', 'id': 289, 'frequency': 'c', 'synset': 'colander.n.01'}, {'name': 'coleslaw', 'id': 290, 'frequency': 'c', 'synset': 'coleslaw.n.01'}, {'name': 'coloring_material', 'id': 291, 'frequency': 'r', 'synset': 'coloring_material.n.01'}, {'name': 'combination_lock', 'id': 292, 'frequency': 'r', 'synset': 'combination_lock.n.01'}, {'name': 'pacifier', 'id': 293, 'frequency': 'c', 'synset': 'comforter.n.04'}, {'name': 'comic_book', 'id': 294, 'frequency': 'r', 'synset': 'comic_book.n.01'}, {'name': 'compass', 'id': 295, 'frequency': 'r', 'synset': 'compass.n.01'}, {'name': 'computer_keyboard', 'id': 296, 'frequency': 'f', 'synset': 'computer_keyboard.n.01'}, {'name': 'condiment', 'id': 297, 'frequency': 'f', 'synset': 'condiment.n.01'}, {'name': 'cone', 'id': 298, 'frequency': 'f', 'synset': 'cone.n.01'}, {'name': 'control', 'id': 299, 'frequency': 'f', 'synset': 'control.n.09'}, {'name': 'convertible_(automobile)', 'id': 300, 'frequency': 'r', 'synset': 'convertible.n.01'}, {'name': 'sofa_bed', 'id': 301, 'frequency': 'r', 'synset': 'convertible.n.03'}, {'name': 'cooker', 'id': 302, 'frequency': 'r', 'synset': 'cooker.n.01'}, {'name': 'cookie', 'id': 303, 'frequency': 'f', 'synset': 'cookie.n.01'}, {'name': 'cooking_utensil', 'id': 304, 'frequency': 'r', 'synset': 'cooking_utensil.n.01'}, {'name': 'cooler_(for_food)', 'id': 305, 'frequency': 'f', 'synset': 'cooler.n.01'}, {'name': 'cork_(bottle_plug)', 'id': 306, 'frequency': 'f', 'synset': 'cork.n.04'}, {'name': 'corkboard', 'id': 307, 'frequency': 'r', 'synset': 'corkboard.n.01'}, {'name': 'corkscrew', 'id': 308, 'frequency': 'c', 'synset': 'corkscrew.n.01'}, {'name': 'edible_corn', 'id': 309, 'frequency': 'f', 'synset': 'corn.n.03'}, {'name': 'cornbread', 'id': 310, 'frequency': 'r', 'synset': 'cornbread.n.01'}, {'name': 'cornet', 'id': 311, 'frequency': 'c', 'synset': 'cornet.n.01'}, {'name': 'cornice', 'id': 312, 'frequency': 'c', 'synset': 'cornice.n.01'}, {'name': 'cornmeal', 'id': 313, 'frequency': 'r', 'synset': 'cornmeal.n.01'}, {'name': 'corset', 'id': 314, 'frequency': 'c', 'synset': 'corset.n.01'}, {'name': 'costume', 'id': 315, 'frequency': 'c', 'synset': 'costume.n.04'}, {'name': 'cougar', 'id': 316, 'frequency': 'r', 'synset': 'cougar.n.01'}, {'name': 'coverall', 'id': 317, 'frequency': 'r', 'synset': 'coverall.n.01'}, {'name': 'cowbell', 'id': 318, 'frequency': 'c', 'synset': 'cowbell.n.01'}, {'name': 'cowboy_hat', 'id': 319, 'frequency': 'f', 'synset': 'cowboy_hat.n.01'}, {'name': 'crab_(animal)', 'id': 320, 'frequency': 'c', 'synset': 'crab.n.01'}, {'name': 'crabmeat', 'id': 321, 'frequency': 'r', 'synset': 'crab.n.05'}, {'name': 'cracker', 'id': 322, 'frequency': 'c', 'synset': 'cracker.n.01'}, {'name': 'crape', 'id': 323, 'frequency': 'r', 'synset': 'crape.n.01'}, {'name': 'crate', 'id': 324, 'frequency': 'f', 'synset': 'crate.n.01'}, {'name': 'crayon', 'id': 325, 'frequency': 'c', 'synset': 'crayon.n.01'}, {'name': 'cream_pitcher', 'id': 326, 'frequency': 'r', 'synset': 'cream_pitcher.n.01'}, {'name': 'crescent_roll', 'id': 327, 'frequency': 'c', 'synset': 'crescent_roll.n.01'}, {'name': 'crib', 'id': 328, 'frequency': 'c', 'synset': 'crib.n.01'}, {'name': 'crock_pot', 'id': 329, 'frequency': 'c', 'synset': 'crock.n.03'}, {'name': 'crossbar', 'id': 330, 'frequency': 'f', 'synset': 'crossbar.n.01'}, {'name': 'crouton', 'id': 331, 'frequency': 'r', 'synset': 'crouton.n.01'}, {'name': 'crow', 'id': 332, 'frequency': 'c', 'synset': 'crow.n.01'}, {'name': 'crowbar', 'id': 333, 'frequency': 'r', 'synset': 'crowbar.n.01'}, {'name': 'crown', 'id': 334, 'frequency': 'c', 'synset': 'crown.n.04'}, {'name': 'crucifix', 'id': 335, 'frequency': 'c', 'synset': 'crucifix.n.01'}, {'name': 'cruise_ship', 'id': 336, 'frequency': 'c', 'synset': 'cruise_ship.n.01'}, {'name': 'police_cruiser', 'id': 337, 'frequency': 'c', 'synset': 'cruiser.n.01'}, {'name': 'crumb', 'id': 338, 'frequency': 'f', 'synset': 'crumb.n.03'}, {'name': 'crutch', 'id': 339, 'frequency': 'c', 'synset': 'crutch.n.01'}, {'name': 'cub_(animal)', 'id': 340, 'frequency': 'c', 'synset': 'cub.n.03'}, {'name': 'cube', 'id': 341, 'frequency': 'c', 'synset': 'cube.n.05'}, {'name': 'cucumber', 'id': 342, 'frequency': 'f', 'synset': 'cucumber.n.02'}, {'name': 'cufflink', 'id': 343, 'frequency': 'c', 'synset': 'cufflink.n.01'}, {'name': 'cup', 'id': 344, 'frequency': 'f', 'synset': 'cup.n.01'}, {'name': 'trophy_cup', 'id': 345, 'frequency': 'c', 'synset': 'cup.n.08'}, {'name': 'cupboard', 'id': 346, 'frequency': 'f', 'synset': 'cupboard.n.01'}, {'name': 'cupcake', 'id': 347, 'frequency': 'f', 'synset': 'cupcake.n.01'}, {'name': 'hair_curler', 'id': 348, 'frequency': 'r', 'synset': 'curler.n.01'}, {'name': 'curling_iron', 'id': 349, 'frequency': 'r', 'synset': 'curling_iron.n.01'}, {'name': 'curtain', 'id': 350, 'frequency': 'f', 'synset': 'curtain.n.01'}, {'name': 'cushion', 'id': 351, 'frequency': 'f', 'synset': 'cushion.n.03'}, {'name': 'cylinder', 'id': 352, 'frequency': 'r', 'synset': 'cylinder.n.04'}, {'name': 'cymbal', 'id': 353, 'frequency': 'r', 'synset': 'cymbal.n.01'}, {'name': 'dagger', 'id': 354, 'frequency': 'r', 'synset': 'dagger.n.01'}, {'name': 'dalmatian', 'id': 355, 'frequency': 'r', 'synset': 'dalmatian.n.02'}, {'name': 'dartboard', 'id': 356, 'frequency': 'c', 'synset': 'dartboard.n.01'}, {'name': 'date_(fruit)', 'id': 357, 'frequency': 'r', 'synset': 'date.n.08'}, {'name': 'deck_chair', 'id': 358, 'frequency': 'f', 'synset': 'deck_chair.n.01'}, {'name': 'deer', 'id': 359, 'frequency': 'c', 'synset': 'deer.n.01'}, {'name': 'dental_floss', 'id': 360, 'frequency': 'c', 'synset': 'dental_floss.n.01'}, {'name': 'desk', 'id': 361, 'frequency': 'f', 'synset': 'desk.n.01'}, {'name': 'detergent', 'id': 362, 'frequency': 'r', 'synset': 'detergent.n.01'}, {'name': 'diaper', 'id': 363, 'frequency': 'c', 'synset': 'diaper.n.01'}, {'name': 'diary', 'id': 364, 'frequency': 'r', 'synset': 'diary.n.01'}, {'name': 'die', 'id': 365, 'frequency': 'r', 'synset': 'die.n.01'}, {'name': 'dinghy', 'id': 366, 'frequency': 'r', 'synset': 'dinghy.n.01'}, {'name': 'dining_table', 'id': 367, 'frequency': 'f', 'synset': 'dining_table.n.01'}, {'name': 'tux', 'id': 368, 'frequency': 'r', 'synset': 'dinner_jacket.n.01'}, {'name': 'dish', 'id': 369, 'frequency': 'f', 'synset': 'dish.n.01'}, {'name': 'dish_antenna', 'id': 370, 'frequency': 'c', 'synset': 'dish.n.05'}, {'name': 'dishrag', 'id': 371, 'frequency': 'c', 'synset': 'dishrag.n.01'}, {'name': 'dishtowel', 'id': 372, 'frequency': 'f', 'synset': 'dishtowel.n.01'}, {'name': 'dishwasher', 'id': 373, 'frequency': 'f', 'synset': 'dishwasher.n.01'}, {'name': 'dishwasher_detergent', 'id': 374, 'frequency': 'r', 'synset': 'dishwasher_detergent.n.01'}, {'name': 'dispenser', 'id': 375, 'frequency': 'f', 'synset': 'dispenser.n.01'}, {'name': 'diving_board', 'id': 376, 'frequency': 'r', 'synset': 'diving_board.n.01'}, {'name': 'Dixie_cup', 'id': 377, 'frequency': 'f', 'synset': 'dixie_cup.n.01'}, {'name': 'dog', 'id': 378, 'frequency': 'f', 'synset': 'dog.n.01'}, {'name': 'dog_collar', 'id': 379, 'frequency': 'f', 'synset': 'dog_collar.n.01'}, {'name': 'doll', 'id': 380, 'frequency': 'f', 'synset': 'doll.n.01'}, {'name': 'dollar', 'id': 381, 'frequency': 'r', 'synset': 'dollar.n.02'}, {'name': 'dollhouse', 'id': 382, 'frequency': 'r', 'synset': 'dollhouse.n.01'}, {'name': 'dolphin', 'id': 383, 'frequency': 'c', 'synset': 'dolphin.n.02'}, {'name': 'domestic_ass', 'id': 384, 'frequency': 'c', 'synset': 'domestic_ass.n.01'}, {'name': 'doorknob', 'id': 385, 'frequency': 'f', 'synset': 'doorknob.n.01'}, {'name': 'doormat', 'id': 386, 'frequency': 'c', 'synset': 'doormat.n.02'}, {'name': 'doughnut', 'id': 387, 'frequency': 'f', 'synset': 'doughnut.n.02'}, {'name': 'dove', 'id': 388, 'frequency': 'r', 'synset': 'dove.n.01'}, {'name': 'dragonfly', 'id': 389, 'frequency': 'r', 'synset': 'dragonfly.n.01'}, {'name': 'drawer', 'id': 390, 'frequency': 'f', 'synset': 'drawer.n.01'}, {'name': 'underdrawers', 'id': 391, 'frequency': 'c', 'synset': 'drawers.n.01'}, {'name': 'dress', 'id': 392, 'frequency': 'f', 'synset': 'dress.n.01'}, {'name': 'dress_hat', 'id': 393, 'frequency': 'c', 'synset': 'dress_hat.n.01'}, {'name': 'dress_suit', 'id': 394, 'frequency': 'f', 'synset': 'dress_suit.n.01'}, {'name': 'dresser', 'id': 395, 'frequency': 'f', 'synset': 'dresser.n.05'}, {'name': 'drill', 'id': 396, 'frequency': 'c', 'synset': 'drill.n.01'}, {'name': 'drone', 'id': 397, 'frequency': 'r', 'synset': 'drone.n.04'}, {'name': 'dropper', 'id': 398, 'frequency': 'r', 'synset': 'dropper.n.01'}, {'name': 'drum_(musical_instrument)', 'id': 399, 'frequency': 'c', 'synset': 'drum.n.01'}, {'name': 'drumstick', 'id': 400, 'frequency': 'r', 'synset': 'drumstick.n.02'}, {'name': 'duck', 'id': 401, 'frequency': 'f', 'synset': 'duck.n.01'}, {'name': 'duckling', 'id': 402, 'frequency': 'c', 'synset': 'duckling.n.02'}, {'name': 'duct_tape', 'id': 403, 'frequency': 'c', 'synset': 'duct_tape.n.01'}, {'name': 'duffel_bag', 'id': 404, 'frequency': 'f', 'synset': 'duffel_bag.n.01'}, {'name': 'dumbbell', 'id': 405, 'frequency': 'r', 'synset': 'dumbbell.n.01'}, {'name': 'dumpster', 'id': 406, 'frequency': 'c', 'synset': 'dumpster.n.01'}, {'name': 'dustpan', 'id': 407, 'frequency': 'r', 'synset': 'dustpan.n.02'}, {'name': 'eagle', 'id': 408, 'frequency': 'c', 'synset': 'eagle.n.01'}, {'name': 'earphone', 'id': 409, 'frequency': 'f', 'synset': 'earphone.n.01'}, {'name': 'earplug', 'id': 410, 'frequency': 'r', 'synset': 'earplug.n.01'}, {'name': 'earring', 'id': 411, 'frequency': 'f', 'synset': 'earring.n.01'}, {'name': 'easel', 'id': 412, 'frequency': 'c', 'synset': 'easel.n.01'}, {'name': 'eclair', 'id': 413, 'frequency': 'r', 'synset': 'eclair.n.01'}, {'name': 'eel', 'id': 414, 'frequency': 'r', 'synset': 'eel.n.01'}, {'name': 'egg', 'id': 415, 'frequency': 'f', 'synset': 'egg.n.02'}, {'name': 'egg_roll', 'id': 416, 'frequency': 'r', 'synset': 'egg_roll.n.01'}, {'name': 'egg_yolk', 'id': 417, 'frequency': 'c', 'synset': 'egg_yolk.n.01'}, {'name': 'eggbeater', 'id': 418, 'frequency': 'c', 'synset': 'eggbeater.n.02'}, {'name': 'eggplant', 'id': 419, 'frequency': 'c', 'synset': 'eggplant.n.01'}, {'name': 'electric_chair', 'id': 420, 'frequency': 'r', 'synset': 'electric_chair.n.01'}, {'name': 'refrigerator', 'id': 421, 'frequency': 'f', 'synset': 'electric_refrigerator.n.01'}, {'name': 'elephant', 'id': 422, 'frequency': 'f', 'synset': 'elephant.n.01'}, {'name': 'elk', 'id': 423, 'frequency': 'c', 'synset': 'elk.n.01'}, {'name': 'envelope', 'id': 424, 'frequency': 'c', 'synset': 'envelope.n.01'}, {'name': 'eraser', 'id': 425, 'frequency': 'c', 'synset': 'eraser.n.01'}, {'name': 'escargot', 'id': 426, 'frequency': 'r', 'synset': 'escargot.n.01'}, {'name': 'eyepatch', 'id': 427, 'frequency': 'r', 'synset': 'eyepatch.n.01'}, {'name': 'falcon', 'id': 428, 'frequency': 'r', 'synset': 'falcon.n.01'}, {'name': 'fan', 'id': 429, 'frequency': 'f', 'synset': 'fan.n.01'}, {'name': 'faucet', 'id': 430, 'frequency': 'f', 'synset': 'faucet.n.01'}, {'name': 'fedora', 'id': 431, 'frequency': 'r', 'synset': 'fedora.n.01'}, {'name': 'ferret', 'id': 432, 'frequency': 'r', 'synset': 'ferret.n.02'}, {'name': 'Ferris_wheel', 'id': 433, 'frequency': 'c', 'synset': 'ferris_wheel.n.01'}, {'name': 'ferry', 'id': 434, 'frequency': 'c', 'synset': 'ferry.n.01'}, {'name': 'fig_(fruit)', 'id': 435, 'frequency': 'r', 'synset': 'fig.n.04'}, {'name': 'fighter_jet', 'id': 436, 'frequency': 'c', 'synset': 'fighter.n.02'}, {'name': 'figurine', 'id': 437, 'frequency': 'f', 'synset': 'figurine.n.01'}, {'name': 'file_cabinet', 'id': 438, 'frequency': 'c', 'synset': 'file.n.03'}, {'name': 'file_(tool)', 'id': 439, 'frequency': 'r', 'synset': 'file.n.04'}, {'name': 'fire_alarm', 'id': 440, 'frequency': 'f', 'synset': 'fire_alarm.n.02'}, {'name': 'fire_engine', 'id': 441, 'frequency': 'f', 'synset': 'fire_engine.n.01'}, {'name': 'fire_extinguisher', 'id': 442, 'frequency': 'f', 'synset': 'fire_extinguisher.n.01'}, {'name': 'fire_hose', 'id': 443, 'frequency': 'c', 'synset': 'fire_hose.n.01'}, {'name': 'fireplace', 'id': 444, 'frequency': 'f', 'synset': 'fireplace.n.01'}, {'name': 'fireplug', 'id': 445, 'frequency': 'f', 'synset': 'fireplug.n.01'}, {'name': 'first-aid_kit', 'id': 446, 'frequency': 'r', 'synset': 'first-aid_kit.n.01'}, {'name': 'fish', 'id': 447, 'frequency': 'f', 'synset': 'fish.n.01'}, {'name': 'fish_(food)', 'id': 448, 'frequency': 'c', 'synset': 'fish.n.02'}, {'name': 'fishbowl', 'id': 449, 'frequency': 'r', 'synset': 'fishbowl.n.02'}, {'name': 'fishing_rod', 'id': 450, 'frequency': 'c', 'synset': 'fishing_rod.n.01'}, {'name': 'flag', 'id': 451, 'frequency': 'f', 'synset': 'flag.n.01'}, {'name': 'flagpole', 'id': 452, 'frequency': 'f', 'synset': 'flagpole.n.02'}, {'name': 'flamingo', 'id': 453, 'frequency': 'c', 'synset': 'flamingo.n.01'}, {'name': 'flannel', 'id': 454, 'frequency': 'c', 'synset': 'flannel.n.01'}, {'name': 'flap', 'id': 455, 'frequency': 'c', 'synset': 'flap.n.01'}, {'name': 'flash', 'id': 456, 'frequency': 'r', 'synset': 'flash.n.10'}, {'name': 'flashlight', 'id': 457, 'frequency': 'c', 'synset': 'flashlight.n.01'}, {'name': 'fleece', 'id': 458, 'frequency': 'r', 'synset': 'fleece.n.03'}, {'name': 'flip-flop_(sandal)', 'id': 459, 'frequency': 'f', 'synset': 'flip-flop.n.02'}, {'name': 'flipper_(footwear)', 'id': 460, 'frequency': 'c', 'synset': 'flipper.n.01'}, {'name': 'flower_arrangement', 'id': 461, 'frequency': 'f', 'synset': 'flower_arrangement.n.01'}, {'name': 'flute_glass', 'id': 462, 'frequency': 'c', 'synset': 'flute.n.02'}, {'name': 'foal', 'id': 463, 'frequency': 'c', 'synset': 'foal.n.01'}, {'name': 'folding_chair', 'id': 464, 'frequency': 'c', 'synset': 'folding_chair.n.01'}, {'name': 'food_processor', 'id': 465, 'frequency': 'c', 'synset': 'food_processor.n.01'}, {'name': 'football_(American)', 'id': 466, 'frequency': 'c', 'synset': 'football.n.02'}, {'name': 'football_helmet', 'id': 467, 'frequency': 'r', 'synset': 'football_helmet.n.01'}, {'name': 'footstool', 'id': 468, 'frequency': 'c', 'synset': 'footstool.n.01'}, {'name': 'fork', 'id': 469, 'frequency': 'f', 'synset': 'fork.n.01'}, {'name': 'forklift', 'id': 470, 'frequency': 'c', 'synset': 'forklift.n.01'}, {'name': 'freight_car', 'id': 471, 'frequency': 'c', 'synset': 'freight_car.n.01'}, {'name': 'French_toast', 'id': 472, 'frequency': 'c', 'synset': 'french_toast.n.01'}, {'name': 'freshener', 'id': 473, 'frequency': 'c', 'synset': 'freshener.n.01'}, {'name': 'frisbee', 'id': 474, 'frequency': 'f', 'synset': 'frisbee.n.01'}, {'name': 'frog', 'id': 475, 'frequency': 'c', 'synset': 'frog.n.01'}, {'name': 'fruit_juice', 'id': 476, 'frequency': 'c', 'synset': 'fruit_juice.n.01'}, {'name': 'frying_pan', 'id': 477, 'frequency': 'f', 'synset': 'frying_pan.n.01'}, {'name': 'fudge', 'id': 478, 'frequency': 'r', 'synset': 'fudge.n.01'}, {'name': 'funnel', 'id': 479, 'frequency': 'r', 'synset': 'funnel.n.02'}, {'name': 'futon', 'id': 480, 'frequency': 'r', 'synset': 'futon.n.01'}, {'name': 'gag', 'id': 481, 'frequency': 'r', 'synset': 'gag.n.02'}, {'name': 'garbage', 'id': 482, 'frequency': 'r', 'synset': 'garbage.n.03'}, {'name': 'garbage_truck', 'id': 483, 'frequency': 'c', 'synset': 'garbage_truck.n.01'}, {'name': 'garden_hose', 'id': 484, 'frequency': 'c', 'synset': 'garden_hose.n.01'}, {'name': 'gargle', 'id': 485, 'frequency': 'c', 'synset': 'gargle.n.01'}, {'name': 'gargoyle', 'id': 486, 'frequency': 'r', 'synset': 'gargoyle.n.02'}, {'name': 'garlic', 'id': 487, 'frequency': 'c', 'synset': 'garlic.n.02'}, {'name': 'gasmask', 'id': 488, 'frequency': 'r', 'synset': 'gasmask.n.01'}, {'name': 'gazelle', 'id': 489, 'frequency': 'c', 'synset': 'gazelle.n.01'}, {'name': 'gelatin', 'id': 490, 'frequency': 'c', 'synset': 'gelatin.n.02'}, {'name': 'gemstone', 'id': 491, 'frequency': 'r', 'synset': 'gem.n.02'}, {'name': 'generator', 'id': 492, 'frequency': 'r', 'synset': 'generator.n.02'}, {'name': 'giant_panda', 'id': 493, 'frequency': 'c', 'synset': 'giant_panda.n.01'}, {'name': 'gift_wrap', 'id': 494, 'frequency': 'c', 'synset': 'gift_wrap.n.01'}, {'name': 'ginger', 'id': 495, 'frequency': 'c', 'synset': 'ginger.n.03'}, {'name': 'giraffe', 'id': 496, 'frequency': 'f', 'synset': 'giraffe.n.01'}, {'name': 'cincture', 'id': 497, 'frequency': 'c', 'synset': 'girdle.n.02'}, {'name': 'glass_(drink_container)', 'id': 498, 'frequency': 'f', 'synset': 'glass.n.02'}, {'name': 'globe', 'id': 499, 'frequency': 'c', 'synset': 'globe.n.03'}, {'name': 'glove', 'id': 500, 'frequency': 'f', 'synset': 'glove.n.02'}, {'name': 'goat', 'id': 501, 'frequency': 'c', 'synset': 'goat.n.01'}, {'name': 'goggles', 'id': 502, 'frequency': 'f', 'synset': 'goggles.n.01'}, {'name': 'goldfish', 'id': 503, 'frequency': 'r', 'synset': 'goldfish.n.01'}, {'name': 'golf_club', 'id': 504, 'frequency': 'c', 'synset': 'golf_club.n.02'}, {'name': 'golfcart', 'id': 505, 'frequency': 'c', 'synset': 'golfcart.n.01'}, {'name': 'gondola_(boat)', 'id': 506, 'frequency': 'r', 'synset': 'gondola.n.02'}, {'name': 'goose', 'id': 507, 'frequency': 'c', 'synset': 'goose.n.01'}, {'name': 'gorilla', 'id': 508, 'frequency': 'r', 'synset': 'gorilla.n.01'}, {'name': 'gourd', 'id': 509, 'frequency': 'r', 'synset': 'gourd.n.02'}, {'name': 'grape', 'id': 510, 'frequency': 'f', 'synset': 'grape.n.01'}, {'name': 'grater', 'id': 511, 'frequency': 'c', 'synset': 'grater.n.01'}, {'name': 'gravestone', 'id': 512, 'frequency': 'c', 'synset': 'gravestone.n.01'}, {'name': 'gravy_boat', 'id': 513, 'frequency': 'r', 'synset': 'gravy_boat.n.01'}, {'name': 'green_bean', 'id': 514, 'frequency': 'f', 'synset': 'green_bean.n.02'}, {'name': 'green_onion', 'id': 515, 'frequency': 'f', 'synset': 'green_onion.n.01'}, {'name': 'griddle', 'id': 516, 'frequency': 'r', 'synset': 'griddle.n.01'}, {'name': 'grill', 'id': 517, 'frequency': 'f', 'synset': 'grill.n.02'}, {'name': 'grits', 'id': 518, 'frequency': 'r', 'synset': 'grits.n.01'}, {'name': 'grizzly', 'id': 519, 'frequency': 'c', 'synset': 'grizzly.n.01'}, {'name': 'grocery_bag', 'id': 520, 'frequency': 'c', 'synset': 'grocery_bag.n.01'}, {'name': 'guitar', 'id': 521, 'frequency': 'f', 'synset': 'guitar.n.01'}, {'name': 'gull', 'id': 522, 'frequency': 'c', 'synset': 'gull.n.02'}, {'name': 'gun', 'id': 523, 'frequency': 'c', 'synset': 'gun.n.01'}, {'name': 'hairbrush', 'id': 524, 'frequency': 'f', 'synset': 'hairbrush.n.01'}, {'name': 'hairnet', 'id': 525, 'frequency': 'c', 'synset': 'hairnet.n.01'}, {'name': 'hairpin', 'id': 526, 'frequency': 'c', 'synset': 'hairpin.n.01'}, {'name': 'halter_top', 'id': 527, 'frequency': 'r', 'synset': 'halter.n.03'}, {'name': 'ham', 'id': 528, 'frequency': 'f', 'synset': 'ham.n.01'}, {'name': 'hamburger', 'id': 529, 'frequency': 'c', 'synset': 'hamburger.n.01'}, {'name': 'hammer', 'id': 530, 'frequency': 'c', 'synset': 'hammer.n.02'}, {'name': 'hammock', 'id': 531, 'frequency': 'c', 'synset': 'hammock.n.02'}, {'name': 'hamper', 'id': 532, 'frequency': 'r', 'synset': 'hamper.n.02'}, {'name': 'hamster', 'id': 533, 'frequency': 'c', 'synset': 'hamster.n.01'}, {'name': 'hair_dryer', 'id': 534, 'frequency': 'f', 'synset': 'hand_blower.n.01'}, {'name': 'hand_glass', 'id': 535, 'frequency': 'r', 'synset': 'hand_glass.n.01'}, {'name': 'hand_towel', 'id': 536, 'frequency': 'f', 'synset': 'hand_towel.n.01'}, {'name': 'handcart', 'id': 537, 'frequency': 'c', 'synset': 'handcart.n.01'}, {'name': 'handcuff', 'id': 538, 'frequency': 'r', 'synset': 'handcuff.n.01'}, {'name': 'handkerchief', 'id': 539, 'frequency': 'c', 'synset': 'handkerchief.n.01'}, {'name': 'handle', 'id': 540, 'frequency': 'f', 'synset': 'handle.n.01'}, {'name': 'handsaw', 'id': 541, 'frequency': 'r', 'synset': 'handsaw.n.01'}, {'name': 'hardback_book', 'id': 542, 'frequency': 'r', 'synset': 'hardback.n.01'}, {'name': 'harmonium', 'id': 543, 'frequency': 'r', 'synset': 'harmonium.n.01'}, {'name': 'hat', 'id': 544, 'frequency': 'f', 'synset': 'hat.n.01'}, {'name': 'hatbox', 'id': 545, 'frequency': 'r', 'synset': 'hatbox.n.01'}, {'name': 'veil', 'id': 546, 'frequency': 'c', 'synset': 'head_covering.n.01'}, {'name': 'headband', 'id': 547, 'frequency': 'f', 'synset': 'headband.n.01'}, {'name': 'headboard', 'id': 548, 'frequency': 'f', 'synset': 'headboard.n.01'}, {'name': 'headlight', 'id': 549, 'frequency': 'f', 'synset': 'headlight.n.01'}, {'name': 'headscarf', 'id': 550, 'frequency': 'c', 'synset': 'headscarf.n.01'}, {'name': 'headset', 'id': 551, 'frequency': 'r', 'synset': 'headset.n.01'}, {'name': 'headstall_(for_horses)', 'id': 552, 'frequency': 'c', 'synset': 'headstall.n.01'}, {'name': 'heart', 'id': 553, 'frequency': 'c', 'synset': 'heart.n.02'}, {'name': 'heater', 'id': 554, 'frequency': 'c', 'synset': 'heater.n.01'}, {'name': 'helicopter', 'id': 555, 'frequency': 'c', 'synset': 'helicopter.n.01'}, {'name': 'helmet', 'id': 556, 'frequency': 'f', 'synset': 'helmet.n.02'}, {'name': 'heron', 'id': 557, 'frequency': 'r', 'synset': 'heron.n.02'}, {'name': 'highchair', 'id': 558, 'frequency': 'c', 'synset': 'highchair.n.01'}, {'name': 'hinge', 'id': 559, 'frequency': 'f', 'synset': 'hinge.n.01'}, {'name': 'hippopotamus', 'id': 560, 'frequency': 'r', 'synset': 'hippopotamus.n.01'}, {'name': 'hockey_stick', 'id': 561, 'frequency': 'r', 'synset': 'hockey_stick.n.01'}, {'name': 'hog', 'id': 562, 'frequency': 'c', 'synset': 'hog.n.03'}, {'name': 'home_plate_(baseball)', 'id': 563, 'frequency': 'f', 'synset': 'home_plate.n.01'}, {'name': 'honey', 'id': 564, 'frequency': 'c', 'synset': 'honey.n.01'}, {'name': 'fume_hood', 'id': 565, 'frequency': 'f', 'synset': 'hood.n.06'}, {'name': 'hook', 'id': 566, 'frequency': 'f', 'synset': 'hook.n.05'}, {'name': 'hookah', 'id': 567, 'frequency': 'r', 'synset': 'hookah.n.01'}, {'name': 'hornet', 'id': 568, 'frequency': 'r', 'synset': 'hornet.n.01'}, {'name': 'horse', 'id': 569, 'frequency': 'f', 'synset': 'horse.n.01'}, {'name': 'hose', 'id': 570, 'frequency': 'f', 'synset': 'hose.n.03'}, {'name': 'hot-air_balloon', 'id': 571, 'frequency': 'r', 'synset': 'hot-air_balloon.n.01'}, {'name': 'hotplate', 'id': 572, 'frequency': 'r', 'synset': 'hot_plate.n.01'}, {'name': 'hot_sauce', 'id': 573, 'frequency': 'c', 'synset': 'hot_sauce.n.01'}, {'name': 'hourglass', 'id': 574, 'frequency': 'r', 'synset': 'hourglass.n.01'}, {'name': 'houseboat', 'id': 575, 'frequency': 'r', 'synset': 'houseboat.n.01'}, {'name': 'hummingbird', 'id': 576, 'frequency': 'c', 'synset': 'hummingbird.n.01'}, {'name': 'hummus', 'id': 577, 'frequency': 'r', 'synset': 'hummus.n.01'}, {'name': 'polar_bear', 'id': 578, 'frequency': 'f', 'synset': 'ice_bear.n.01'}, {'name': 'icecream', 'id': 579, 'frequency': 'c', 'synset': 'ice_cream.n.01'}, {'name': 'popsicle', 'id': 580, 'frequency': 'r', 'synset': 'ice_lolly.n.01'}, {'name': 'ice_maker', 'id': 581, 'frequency': 'c', 'synset': 'ice_maker.n.01'}, {'name': 'ice_pack', 'id': 582, 'frequency': 'r', 'synset': 'ice_pack.n.01'}, {'name': 'ice_skate', 'id': 583, 'frequency': 'r', 'synset': 'ice_skate.n.01'}, {'name': 'igniter', 'id': 584, 'frequency': 'c', 'synset': 'igniter.n.01'}, {'name': 'inhaler', 'id': 585, 'frequency': 'r', 'synset': 'inhaler.n.01'}, {'name': 'iPod', 'id': 586, 'frequency': 'f', 'synset': 'ipod.n.01'}, {'name': 'iron_(for_clothing)', 'id': 587, 'frequency': 'c', 'synset': 'iron.n.04'}, {'name': 'ironing_board', 'id': 588, 'frequency': 'c', 'synset': 'ironing_board.n.01'}, {'name': 'jacket', 'id': 589, 'frequency': 'f', 'synset': 'jacket.n.01'}, {'name': 'jam', 'id': 590, 'frequency': 'c', 'synset': 'jam.n.01'}, {'name': 'jar', 'id': 591, 'frequency': 'f', 'synset': 'jar.n.01'}, {'name': 'jean', 'id': 592, 'frequency': 'f', 'synset': 'jean.n.01'}, {'name': 'jeep', 'id': 593, 'frequency': 'c', 'synset': 'jeep.n.01'}, {'name': 'jelly_bean', 'id': 594, 'frequency': 'r', 'synset': 'jelly_bean.n.01'}, {'name': 'jersey', 'id': 595, 'frequency': 'f', 'synset': 'jersey.n.03'}, {'name': 'jet_plane', 'id': 596, 'frequency': 'c', 'synset': 'jet.n.01'}, {'name': 'jewel', 'id': 597, 'frequency': 'r', 'synset': 'jewel.n.01'}, {'name': 'jewelry', 'id': 598, 'frequency': 'c', 'synset': 'jewelry.n.01'}, {'name': 'joystick', 'id': 599, 'frequency': 'r', 'synset': 'joystick.n.02'}, {'name': 'jumpsuit', 'id': 600, 'frequency': 'c', 'synset': 'jump_suit.n.01'}, {'name': 'kayak', 'id': 601, 'frequency': 'c', 'synset': 'kayak.n.01'}, {'name': 'keg', 'id': 602, 'frequency': 'r', 'synset': 'keg.n.02'}, {'name': 'kennel', 'id': 603, 'frequency': 'r', 'synset': 'kennel.n.01'}, {'name': 'kettle', 'id': 604, 'frequency': 'c', 'synset': 'kettle.n.01'}, {'name': 'key', 'id': 605, 'frequency': 'f', 'synset': 'key.n.01'}, {'name': 'keycard', 'id': 606, 'frequency': 'r', 'synset': 'keycard.n.01'}, {'name': 'kilt', 'id': 607, 'frequency': 'c', 'synset': 'kilt.n.01'}, {'name': 'kimono', 'id': 608, 'frequency': 'c', 'synset': 'kimono.n.01'}, {'name': 'kitchen_sink', 'id': 609, 'frequency': 'f', 'synset': 'kitchen_sink.n.01'}, {'name': 'kitchen_table', 'id': 610, 'frequency': 'r', 'synset': 'kitchen_table.n.01'}, {'name': 'kite', 'id': 611, 'frequency': 'f', 'synset': 'kite.n.03'}, {'name': 'kitten', 'id': 612, 'frequency': 'c', 'synset': 'kitten.n.01'}, {'name': 'kiwi_fruit', 'id': 613, 'frequency': 'c', 'synset': 'kiwi.n.03'}, {'name': 'knee_pad', 'id': 614, 'frequency': 'f', 'synset': 'knee_pad.n.01'}, {'name': 'knife', 'id': 615, 'frequency': 'f', 'synset': 'knife.n.01'}, {'name': 'knitting_needle', 'id': 616, 'frequency': 'r', 'synset': 'knitting_needle.n.01'}, {'name': 'knob', 'id': 617, 'frequency': 'f', 'synset': 'knob.n.02'}, {'name': 'knocker_(on_a_door)', 'id': 618, 'frequency': 'r', 'synset': 'knocker.n.05'}, {'name': 'koala', 'id': 619, 'frequency': 'r', 'synset': 'koala.n.01'}, {'name': 'lab_coat', 'id': 620, 'frequency': 'r', 'synset': 'lab_coat.n.01'}, {'name': 'ladder', 'id': 621, 'frequency': 'f', 'synset': 'ladder.n.01'}, {'name': 'ladle', 'id': 622, 'frequency': 'c', 'synset': 'ladle.n.01'}, {'name': 'ladybug', 'id': 623, 'frequency': 'c', 'synset': 'ladybug.n.01'}, {'name': 'lamb_(animal)', 'id': 624, 'frequency': 'f', 'synset': 'lamb.n.01'}, {'name': 'lamb-chop', 'id': 625, 'frequency': 'r', 'synset': 'lamb_chop.n.01'}, {'name': 'lamp', 'id': 626, 'frequency': 'f', 'synset': 'lamp.n.02'}, {'name': 'lamppost', 'id': 627, 'frequency': 'f', 'synset': 'lamppost.n.01'}, {'name': 'lampshade', 'id': 628, 'frequency': 'f', 'synset': 'lampshade.n.01'}, {'name': 'lantern', 'id': 629, 'frequency': 'c', 'synset': 'lantern.n.01'}, {'name': 'lanyard', 'id': 630, 'frequency': 'f', 'synset': 'lanyard.n.02'}, {'name': 'laptop_computer', 'id': 631, 'frequency': 'f', 'synset': 'laptop.n.01'}, {'name': 'lasagna', 'id': 632, 'frequency': 'r', 'synset': 'lasagna.n.01'}, {'name': 'latch', 'id': 633, 'frequency': 'f', 'synset': 'latch.n.02'}, {'name': 'lawn_mower', 'id': 634, 'frequency': 'r', 'synset': 'lawn_mower.n.01'}, {'name': 'leather', 'id': 635, 'frequency': 'r', 'synset': 'leather.n.01'}, {'name': 'legging_(clothing)', 'id': 636, 'frequency': 'c', 'synset': 'legging.n.01'}, {'name': 'Lego', 'id': 637, 'frequency': 'c', 'synset': 'lego.n.01'}, {'name': 'legume', 'id': 638, 'frequency': 'r', 'synset': 'legume.n.02'}, {'name': 'lemon', 'id': 639, 'frequency': 'f', 'synset': 'lemon.n.01'}, {'name': 'lemonade', 'id': 640, 'frequency': 'r', 'synset': 'lemonade.n.01'}, {'name': 'lettuce', 'id': 641, 'frequency': 'f', 'synset': 'lettuce.n.02'}, {'name': 'license_plate', 'id': 642, 'frequency': 'f', 'synset': 'license_plate.n.01'}, {'name': 'life_buoy', 'id': 643, 'frequency': 'f', 'synset': 'life_buoy.n.01'}, {'name': 'life_jacket', 'id': 644, 'frequency': 'f', 'synset': 'life_jacket.n.01'}, {'name': 'lightbulb', 'id': 645, 'frequency': 'f', 'synset': 'light_bulb.n.01'}, {'name': 'lightning_rod', 'id': 646, 'frequency': 'r', 'synset': 'lightning_rod.n.02'}, {'name': 'lime', 'id': 647, 'frequency': 'f', 'synset': 'lime.n.06'}, {'name': 'limousine', 'id': 648, 'frequency': 'r', 'synset': 'limousine.n.01'}, {'name': 'lion', 'id': 649, 'frequency': 'c', 'synset': 'lion.n.01'}, {'name': 'lip_balm', 'id': 650, 'frequency': 'c', 'synset': 'lip_balm.n.01'}, {'name': 'liquor', 'id': 651, 'frequency': 'r', 'synset': 'liquor.n.01'}, {'name': 'lizard', 'id': 652, 'frequency': 'c', 'synset': 'lizard.n.01'}, {'name': 'log', 'id': 653, 'frequency': 'f', 'synset': 'log.n.01'}, {'name': 'lollipop', 'id': 654, 'frequency': 'c', 'synset': 'lollipop.n.02'}, {'name': 'speaker_(stero_equipment)', 'id': 655, 'frequency': 'f', 'synset': 'loudspeaker.n.01'}, {'name': 'loveseat', 'id': 656, 'frequency': 'c', 'synset': 'love_seat.n.01'}, {'name': 'machine_gun', 'id': 657, 'frequency': 'r', 'synset': 'machine_gun.n.01'}, {'name': 'magazine', 'id': 658, 'frequency': 'f', 'synset': 'magazine.n.02'}, {'name': 'magnet', 'id': 659, 'frequency': 'f', 'synset': 'magnet.n.01'}, {'name': 'mail_slot', 'id': 660, 'frequency': 'c', 'synset': 'mail_slot.n.01'}, {'name': 'mailbox_(at_home)', 'id': 661, 'frequency': 'f', 'synset': 'mailbox.n.01'}, {'name': 'mallard', 'id': 662, 'frequency': 'r', 'synset': 'mallard.n.01'}, {'name': 'mallet', 'id': 663, 'frequency': 'r', 'synset': 'mallet.n.01'}, {'name': 'mammoth', 'id': 664, 'frequency': 'r', 'synset': 'mammoth.n.01'}, {'name': 'manatee', 'id': 665, 'frequency': 'r', 'synset': 'manatee.n.01'}, {'name': 'mandarin_orange', 'id': 666, 'frequency': 'c', 'synset': 'mandarin.n.05'}, {'name': 'manger', 'id': 667, 'frequency': 'c', 'synset': 'manger.n.01'}, {'name': 'manhole', 'id': 668, 'frequency': 'f', 'synset': 'manhole.n.01'}, {'name': 'map', 'id': 669, 'frequency': 'f', 'synset': 'map.n.01'}, {'name': 'marker', 'id': 670, 'frequency': 'f', 'synset': 'marker.n.03'}, {'name': 'martini', 'id': 671, 'frequency': 'r', 'synset': 'martini.n.01'}, {'name': 'mascot', 'id': 672, 'frequency': 'r', 'synset': 'mascot.n.01'}, {'name': 'mashed_potato', 'id': 673, 'frequency': 'c', 'synset': 'mashed_potato.n.01'}, {'name': 'masher', 'id': 674, 'frequency': 'r', 'synset': 'masher.n.02'}, {'name': 'mask', 'id': 675, 'frequency': 'f', 'synset': 'mask.n.04'}, {'name': 'mast', 'id': 676, 'frequency': 'f', 'synset': 'mast.n.01'}, {'name': 'mat_(gym_equipment)', 'id': 677, 'frequency': 'c', 'synset': 'mat.n.03'}, {'name': 'matchbox', 'id': 678, 'frequency': 'r', 'synset': 'matchbox.n.01'}, {'name': 'mattress', 'id': 679, 'frequency': 'f', 'synset': 'mattress.n.01'}, {'name': 'measuring_cup', 'id': 680, 'frequency': 'c', 'synset': 'measuring_cup.n.01'}, {'name': 'measuring_stick', 'id': 681, 'frequency': 'c', 'synset': 'measuring_stick.n.01'}, {'name': 'meatball', 'id': 682, 'frequency': 'c', 'synset': 'meatball.n.01'}, {'name': 'medicine', 'id': 683, 'frequency': 'c', 'synset': 'medicine.n.02'}, {'name': 'melon', 'id': 684, 'frequency': 'c', 'synset': 'melon.n.01'}, {'name': 'microphone', 'id': 685, 'frequency': 'f', 'synset': 'microphone.n.01'}, {'name': 'microscope', 'id': 686, 'frequency': 'r', 'synset': 'microscope.n.01'}, {'name': 'microwave_oven', 'id': 687, 'frequency': 'f', 'synset': 'microwave.n.02'}, {'name': 'milestone', 'id': 688, 'frequency': 'r', 'synset': 'milestone.n.01'}, {'name': 'milk', 'id': 689, 'frequency': 'f', 'synset': 'milk.n.01'}, {'name': 'milk_can', 'id': 690, 'frequency': 'r', 'synset': 'milk_can.n.01'}, {'name': 'milkshake', 'id': 691, 'frequency': 'r', 'synset': 'milkshake.n.01'}, {'name': 'minivan', 'id': 692, 'frequency': 'f', 'synset': 'minivan.n.01'}, {'name': 'mint_candy', 'id': 693, 'frequency': 'r', 'synset': 'mint.n.05'}, {'name': 'mirror', 'id': 694, 'frequency': 'f', 'synset': 'mirror.n.01'}, {'name': 'mitten', 'id': 695, 'frequency': 'c', 'synset': 'mitten.n.01'}, {'name': 'mixer_(kitchen_tool)', 'id': 696, 'frequency': 'c', 'synset': 'mixer.n.04'}, {'name': 'money', 'id': 697, 'frequency': 'c', 'synset': 'money.n.03'}, {'name': 'monitor_(computer_equipment) computer_monitor', 'id': 698, 'frequency': 'f', 'synset': 'monitor.n.04'}, {'name': 'monkey', 'id': 699, 'frequency': 'c', 'synset': 'monkey.n.01'}, {'name': 'motor', 'id': 700, 'frequency': 'f', 'synset': 'motor.n.01'}, {'name': 'motor_scooter', 'id': 701, 'frequency': 'f', 'synset': 'motor_scooter.n.01'}, {'name': 'motor_vehicle', 'id': 702, 'frequency': 'r', 'synset': 'motor_vehicle.n.01'}, {'name': 'motorcycle', 'id': 703, 'frequency': 'f', 'synset': 'motorcycle.n.01'}, {'name': 'mound_(baseball)', 'id': 704, 'frequency': 'f', 'synset': 'mound.n.01'}, {'name': 'mouse_(computer_equipment)', 'id': 705, 'frequency': 'f', 'synset': 'mouse.n.04'}, {'name': 'mousepad', 'id': 706, 'frequency': 'f', 'synset': 'mousepad.n.01'}, {'name': 'muffin', 'id': 707, 'frequency': 'c', 'synset': 'muffin.n.01'}, {'name': 'mug', 'id': 708, 'frequency': 'f', 'synset': 'mug.n.04'}, {'name': 'mushroom', 'id': 709, 'frequency': 'f', 'synset': 'mushroom.n.02'}, {'name': 'music_stool', 'id': 710, 'frequency': 'r', 'synset': 'music_stool.n.01'}, {'name': 'musical_instrument', 'id': 711, 'frequency': 'c', 'synset': 'musical_instrument.n.01'}, {'name': 'nailfile', 'id': 712, 'frequency': 'r', 'synset': 'nailfile.n.01'}, {'name': 'napkin', 'id': 713, 'frequency': 'f', 'synset': 'napkin.n.01'}, {'name': 'neckerchief', 'id': 714, 'frequency': 'r', 'synset': 'neckerchief.n.01'}, {'name': 'necklace', 'id': 715, 'frequency': 'f', 'synset': 'necklace.n.01'}, {'name': 'necktie', 'id': 716, 'frequency': 'f', 'synset': 'necktie.n.01'}, {'name': 'needle', 'id': 717, 'frequency': 'c', 'synset': 'needle.n.03'}, {'name': 'nest', 'id': 718, 'frequency': 'c', 'synset': 'nest.n.01'}, {'name': 'newspaper', 'id': 719, 'frequency': 'f', 'synset': 'newspaper.n.01'}, {'name': 'newsstand', 'id': 720, 'frequency': 'c', 'synset': 'newsstand.n.01'}, {'name': 'nightshirt', 'id': 721, 'frequency': 'c', 'synset': 'nightwear.n.01'}, {'name': 'nosebag_(for_animals)', 'id': 722, 'frequency': 'r', 'synset': 'nosebag.n.01'}, {'name': 'noseband_(for_animals)', 'id': 723, 'frequency': 'c', 'synset': 'noseband.n.01'}, {'name': 'notebook', 'id': 724, 'frequency': 'f', 'synset': 'notebook.n.01'}, {'name': 'notepad', 'id': 725, 'frequency': 'c', 'synset': 'notepad.n.01'}, {'name': 'nut', 'id': 726, 'frequency': 'f', 'synset': 'nut.n.03'}, {'name': 'nutcracker', 'id': 727, 'frequency': 'r', 'synset': 'nutcracker.n.01'}, {'name': 'oar', 'id': 728, 'frequency': 'f', 'synset': 'oar.n.01'}, {'name': 'octopus_(food)', 'id': 729, 'frequency': 'r', 'synset': 'octopus.n.01'}, {'name': 'octopus_(animal)', 'id': 730, 'frequency': 'r', 'synset': 'octopus.n.02'}, {'name': 'oil_lamp', 'id': 731, 'frequency': 'c', 'synset': 'oil_lamp.n.01'}, {'name': 'olive_oil', 'id': 732, 'frequency': 'c', 'synset': 'olive_oil.n.01'}, {'name': 'omelet', 'id': 733, 'frequency': 'r', 'synset': 'omelet.n.01'}, {'name': 'onion', 'id': 734, 'frequency': 'f', 'synset': 'onion.n.01'}, {'name': 'orange_(fruit)', 'id': 735, 'frequency': 'f', 'synset': 'orange.n.01'}, {'name': 'orange_juice', 'id': 736, 'frequency': 'c', 'synset': 'orange_juice.n.01'}, {'name': 'ostrich', 'id': 737, 'frequency': 'c', 'synset': 'ostrich.n.02'}, {'name': 'ottoman', 'id': 738, 'frequency': 'f', 'synset': 'ottoman.n.03'}, {'name': 'oven', 'id': 739, 'frequency': 'f', 'synset': 'oven.n.01'}, {'name': 'overalls_(clothing)', 'id': 740, 'frequency': 'c', 'synset': 'overall.n.01'}, {'name': 'owl', 'id': 741, 'frequency': 'c', 'synset': 'owl.n.01'}, {'name': 'packet', 'id': 742, 'frequency': 'c', 'synset': 'packet.n.03'}, {'name': 'inkpad', 'id': 743, 'frequency': 'r', 'synset': 'pad.n.03'}, {'name': 'pad', 'id': 744, 'frequency': 'c', 'synset': 'pad.n.04'}, {'name': 'paddle', 'id': 745, 'frequency': 'f', 'synset': 'paddle.n.04'}, {'name': 'padlock', 'id': 746, 'frequency': 'c', 'synset': 'padlock.n.01'}, {'name': 'paintbrush', 'id': 747, 'frequency': 'c', 'synset': 'paintbrush.n.01'}, {'name': 'painting', 'id': 748, 'frequency': 'f', 'synset': 'painting.n.01'}, {'name': 'pajamas', 'id': 749, 'frequency': 'f', 'synset': 'pajama.n.02'}, {'name': 'palette', 'id': 750, 'frequency': 'c', 'synset': 'palette.n.02'}, {'name': 'pan_(for_cooking)', 'id': 751, 'frequency': 'f', 'synset': 'pan.n.01'}, {'name': 'pan_(metal_container)', 'id': 752, 'frequency': 'r', 'synset': 'pan.n.03'}, {'name': 'pancake', 'id': 753, 'frequency': 'c', 'synset': 'pancake.n.01'}, {'name': 'pantyhose', 'id': 754, 'frequency': 'r', 'synset': 'pantyhose.n.01'}, {'name': 'papaya', 'id': 755, 'frequency': 'r', 'synset': 'papaya.n.02'}, {'name': 'paper_plate', 'id': 756, 'frequency': 'f', 'synset': 'paper_plate.n.01'}, {'name': 'paper_towel', 'id': 757, 'frequency': 'f', 'synset': 'paper_towel.n.01'}, {'name': 'paperback_book', 'id': 758, 'frequency': 'r', 'synset': 'paperback_book.n.01'}, {'name': 'paperweight', 'id': 759, 'frequency': 'r', 'synset': 'paperweight.n.01'}, {'name': 'parachute', 'id': 760, 'frequency': 'c', 'synset': 'parachute.n.01'}, {'name': 'parakeet', 'id': 761, 'frequency': 'c', 'synset': 'parakeet.n.01'}, {'name': 'parasail_(sports)', 'id': 762, 'frequency': 'c', 'synset': 'parasail.n.01'}, {'name': 'parasol', 'id': 763, 'frequency': 'c', 'synset': 'parasol.n.01'}, {'name': 'parchment', 'id': 764, 'frequency': 'r', 'synset': 'parchment.n.01'}, {'name': 'parka', 'id': 765, 'frequency': 'c', 'synset': 'parka.n.01'}, {'name': 'parking_meter', 'id': 766, 'frequency': 'f', 'synset': 'parking_meter.n.01'}, {'name': 'parrot', 'id': 767, 'frequency': 'c', 'synset': 'parrot.n.01'}, {'name': 'passenger_car_(part_of_a_train)', 'id': 768, 'frequency': 'c', 'synset': 'passenger_car.n.01'}, {'name': 'passenger_ship', 'id': 769, 'frequency': 'r', 'synset': 'passenger_ship.n.01'}, {'name': 'passport', 'id': 770, 'frequency': 'c', 'synset': 'passport.n.02'}, {'name': 'pastry', 'id': 771, 'frequency': 'f', 'synset': 'pastry.n.02'}, {'name': 'patty_(food)', 'id': 772, 'frequency': 'r', 'synset': 'patty.n.01'}, {'name': 'pea_(food)', 'id': 773, 'frequency': 'c', 'synset': 'pea.n.01'}, {'name': 'peach', 'id': 774, 'frequency': 'c', 'synset': 'peach.n.03'}, {'name': 'peanut_butter', 'id': 775, 'frequency': 'c', 'synset': 'peanut_butter.n.01'}, {'name': 'pear', 'id': 776, 'frequency': 'f', 'synset': 'pear.n.01'}, {'name': 'peeler_(tool_for_fruit_and_vegetables)', 'id': 777, 'frequency': 'c', 'synset': 'peeler.n.03'}, {'name': 'wooden_leg', 'id': 778, 'frequency': 'r', 'synset': 'peg.n.04'}, {'name': 'pegboard', 'id': 779, 'frequency': 'r', 'synset': 'pegboard.n.01'}, {'name': 'pelican', 'id': 780, 'frequency': 'c', 'synset': 'pelican.n.01'}, {'name': 'pen', 'id': 781, 'frequency': 'f', 'synset': 'pen.n.01'}, {'name': 'pencil', 'id': 782, 'frequency': 'f', 'synset': 'pencil.n.01'}, {'name': 'pencil_box', 'id': 783, 'frequency': 'r', 'synset': 'pencil_box.n.01'}, {'name': 'pencil_sharpener', 'id': 784, 'frequency': 'r', 'synset': 'pencil_sharpener.n.01'}, {'name': 'pendulum', 'id': 785, 'frequency': 'r', 'synset': 'pendulum.n.01'}, {'name': 'penguin', 'id': 786, 'frequency': 'c', 'synset': 'penguin.n.01'}, {'name': 'pennant', 'id': 787, 'frequency': 'r', 'synset': 'pennant.n.02'}, {'name': 'penny_(coin)', 'id': 788, 'frequency': 'r', 'synset': 'penny.n.02'}, {'name': 'pepper', 'id': 789, 'frequency': 'f', 'synset': 'pepper.n.03'}, {'name': 'pepper_mill', 'id': 790, 'frequency': 'c', 'synset': 'pepper_mill.n.01'}, {'name': 'perfume', 'id': 791, 'frequency': 'c', 'synset': 'perfume.n.02'}, {'name': 'persimmon', 'id': 792, 'frequency': 'r', 'synset': 'persimmon.n.02'}, {'name': 'person', 'id': 793, 'frequency': 'f', 'synset': 'person.n.01'}, {'name': 'pet', 'id': 794, 'frequency': 'c', 'synset': 'pet.n.01'}, {'name': 'pew_(church_bench)', 'id': 795, 'frequency': 'c', 'synset': 'pew.n.01'}, {'name': 'phonebook', 'id': 796, 'frequency': 'r', 'synset': 'phonebook.n.01'}, {'name': 'phonograph_record', 'id': 797, 'frequency': 'c', 'synset': 'phonograph_record.n.01'}, {'name': 'piano', 'id': 798, 'frequency': 'f', 'synset': 'piano.n.01'}, {'name': 'pickle', 'id': 799, 'frequency': 'f', 'synset': 'pickle.n.01'}, {'name': 'pickup_truck', 'id': 800, 'frequency': 'f', 'synset': 'pickup.n.01'}, {'name': 'pie', 'id': 801, 'frequency': 'c', 'synset': 'pie.n.01'}, {'name': 'pigeon', 'id': 802, 'frequency': 'c', 'synset': 'pigeon.n.01'}, {'name': 'piggy_bank', 'id': 803, 'frequency': 'r', 'synset': 'piggy_bank.n.01'}, {'name': 'pillow', 'id': 804, 'frequency': 'f', 'synset': 'pillow.n.01'}, {'name': 'pin_(non_jewelry)', 'id': 805, 'frequency': 'r', 'synset': 'pin.n.09'}, {'name': 'pineapple', 'id': 806, 'frequency': 'f', 'synset': 'pineapple.n.02'}, {'name': 'pinecone', 'id': 807, 'frequency': 'c', 'synset': 'pinecone.n.01'}, {'name': 'ping-pong_ball', 'id': 808, 'frequency': 'r', 'synset': 'ping-pong_ball.n.01'}, {'name': 'pinwheel', 'id': 809, 'frequency': 'r', 'synset': 'pinwheel.n.03'}, {'name': 'tobacco_pipe', 'id': 810, 'frequency': 'r', 'synset': 'pipe.n.01'}, {'name': 'pipe', 'id': 811, 'frequency': 'f', 'synset': 'pipe.n.02'}, {'name': 'pistol', 'id': 812, 'frequency': 'r', 'synset': 'pistol.n.01'}, {'name': 'pita_(bread)', 'id': 813, 'frequency': 'c', 'synset': 'pita.n.01'}, {'name': 'pitcher_(vessel_for_liquid)', 'id': 814, 'frequency': 'f', 'synset': 'pitcher.n.02'}, {'name': 'pitchfork', 'id': 815, 'frequency': 'r', 'synset': 'pitchfork.n.01'}, {'name': 'pizza', 'id': 816, 'frequency': 'f', 'synset': 'pizza.n.01'}, {'name': 'place_mat', 'id': 817, 'frequency': 'f', 'synset': 'place_mat.n.01'}, {'name': 'plate', 'id': 818, 'frequency': 'f', 'synset': 'plate.n.04'}, {'name': 'platter', 'id': 819, 'frequency': 'c', 'synset': 'platter.n.01'}, {'name': 'playpen', 'id': 820, 'frequency': 'r', 'synset': 'playpen.n.01'}, {'name': 'pliers', 'id': 821, 'frequency': 'c', 'synset': 'pliers.n.01'}, {'name': 'plow_(farm_equipment)', 'id': 822, 'frequency': 'r', 'synset': 'plow.n.01'}, {'name': 'plume', 'id': 823, 'frequency': 'r', 'synset': 'plume.n.02'}, {'name': 'pocket_watch', 'id': 824, 'frequency': 'r', 'synset': 'pocket_watch.n.01'}, {'name': 'pocketknife', 'id': 825, 'frequency': 'c', 'synset': 'pocketknife.n.01'}, {'name': 'poker_(fire_stirring_tool)', 'id': 826, 'frequency': 'c', 'synset': 'poker.n.01'}, {'name': 'pole', 'id': 827, 'frequency': 'f', 'synset': 'pole.n.01'}, {'name': 'polo_shirt', 'id': 828, 'frequency': 'f', 'synset': 'polo_shirt.n.01'}, {'name': 'poncho', 'id': 829, 'frequency': 'r', 'synset': 'poncho.n.01'}, {'name': 'pony', 'id': 830, 'frequency': 'c', 'synset': 'pony.n.05'}, {'name': 'pool_table', 'id': 831, 'frequency': 'r', 'synset': 'pool_table.n.01'}, {'name': 'pop_(soda)', 'id': 832, 'frequency': 'f', 'synset': 'pop.n.02'}, {'name': 'postbox_(public)', 'id': 833, 'frequency': 'c', 'synset': 'postbox.n.01'}, {'name': 'postcard', 'id': 834, 'frequency': 'c', 'synset': 'postcard.n.01'}, {'name': 'poster', 'id': 835, 'frequency': 'f', 'synset': 'poster.n.01'}, {'name': 'pot', 'id': 836, 'frequency': 'f', 'synset': 'pot.n.01'}, {'name': 'flowerpot', 'id': 837, 'frequency': 'f', 'synset': 'pot.n.04'}, {'name': 'potato', 'id': 838, 'frequency': 'f', 'synset': 'potato.n.01'}, {'name': 'potholder', 'id': 839, 'frequency': 'c', 'synset': 'potholder.n.01'}, {'name': 'pottery', 'id': 840, 'frequency': 'c', 'synset': 'pottery.n.01'}, {'name': 'pouch', 'id': 841, 'frequency': 'c', 'synset': 'pouch.n.01'}, {'name': 'power_shovel', 'id': 842, 'frequency': 'c', 'synset': 'power_shovel.n.01'}, {'name': 'prawn', 'id': 843, 'frequency': 'c', 'synset': 'prawn.n.01'}, {'name': 'pretzel', 'id': 844, 'frequency': 'c', 'synset': 'pretzel.n.01'}, {'name': 'printer', 'id': 845, 'frequency': 'f', 'synset': 'printer.n.03'}, {'name': 'projectile_(weapon)', 'id': 846, 'frequency': 'c', 'synset': 'projectile.n.01'}, {'name': 'projector', 'id': 847, 'frequency': 'c', 'synset': 'projector.n.02'}, {'name': 'propeller', 'id': 848, 'frequency': 'f', 'synset': 'propeller.n.01'}, {'name': 'prune', 'id': 849, 'frequency': 'r', 'synset': 'prune.n.01'}, {'name': 'pudding', 'id': 850, 'frequency': 'r', 'synset': 'pudding.n.01'}, {'name': 'puffer_(fish)', 'id': 851, 'frequency': 'r', 'synset': 'puffer.n.02'}, {'name': 'puffin', 'id': 852, 'frequency': 'r', 'synset': 'puffin.n.01'}, {'name': 'pug-dog', 'id': 853, 'frequency': 'r', 'synset': 'pug.n.01'}, {'name': 'pumpkin', 'id': 854, 'frequency': 'c', 'synset': 'pumpkin.n.02'}, {'name': 'puncher', 'id': 855, 'frequency': 'r', 'synset': 'punch.n.03'}, {'name': 'puppet', 'id': 856, 'frequency': 'r', 'synset': 'puppet.n.01'}, {'name': 'puppy', 'id': 857, 'frequency': 'c', 'synset': 'puppy.n.01'}, {'name': 'quesadilla', 'id': 858, 'frequency': 'r', 'synset': 'quesadilla.n.01'}, {'name': 'quiche', 'id': 859, 'frequency': 'r', 'synset': 'quiche.n.02'}, {'name': 'quilt', 'id': 860, 'frequency': 'f', 'synset': 'quilt.n.01'}, {'name': 'rabbit', 'id': 861, 'frequency': 'c', 'synset': 'rabbit.n.01'}, {'name': 'race_car', 'id': 862, 'frequency': 'r', 'synset': 'racer.n.02'}, {'name': 'racket', 'id': 863, 'frequency': 'c', 'synset': 'racket.n.04'}, {'name': 'radar', 'id': 864, 'frequency': 'r', 'synset': 'radar.n.01'}, {'name': 'radiator', 'id': 865, 'frequency': 'f', 'synset': 'radiator.n.03'}, {'name': 'radio_receiver', 'id': 866, 'frequency': 'c', 'synset': 'radio_receiver.n.01'}, {'name': 'radish', 'id': 867, 'frequency': 'c', 'synset': 'radish.n.03'}, {'name': 'raft', 'id': 868, 'frequency': 'c', 'synset': 'raft.n.01'}, {'name': 'rag_doll', 'id': 869, 'frequency': 'r', 'synset': 'rag_doll.n.01'}, {'name': 'raincoat', 'id': 870, 'frequency': 'c', 'synset': 'raincoat.n.01'}, {'name': 'ram_(animal)', 'id': 871, 'frequency': 'c', 'synset': 'ram.n.05'}, {'name': 'raspberry', 'id': 872, 'frequency': 'c', 'synset': 'raspberry.n.02'}, {'name': 'rat', 'id': 873, 'frequency': 'r', 'synset': 'rat.n.01'}, {'name': 'razorblade', 'id': 874, 'frequency': 'c', 'synset': 'razorblade.n.01'}, {'name': 'reamer_(juicer)', 'id': 875, 'frequency': 'c', 'synset': 'reamer.n.01'}, {'name': 'rearview_mirror', 'id': 876, 'frequency': 'f', 'synset': 'rearview_mirror.n.01'}, {'name': 'receipt', 'id': 877, 'frequency': 'c', 'synset': 'receipt.n.02'}, {'name': 'recliner', 'id': 878, 'frequency': 'c', 'synset': 'recliner.n.01'}, {'name': 'record_player', 'id': 879, 'frequency': 'c', 'synset': 'record_player.n.01'}, {'name': 'reflector', 'id': 880, 'frequency': 'f', 'synset': 'reflector.n.01'}, {'name': 'remote_control', 'id': 881, 'frequency': 'f', 'synset': 'remote_control.n.01'}, {'name': 'rhinoceros', 'id': 882, 'frequency': 'c', 'synset': 'rhinoceros.n.01'}, {'name': 'rib_(food)', 'id': 883, 'frequency': 'r', 'synset': 'rib.n.03'}, {'name': 'rifle', 'id': 884, 'frequency': 'c', 'synset': 'rifle.n.01'}, {'name': 'ring', 'id': 885, 'frequency': 'f', 'synset': 'ring.n.08'}, {'name': 'river_boat', 'id': 886, 'frequency': 'r', 'synset': 'river_boat.n.01'}, {'name': 'road_map', 'id': 887, 'frequency': 'r', 'synset': 'road_map.n.02'}, {'name': 'robe', 'id': 888, 'frequency': 'c', 'synset': 'robe.n.01'}, {'name': 'rocking_chair', 'id': 889, 'frequency': 'c', 'synset': 'rocking_chair.n.01'}, {'name': 'rodent', 'id': 890, 'frequency': 'r', 'synset': 'rodent.n.01'}, {'name': 'roller_skate', 'id': 891, 'frequency': 'r', 'synset': 'roller_skate.n.01'}, {'name': 'Rollerblade', 'id': 892, 'frequency': 'r', 'synset': 'rollerblade.n.01'}, {'name': 'rolling_pin', 'id': 893, 'frequency': 'c', 'synset': 'rolling_pin.n.01'}, {'name': 'root_beer', 'id': 894, 'frequency': 'r', 'synset': 'root_beer.n.01'}, {'name': 'router_(computer_equipment)', 'id': 895, 'frequency': 'c', 'synset': 'router.n.02'}, {'name': 'rubber_band', 'id': 896, 'frequency': 'f', 'synset': 'rubber_band.n.01'}, {'name': 'runner_(carpet)', 'id': 897, 'frequency': 'c', 'synset': 'runner.n.08'}, {'name': 'plastic_bag', 'id': 898, 'frequency': 'f', 'synset': 'sack.n.01'}, {'name': 'saddle_(on_an_animal)', 'id': 899, 'frequency': 'f', 'synset': 'saddle.n.01'}, {'name': 'saddle_blanket', 'id': 900, 'frequency': 'f', 'synset': 'saddle_blanket.n.01'}, {'name': 'saddlebag', 'id': 901, 'frequency': 'c', 'synset': 'saddlebag.n.01'}, {'name': 'safety_pin', 'id': 902, 'frequency': 'r', 'synset': 'safety_pin.n.01'}, {'name': 'sail', 'id': 903, 'frequency': 'f', 'synset': 'sail.n.01'}, {'name': 'salad', 'id': 904, 'frequency': 'f', 'synset': 'salad.n.01'}, {'name': 'salad_plate', 'id': 905, 'frequency': 'r', 'synset': 'salad_plate.n.01'}, {'name': 'salami', 'id': 906, 'frequency': 'c', 'synset': 'salami.n.01'}, {'name': 'salmon_(fish)', 'id': 907, 'frequency': 'c', 'synset': 'salmon.n.01'}, {'name': 'salmon_(food)', 'id': 908, 'frequency': 'r', 'synset': 'salmon.n.03'}, {'name': 'salsa', 'id': 909, 'frequency': 'c', 'synset': 'salsa.n.01'}, {'name': 'saltshaker', 'id': 910, 'frequency': 'f', 'synset': 'saltshaker.n.01'}, {'name': 'sandal_(type_of_shoe)', 'id': 911, 'frequency': 'f', 'synset': 'sandal.n.01'}, {'name': 'sandwich', 'id': 912, 'frequency': 'f', 'synset': 'sandwich.n.01'}, {'name': 'satchel', 'id': 913, 'frequency': 'r', 'synset': 'satchel.n.01'}, {'name': 'saucepan', 'id': 914, 'frequency': 'r', 'synset': 'saucepan.n.01'}, {'name': 'saucer', 'id': 915, 'frequency': 'f', 'synset': 'saucer.n.02'}, {'name': 'sausage', 'id': 916, 'frequency': 'f', 'synset': 'sausage.n.01'}, {'name': 'sawhorse', 'id': 917, 'frequency': 'r', 'synset': 'sawhorse.n.01'}, {'name': 'saxophone', 'id': 918, 'frequency': 'r', 'synset': 'sax.n.02'}, {'name': 'scale_(measuring_instrument)', 'id': 919, 'frequency': 'f', 'synset': 'scale.n.07'}, {'name': 'scarecrow', 'id': 920, 'frequency': 'r', 'synset': 'scarecrow.n.01'}, {'name': 'scarf', 'id': 921, 'frequency': 'f', 'synset': 'scarf.n.01'}, {'name': 'school_bus', 'id': 922, 'frequency': 'c', 'synset': 'school_bus.n.01'}, {'name': 'scissors', 'id': 923, 'frequency': 'f', 'synset': 'scissors.n.01'}, {'name': 'scoreboard', 'id': 924, 'frequency': 'f', 'synset': 'scoreboard.n.01'}, {'name': 'scraper', 'id': 925, 'frequency': 'r', 'synset': 'scraper.n.01'}, {'name': 'screwdriver', 'id': 926, 'frequency': 'c', 'synset': 'screwdriver.n.01'}, {'name': 'scrubbing_brush', 'id': 927, 'frequency': 'f', 'synset': 'scrub_brush.n.01'}, {'name': 'sculpture', 'id': 928, 'frequency': 'c', 'synset': 'sculpture.n.01'}, {'name': 'seabird', 'id': 929, 'frequency': 'c', 'synset': 'seabird.n.01'}, {'name': 'seahorse', 'id': 930, 'frequency': 'c', 'synset': 'seahorse.n.02'}, {'name': 'seaplane', 'id': 931, 'frequency': 'r', 'synset': 'seaplane.n.01'}, {'name': 'seashell', 'id': 932, 'frequency': 'c', 'synset': 'seashell.n.01'}, {'name': 'sewing_machine', 'id': 933, 'frequency': 'c', 'synset': 'sewing_machine.n.01'}, {'name': 'shaker', 'id': 934, 'frequency': 'c', 'synset': 'shaker.n.03'}, {'name': 'shampoo', 'id': 935, 'frequency': 'c', 'synset': 'shampoo.n.01'}, {'name': 'shark', 'id': 936, 'frequency': 'c', 'synset': 'shark.n.01'}, {'name': 'sharpener', 'id': 937, 'frequency': 'r', 'synset': 'sharpener.n.01'}, {'name': 'Sharpie', 'id': 938, 'frequency': 'r', 'synset': 'sharpie.n.03'}, {'name': 'shaver_(electric)', 'id': 939, 'frequency': 'r', 'synset': 'shaver.n.03'}, {'name': 'shaving_cream', 'id': 940, 'frequency': 'c', 'synset': 'shaving_cream.n.01'}, {'name': 'shawl', 'id': 941, 'frequency': 'r', 'synset': 'shawl.n.01'}, {'name': 'shears', 'id': 942, 'frequency': 'r', 'synset': 'shears.n.01'}, {'name': 'sheep', 'id': 943, 'frequency': 'f', 'synset': 'sheep.n.01'}, {'name': 'shepherd_dog', 'id': 944, 'frequency': 'r', 'synset': 'shepherd_dog.n.01'}, {'name': 'sherbert', 'id': 945, 'frequency': 'r', 'synset': 'sherbert.n.01'}, {'name': 'shield', 'id': 946, 'frequency': 'c', 'synset': 'shield.n.02'}, {'name': 'shirt', 'id': 947, 'frequency': 'f', 'synset': 'shirt.n.01'}, {'name': 'shoe', 'id': 948, 'frequency': 'f', 'synset': 'shoe.n.01'}, {'name': 'shopping_bag', 'id': 949, 'frequency': 'f', 'synset': 'shopping_bag.n.01'}, {'name': 'shopping_cart', 'id': 950, 'frequency': 'c', 'synset': 'shopping_cart.n.01'}, {'name': 'short_pants', 'id': 951, 'frequency': 'f', 'synset': 'short_pants.n.01'}, {'name': 'shot_glass', 'id': 952, 'frequency': 'r', 'synset': 'shot_glass.n.01'}, {'name': 'shoulder_bag', 'id': 953, 'frequency': 'f', 'synset': 'shoulder_bag.n.01'}, {'name': 'shovel', 'id': 954, 'frequency': 'c', 'synset': 'shovel.n.01'}, {'name': 'shower_head', 'id': 955, 'frequency': 'f', 'synset': 'shower.n.01'}, {'name': 'shower_cap', 'id': 956, 'frequency': 'r', 'synset': 'shower_cap.n.01'}, {'name': 'shower_curtain', 'id': 957, 'frequency': 'f', 'synset': 'shower_curtain.n.01'}, {'name': 'shredder_(for_paper)', 'id': 958, 'frequency': 'r', 'synset': 'shredder.n.01'}, {'name': 'signboard', 'id': 959, 'frequency': 'f', 'synset': 'signboard.n.01'}, {'name': 'silo', 'id': 960, 'frequency': 'c', 'synset': 'silo.n.01'}, {'name': 'sink', 'id': 961, 'frequency': 'f', 'synset': 'sink.n.01'}, {'name': 'skateboard', 'id': 962, 'frequency': 'f', 'synset': 'skateboard.n.01'}, {'name': 'skewer', 'id': 963, 'frequency': 'c', 'synset': 'skewer.n.01'}, {'name': 'ski', 'id': 964, 'frequency': 'f', 'synset': 'ski.n.01'}, {'name': 'ski_boot', 'id': 965, 'frequency': 'f', 'synset': 'ski_boot.n.01'}, {'name': 'ski_parka', 'id': 966, 'frequency': 'f', 'synset': 'ski_parka.n.01'}, {'name': 'ski_pole', 'id': 967, 'frequency': 'f', 'synset': 'ski_pole.n.01'}, {'name': 'skirt', 'id': 968, 'frequency': 'f', 'synset': 'skirt.n.02'}, {'name': 'skullcap', 'id': 969, 'frequency': 'r', 'synset': 'skullcap.n.01'}, {'name': 'sled', 'id': 970, 'frequency': 'c', 'synset': 'sled.n.01'}, {'name': 'sleeping_bag', 'id': 971, 'frequency': 'c', 'synset': 'sleeping_bag.n.01'}, {'name': 'sling_(bandage)', 'id': 972, 'frequency': 'r', 'synset': 'sling.n.05'}, {'name': 'slipper_(footwear)', 'id': 973, 'frequency': 'c', 'synset': 'slipper.n.01'}, {'name': 'smoothie', 'id': 974, 'frequency': 'r', 'synset': 'smoothie.n.02'}, {'name': 'snake', 'id': 975, 'frequency': 'r', 'synset': 'snake.n.01'}, {'name': 'snowboard', 'id': 976, 'frequency': 'f', 'synset': 'snowboard.n.01'}, {'name': 'snowman', 'id': 977, 'frequency': 'c', 'synset': 'snowman.n.01'}, {'name': 'snowmobile', 'id': 978, 'frequency': 'c', 'synset': 'snowmobile.n.01'}, {'name': 'soap', 'id': 979, 'frequency': 'f', 'synset': 'soap.n.01'}, {'name': 'soccer_ball', 'id': 980, 'frequency': 'f', 'synset': 'soccer_ball.n.01'}, {'name': 'sock', 'id': 981, 'frequency': 'f', 'synset': 'sock.n.01'}, {'name': 'sofa', 'id': 982, 'frequency': 'f', 'synset': 'sofa.n.01'}, {'name': 'softball', 'id': 983, 'frequency': 'r', 'synset': 'softball.n.01'}, {'name': 'solar_array', 'id': 984, 'frequency': 'c', 'synset': 'solar_array.n.01'}, {'name': 'sombrero', 'id': 985, 'frequency': 'r', 'synset': 'sombrero.n.02'}, {'name': 'soup', 'id': 986, 'frequency': 'f', 'synset': 'soup.n.01'}, {'name': 'soup_bowl', 'id': 987, 'frequency': 'r', 'synset': 'soup_bowl.n.01'}, {'name': 'soupspoon', 'id': 988, 'frequency': 'c', 'synset': 'soupspoon.n.01'}, {'name': 'sour_cream', 'id': 989, 'frequency': 'c', 'synset': 'sour_cream.n.01'}, {'name': 'soya_milk', 'id': 990, 'frequency': 'r', 'synset': 'soya_milk.n.01'}, {'name': 'space_shuttle', 'id': 991, 'frequency': 'r', 'synset': 'space_shuttle.n.01'}, {'name': 'sparkler_(fireworks)', 'id': 992, 'frequency': 'r', 'synset': 'sparkler.n.02'}, {'name': 'spatula', 'id': 993, 'frequency': 'f', 'synset': 'spatula.n.02'}, {'name': 'spear', 'id': 994, 'frequency': 'r', 'synset': 'spear.n.01'}, {'name': 'spectacles', 'id': 995, 'frequency': 'f', 'synset': 'spectacles.n.01'}, {'name': 'spice_rack', 'id': 996, 'frequency': 'c', 'synset': 'spice_rack.n.01'}, {'name': 'spider', 'id': 997, 'frequency': 'c', 'synset': 'spider.n.01'}, {'name': 'crawfish', 'id': 998, 'frequency': 'r', 'synset': 'spiny_lobster.n.02'}, {'name': 'sponge', 'id': 999, 'frequency': 'c', 'synset': 'sponge.n.01'}, {'name': 'spoon', 'id': 1000, 'frequency': 'f', 'synset': 'spoon.n.01'}, {'name': 'sportswear', 'id': 1001, 'frequency': 'c', 'synset': 'sportswear.n.01'}, {'name': 'spotlight', 'id': 1002, 'frequency': 'c', 'synset': 'spotlight.n.02'}, {'name': 'squid_(food)', 'id': 1003, 'frequency': 'r', 'synset': 'squid.n.01'}, {'name': 'squirrel', 'id': 1004, 'frequency': 'c', 'synset': 'squirrel.n.01'}, {'name': 'stagecoach', 'id': 1005, 'frequency': 'r', 'synset': 'stagecoach.n.01'}, {'name': 'stapler_(stapling_machine)', 'id': 1006, 'frequency': 'c', 'synset': 'stapler.n.01'}, {'name': 'starfish', 'id': 1007, 'frequency': 'c', 'synset': 'starfish.n.01'}, {'name': 'statue_(sculpture)', 'id': 1008, 'frequency': 'f', 'synset': 'statue.n.01'}, {'name': 'steak_(food)', 'id': 1009, 'frequency': 'c', 'synset': 'steak.n.01'}, {'name': 'steak_knife', 'id': 1010, 'frequency': 'r', 'synset': 'steak_knife.n.01'}, {'name': 'steering_wheel', 'id': 1011, 'frequency': 'f', 'synset': 'steering_wheel.n.01'}, {'name': 'stepladder', 'id': 1012, 'frequency': 'r', 'synset': 'step_ladder.n.01'}, {'name': 'step_stool', 'id': 1013, 'frequency': 'c', 'synset': 'step_stool.n.01'}, {'name': 'stereo_(sound_system)', 'id': 1014, 'frequency': 'c', 'synset': 'stereo.n.01'}, {'name': 'stew', 'id': 1015, 'frequency': 'r', 'synset': 'stew.n.02'}, {'name': 'stirrer', 'id': 1016, 'frequency': 'r', 'synset': 'stirrer.n.02'}, {'name': 'stirrup', 'id': 1017, 'frequency': 'f', 'synset': 'stirrup.n.01'}, {'name': 'stool', 'id': 1018, 'frequency': 'f', 'synset': 'stool.n.01'}, {'name': 'stop_sign', 'id': 1019, 'frequency': 'f', 'synset': 'stop_sign.n.01'}, {'name': 'brake_light', 'id': 1020, 'frequency': 'f', 'synset': 'stoplight.n.01'}, {'name': 'stove', 'id': 1021, 'frequency': 'f', 'synset': 'stove.n.01'}, {'name': 'strainer', 'id': 1022, 'frequency': 'c', 'synset': 'strainer.n.01'}, {'name': 'strap', 'id': 1023, 'frequency': 'f', 'synset': 'strap.n.01'}, {'name': 'straw_(for_drinking)', 'id': 1024, 'frequency': 'f', 'synset': 'straw.n.04'}, {'name': 'strawberry', 'id': 1025, 'frequency': 'f', 'synset': 'strawberry.n.01'}, {'name': 'street_sign', 'id': 1026, 'frequency': 'f', 'synset': 'street_sign.n.01'}, {'name': 'streetlight', 'id': 1027, 'frequency': 'f', 'synset': 'streetlight.n.01'}, {'name': 'string_cheese', 'id': 1028, 'frequency': 'r', 'synset': 'string_cheese.n.01'}, {'name': 'stylus', 'id': 1029, 'frequency': 'r', 'synset': 'stylus.n.02'}, {'name': 'subwoofer', 'id': 1030, 'frequency': 'r', 'synset': 'subwoofer.n.01'}, {'name': 'sugar_bowl', 'id': 1031, 'frequency': 'r', 'synset': 'sugar_bowl.n.01'}, {'name': 'sugarcane_(plant)', 'id': 1032, 'frequency': 'r', 'synset': 'sugarcane.n.01'}, {'name': 'suit_(clothing)', 'id': 1033, 'frequency': 'f', 'synset': 'suit.n.01'}, {'name': 'sunflower', 'id': 1034, 'frequency': 'c', 'synset': 'sunflower.n.01'}, {'name': 'sunglasses', 'id': 1035, 'frequency': 'f', 'synset': 'sunglasses.n.01'}, {'name': 'sunhat', 'id': 1036, 'frequency': 'c', 'synset': 'sunhat.n.01'}, {'name': 'surfboard', 'id': 1037, 'frequency': 'f', 'synset': 'surfboard.n.01'}, {'name': 'sushi', 'id': 1038, 'frequency': 'c', 'synset': 'sushi.n.01'}, {'name': 'mop', 'id': 1039, 'frequency': 'c', 'synset': 'swab.n.02'}, {'name': 'sweat_pants', 'id': 1040, 'frequency': 'c', 'synset': 'sweat_pants.n.01'}, {'name': 'sweatband', 'id': 1041, 'frequency': 'c', 'synset': 'sweatband.n.02'}, {'name': 'sweater', 'id': 1042, 'frequency': 'f', 'synset': 'sweater.n.01'}, {'name': 'sweatshirt', 'id': 1043, 'frequency': 'f', 'synset': 'sweatshirt.n.01'}, {'name': 'sweet_potato', 'id': 1044, 'frequency': 'c', 'synset': 'sweet_potato.n.02'}, {'name': 'swimsuit', 'id': 1045, 'frequency': 'f', 'synset': 'swimsuit.n.01'}, {'name': 'sword', 'id': 1046, 'frequency': 'c', 'synset': 'sword.n.01'}, {'name': 'syringe', 'id': 1047, 'frequency': 'r', 'synset': 'syringe.n.01'}, {'name': 'Tabasco_sauce', 'id': 1048, 'frequency': 'r', 'synset': 'tabasco.n.02'}, {'name': 'table-tennis_table', 'id': 1049, 'frequency': 'r', 'synset': 'table-tennis_table.n.01'}, {'name': 'table', 'id': 1050, 'frequency': 'f', 'synset': 'table.n.02'}, {'name': 'table_lamp', 'id': 1051, 'frequency': 'c', 'synset': 'table_lamp.n.01'}, {'name': 'tablecloth', 'id': 1052, 'frequency': 'f', 'synset': 'tablecloth.n.01'}, {'name': 'tachometer', 'id': 1053, 'frequency': 'r', 'synset': 'tachometer.n.01'}, {'name': 'taco', 'id': 1054, 'frequency': 'r', 'synset': 'taco.n.02'}, {'name': 'tag', 'id': 1055, 'frequency': 'f', 'synset': 'tag.n.02'}, {'name': 'taillight', 'id': 1056, 'frequency': 'f', 'synset': 'taillight.n.01'}, {'name': 'tambourine', 'id': 1057, 'frequency': 'r', 'synset': 'tambourine.n.01'}, {'name': 'army_tank', 'id': 1058, 'frequency': 'r', 'synset': 'tank.n.01'}, {'name': 'tank_(storage_vessel)', 'id': 1059, 'frequency': 'f', 'synset': 'tank.n.02'}, {'name': 'tank_top_(clothing)', 'id': 1060, 'frequency': 'f', 'synset': 'tank_top.n.01'}, {'name': 'tape_(sticky_cloth_or_paper)', 'id': 1061, 'frequency': 'f', 'synset': 'tape.n.01'}, {'name': 'tape_measure', 'id': 1062, 'frequency': 'c', 'synset': 'tape.n.04'}, {'name': 'tapestry', 'id': 1063, 'frequency': 'c', 'synset': 'tapestry.n.02'}, {'name': 'tarp', 'id': 1064, 'frequency': 'f', 'synset': 'tarpaulin.n.01'}, {'name': 'tartan', 'id': 1065, 'frequency': 'c', 'synset': 'tartan.n.01'}, {'name': 'tassel', 'id': 1066, 'frequency': 'c', 'synset': 'tassel.n.01'}, {'name': 'tea_bag', 'id': 1067, 'frequency': 'c', 'synset': 'tea_bag.n.01'}, {'name': 'teacup', 'id': 1068, 'frequency': 'c', 'synset': 'teacup.n.02'}, {'name': 'teakettle', 'id': 1069, 'frequency': 'c', 'synset': 'teakettle.n.01'}, {'name': 'teapot', 'id': 1070, 'frequency': 'f', 'synset': 'teapot.n.01'}, {'name': 'teddy_bear', 'id': 1071, 'frequency': 'f', 'synset': 'teddy.n.01'}, {'name': 'telephone', 'id': 1072, 'frequency': 'f', 'synset': 'telephone.n.01'}, {'name': 'telephone_booth', 'id': 1073, 'frequency': 'c', 'synset': 'telephone_booth.n.01'}, {'name': 'telephone_pole', 'id': 1074, 'frequency': 'f', 'synset': 'telephone_pole.n.01'}, {'name': 'telephoto_lens', 'id': 1075, 'frequency': 'r', 'synset': 'telephoto_lens.n.01'}, {'name': 'television_camera', 'id': 1076, 'frequency': 'c', 'synset': 'television_camera.n.01'}, {'name': 'television_set', 'id': 1077, 'frequency': 'f', 'synset': 'television_receiver.n.01'}, {'name': 'tennis_ball', 'id': 1078, 'frequency': 'f', 'synset': 'tennis_ball.n.01'}, {'name': 'tennis_racket', 'id': 1079, 'frequency': 'f', 'synset': 'tennis_racket.n.01'}, {'name': 'tequila', 'id': 1080, 'frequency': 'r', 'synset': 'tequila.n.01'}, {'name': 'thermometer', 'id': 1081, 'frequency': 'c', 'synset': 'thermometer.n.01'}, {'name': 'thermos_bottle', 'id': 1082, 'frequency': 'c', 'synset': 'thermos.n.01'}, {'name': 'thermostat', 'id': 1083, 'frequency': 'f', 'synset': 'thermostat.n.01'}, {'name': 'thimble', 'id': 1084, 'frequency': 'r', 'synset': 'thimble.n.02'}, {'name': 'thread', 'id': 1085, 'frequency': 'c', 'synset': 'thread.n.01'}, {'name': 'thumbtack', 'id': 1086, 'frequency': 'c', 'synset': 'thumbtack.n.01'}, {'name': 'tiara', 'id': 1087, 'frequency': 'c', 'synset': 'tiara.n.01'}, {'name': 'tiger', 'id': 1088, 'frequency': 'c', 'synset': 'tiger.n.02'}, {'name': 'tights_(clothing)', 'id': 1089, 'frequency': 'c', 'synset': 'tights.n.01'}, {'name': 'timer', 'id': 1090, 'frequency': 'c', 'synset': 'timer.n.01'}, {'name': 'tinfoil', 'id': 1091, 'frequency': 'f', 'synset': 'tinfoil.n.01'}, {'name': 'tinsel', 'id': 1092, 'frequency': 'c', 'synset': 'tinsel.n.01'}, {'name': 'tissue_paper', 'id': 1093, 'frequency': 'f', 'synset': 'tissue.n.02'}, {'name': 'toast_(food)', 'id': 1094, 'frequency': 'c', 'synset': 'toast.n.01'}, {'name': 'toaster', 'id': 1095, 'frequency': 'f', 'synset': 'toaster.n.02'}, {'name': 'toaster_oven', 'id': 1096, 'frequency': 'f', 'synset': 'toaster_oven.n.01'}, {'name': 'toilet', 'id': 1097, 'frequency': 'f', 'synset': 'toilet.n.02'}, {'name': 'toilet_tissue', 'id': 1098, 'frequency': 'f', 'synset': 'toilet_tissue.n.01'}, {'name': 'tomato', 'id': 1099, 'frequency': 'f', 'synset': 'tomato.n.01'}, {'name': 'tongs', 'id': 1100, 'frequency': 'f', 'synset': 'tongs.n.01'}, {'name': 'toolbox', 'id': 1101, 'frequency': 'c', 'synset': 'toolbox.n.01'}, {'name': 'toothbrush', 'id': 1102, 'frequency': 'f', 'synset': 'toothbrush.n.01'}, {'name': 'toothpaste', 'id': 1103, 'frequency': 'f', 'synset': 'toothpaste.n.01'}, {'name': 'toothpick', 'id': 1104, 'frequency': 'f', 'synset': 'toothpick.n.01'}, {'name': 'cover', 'id': 1105, 'frequency': 'f', 'synset': 'top.n.09'}, {'name': 'tortilla', 'id': 1106, 'frequency': 'c', 'synset': 'tortilla.n.01'}, {'name': 'tow_truck', 'id': 1107, 'frequency': 'c', 'synset': 'tow_truck.n.01'}, {'name': 'towel', 'id': 1108, 'frequency': 'f', 'synset': 'towel.n.01'}, {'name': 'towel_rack', 'id': 1109, 'frequency': 'f', 'synset': 'towel_rack.n.01'}, {'name': 'toy', 'id': 1110, 'frequency': 'f', 'synset': 'toy.n.03'}, {'name': 'tractor_(farm_equipment)', 'id': 1111, 'frequency': 'c', 'synset': 'tractor.n.01'}, {'name': 'traffic_light', 'id': 1112, 'frequency': 'f', 'synset': 'traffic_light.n.01'}, {'name': 'dirt_bike', 'id': 1113, 'frequency': 'c', 'synset': 'trail_bike.n.01'}, {'name': 'trailer_truck', 'id': 1114, 'frequency': 'f', 'synset': 'trailer_truck.n.01'}, {'name': 'train_(railroad_vehicle)', 'id': 1115, 'frequency': 'f', 'synset': 'train.n.01'}, {'name': 'trampoline', 'id': 1116, 'frequency': 'r', 'synset': 'trampoline.n.01'}, {'name': 'tray', 'id': 1117, 'frequency': 'f', 'synset': 'tray.n.01'}, {'name': 'trench_coat', 'id': 1118, 'frequency': 'r', 'synset': 'trench_coat.n.01'}, {'name': 'triangle_(musical_instrument)', 'id': 1119, 'frequency': 'r', 'synset': 'triangle.n.05'}, {'name': 'tricycle', 'id': 1120, 'frequency': 'c', 'synset': 'tricycle.n.01'}, {'name': 'tripod', 'id': 1121, 'frequency': 'f', 'synset': 'tripod.n.01'}, {'name': 'trousers', 'id': 1122, 'frequency': 'f', 'synset': 'trouser.n.01'}, {'name': 'truck', 'id': 1123, 'frequency': 'f', 'synset': 'truck.n.01'}, {'name': 'truffle_(chocolate)', 'id': 1124, 'frequency': 'r', 'synset': 'truffle.n.03'}, {'name': 'trunk', 'id': 1125, 'frequency': 'c', 'synset': 'trunk.n.02'}, {'name': 'vat', 'id': 1126, 'frequency': 'r', 'synset': 'tub.n.02'}, {'name': 'turban', 'id': 1127, 'frequency': 'c', 'synset': 'turban.n.01'}, {'name': 'turkey_(food)', 'id': 1128, 'frequency': 'c', 'synset': 'turkey.n.04'}, {'name': 'turnip', 'id': 1129, 'frequency': 'r', 'synset': 'turnip.n.01'}, {'name': 'turtle', 'id': 1130, 'frequency': 'c', 'synset': 'turtle.n.02'}, {'name': 'turtleneck_(clothing)', 'id': 1131, 'frequency': 'c', 'synset': 'turtleneck.n.01'}, {'name': 'typewriter', 'id': 1132, 'frequency': 'c', 'synset': 'typewriter.n.01'}, {'name': 'umbrella', 'id': 1133, 'frequency': 'f', 'synset': 'umbrella.n.01'}, {'name': 'underwear', 'id': 1134, 'frequency': 'f', 'synset': 'underwear.n.01'}, {'name': 'unicycle', 'id': 1135, 'frequency': 'r', 'synset': 'unicycle.n.01'}, {'name': 'urinal', 'id': 1136, 'frequency': 'f', 'synset': 'urinal.n.01'}, {'name': 'urn', 'id': 1137, 'frequency': 'c', 'synset': 'urn.n.01'}, {'name': 'vacuum_cleaner', 'id': 1138, 'frequency': 'c', 'synset': 'vacuum.n.04'}, {'name': 'vase', 'id': 1139, 'frequency': 'f', 'synset': 'vase.n.01'}, {'name': 'vending_machine', 'id': 1140, 'frequency': 'c', 'synset': 'vending_machine.n.01'}, {'name': 'vent', 'id': 1141, 'frequency': 'f', 'synset': 'vent.n.01'}, {'name': 'vest', 'id': 1142, 'frequency': 'f', 'synset': 'vest.n.01'}, {'name': 'videotape', 'id': 1143, 'frequency': 'c', 'synset': 'videotape.n.01'}, {'name': 'vinegar', 'id': 1144, 'frequency': 'r', 'synset': 'vinegar.n.01'}, {'name': 'violin', 'id': 1145, 'frequency': 'r', 'synset': 'violin.n.01'}, {'name': 'vodka', 'id': 1146, 'frequency': 'r', 'synset': 'vodka.n.01'}, {'name': 'volleyball', 'id': 1147, 'frequency': 'c', 'synset': 'volleyball.n.02'}, {'name': 'vulture', 'id': 1148, 'frequency': 'r', 'synset': 'vulture.n.01'}, {'name': 'waffle', 'id': 1149, 'frequency': 'c', 'synset': 'waffle.n.01'}, {'name': 'waffle_iron', 'id': 1150, 'frequency': 'r', 'synset': 'waffle_iron.n.01'}, {'name': 'wagon', 'id': 1151, 'frequency': 'c', 'synset': 'wagon.n.01'}, {'name': 'wagon_wheel', 'id': 1152, 'frequency': 'c', 'synset': 'wagon_wheel.n.01'}, {'name': 'walking_stick', 'id': 1153, 'frequency': 'c', 'synset': 'walking_stick.n.01'}, {'name': 'wall_clock', 'id': 1154, 'frequency': 'c', 'synset': 'wall_clock.n.01'}, {'name': 'wall_socket', 'id': 1155, 'frequency': 'f', 'synset': 'wall_socket.n.01'}, {'name': 'wallet', 'id': 1156, 'frequency': 'f', 'synset': 'wallet.n.01'}, {'name': 'walrus', 'id': 1157, 'frequency': 'r', 'synset': 'walrus.n.01'}, {'name': 'wardrobe', 'id': 1158, 'frequency': 'r', 'synset': 'wardrobe.n.01'}, {'name': 'washbasin', 'id': 1159, 'frequency': 'r', 'synset': 'washbasin.n.01'}, {'name': 'automatic_washer', 'id': 1160, 'frequency': 'c', 'synset': 'washer.n.03'}, {'name': 'watch', 'id': 1161, 'frequency': 'f', 'synset': 'watch.n.01'}, {'name': 'water_bottle', 'id': 1162, 'frequency': 'f', 'synset': 'water_bottle.n.01'}, {'name': 'water_cooler', 'id': 1163, 'frequency': 'c', 'synset': 'water_cooler.n.01'}, {'name': 'water_faucet', 'id': 1164, 'frequency': 'c', 'synset': 'water_faucet.n.01'}, {'name': 'water_heater', 'id': 1165, 'frequency': 'r', 'synset': 'water_heater.n.01'}, {'name': 'water_jug', 'id': 1166, 'frequency': 'c', 'synset': 'water_jug.n.01'}, {'name': 'water_gun', 'id': 1167, 'frequency': 'r', 'synset': 'water_pistol.n.01'}, {'name': 'water_scooter', 'id': 1168, 'frequency': 'c', 'synset': 'water_scooter.n.01'}, {'name': 'water_ski', 'id': 1169, 'frequency': 'c', 'synset': 'water_ski.n.01'}, {'name': 'water_tower', 'id': 1170, 'frequency': 'c', 'synset': 'water_tower.n.01'}, {'name': 'watering_can', 'id': 1171, 'frequency': 'c', 'synset': 'watering_can.n.01'}, {'name': 'watermelon', 'id': 1172, 'frequency': 'f', 'synset': 'watermelon.n.02'}, {'name': 'weathervane', 'id': 1173, 'frequency': 'f', 'synset': 'weathervane.n.01'}, {'name': 'webcam', 'id': 1174, 'frequency': 'c', 'synset': 'webcam.n.01'}, {'name': 'wedding_cake', 'id': 1175, 'frequency': 'c', 'synset': 'wedding_cake.n.01'}, {'name': 'wedding_ring', 'id': 1176, 'frequency': 'c', 'synset': 'wedding_ring.n.01'}, {'name': 'wet_suit', 'id': 1177, 'frequency': 'f', 'synset': 'wet_suit.n.01'}, {'name': 'wheel', 'id': 1178, 'frequency': 'f', 'synset': 'wheel.n.01'}, {'name': 'wheelchair', 'id': 1179, 'frequency': 'c', 'synset': 'wheelchair.n.01'}, {'name': 'whipped_cream', 'id': 1180, 'frequency': 'c', 'synset': 'whipped_cream.n.01'}, {'name': 'whistle', 'id': 1181, 'frequency': 'c', 'synset': 'whistle.n.03'}, {'name': 'wig', 'id': 1182, 'frequency': 'c', 'synset': 'wig.n.01'}, {'name': 'wind_chime', 'id': 1183, 'frequency': 'c', 'synset': 'wind_chime.n.01'}, {'name': 'windmill', 'id': 1184, 'frequency': 'c', 'synset': 'windmill.n.01'}, {'name': 'window_box_(for_plants)', 'id': 1185, 'frequency': 'c', 'synset': 'window_box.n.01'}, {'name': 'windshield_wiper', 'id': 1186, 'frequency': 'f', 'synset': 'windshield_wiper.n.01'}, {'name': 'windsock', 'id': 1187, 'frequency': 'c', 'synset': 'windsock.n.01'}, {'name': 'wine_bottle', 'id': 1188, 'frequency': 'f', 'synset': 'wine_bottle.n.01'}, {'name': 'wine_bucket', 'id': 1189, 'frequency': 'c', 'synset': 'wine_bucket.n.01'}, {'name': 'wineglass', 'id': 1190, 'frequency': 'f', 'synset': 'wineglass.n.01'}, {'name': 'blinder_(for_horses)', 'id': 1191, 'frequency': 'f', 'synset': 'winker.n.02'}, {'name': 'wok', 'id': 1192, 'frequency': 'c', 'synset': 'wok.n.01'}, {'name': 'wolf', 'id': 1193, 'frequency': 'r', 'synset': 'wolf.n.01'}, {'name': 'wooden_spoon', 'id': 1194, 'frequency': 'c', 'synset': 'wooden_spoon.n.02'}, {'name': 'wreath', 'id': 1195, 'frequency': 'c', 'synset': 'wreath.n.01'}, {'name': 'wrench', 'id': 1196, 'frequency': 'c', 'synset': 'wrench.n.03'}, {'name': 'wristband', 'id': 1197, 'frequency': 'f', 'synset': 'wristband.n.01'}, {'name': 'wristlet', 'id': 1198, 'frequency': 'f', 'synset': 'wristlet.n.01'}, {'name': 'yacht', 'id': 1199, 'frequency': 'c', 'synset': 'yacht.n.01'}, {'name': 'yogurt', 'id': 1200, 'frequency': 'c', 'synset': 'yogurt.n.01'}, {'name': 'yoke_(animal_equipment)', 'id': 1201, 'frequency': 'c', 'synset': 'yoke.n.07'}, {'name': 'zebra', 'id': 1202, 'frequency': 'f', 'synset': 'zebra.n.01'}, {'name': 'zucchini', 'id': 1203, 'frequency': 'c', 'synset': 'zucchini.n.02'}, {'id': 1204, 'synset': 'organism.n.01', 'name': 'organism'}, {'id': 1205, 'synset': 'benthos.n.02', 'name': 'benthos'}, {'id': 1206, 'synset': 'heterotroph.n.01', 'name': 'heterotroph'}, {'id': 1207, 'synset': 'cell.n.02', 'name': 'cell'}, {'id': 1208, 'synset': 'animal.n.01', 'name': 'animal'}, {'id': 1209, 'synset': 'plant.n.02', 'name': 'plant'}, {'id': 1210, 'synset': 'food.n.01', 'name': 'food'}, {'id': 1211, 'synset': 'artifact.n.01', 'name': 'artifact'}, {'id': 1212, 'synset': 'hop.n.01', 'name': 'hop'}, {'id': 1213, 'synset': 'check-in.n.01', 'name': 'check-in'}, {'id': 1214, 'synset': 'dressage.n.01', 'name': 'dressage'}, {'id': 1215, 'synset': 'curvet.n.01', 'name': 'curvet'}, {'id': 1216, 'synset': 'piaffe.n.01', 'name': 'piaffe'}, {'id': 1217, 'synset': 'funambulism.n.01', 'name': 'funambulism'}, {'id': 1218, 'synset': 'rock_climbing.n.01', 'name': 'rock_climbing'}, {'id': 1219, 'synset': 'contact_sport.n.01', 'name': 'contact_sport'}, {'id': 1220, 'synset': 'outdoor_sport.n.01', 'name': 'outdoor_sport'}, {'id': 1221, 'synset': 'gymnastics.n.01', 'name': 'gymnastics'}, {'id': 1222, 'synset': 'acrobatics.n.01', 'name': 'acrobatics'}, {'id': 1223, 'synset': 'track_and_field.n.01', 'name': 'track_and_field'}, {'id': 1224, 'synset': 'track.n.11', 'name': 'track'}, {'id': 1225, 'synset': 'jumping.n.01', 'name': 'jumping'}, {'id': 1226, 'synset': 'broad_jump.n.02', 'name': 'broad_jump'}, {'id': 1227, 'synset': 'high_jump.n.02', 'name': 'high_jump'}, {'id': 1228, 'synset': 'fosbury_flop.n.01', 'name': 'Fosbury_flop'}, {'id': 1229, 'synset': 'skiing.n.01', 'name': 'skiing'}, {'id': 1230, 'synset': 'cross-country_skiing.n.01', 'name': 'cross-country_skiing'}, {'id': 1231, 'synset': 'ski_jumping.n.01', 'name': 'ski_jumping'}, {'id': 1232, 'synset': 'water_sport.n.01', 'name': 'water_sport'}, {'id': 1233, 'synset': 'swimming.n.01', 'name': 'swimming'}, {'id': 1234, 'synset': 'bathe.n.01', 'name': 'bathe'}, {'id': 1235, 'synset': 'dip.n.08', 'name': 'dip'}, {'id': 1236, 'synset': 'dive.n.02', 'name': 'dive'}, {'id': 1237, 'synset': 'floating.n.01', 'name': 'floating'}, {'id': 1238, 'synset': "dead-man's_float.n.01", 'name': "dead-man's_float"}, {'id': 1239, 'synset': 'belly_flop.n.01', 'name': 'belly_flop'}, {'id': 1240, 'synset': 'cliff_diving.n.01', 'name': 'cliff_diving'}, {'id': 1241, 'synset': 'flip.n.05', 'name': 'flip'}, {'id': 1242, 'synset': 'gainer.n.03', 'name': 'gainer'}, {'id': 1243, 'synset': 'half_gainer.n.01', 'name': 'half_gainer'}, {'id': 1244, 'synset': 'jackknife.n.02', 'name': 'jackknife'}, {'id': 1245, 'synset': 'swan_dive.n.01', 'name': 'swan_dive'}, {'id': 1246, 'synset': 'skin_diving.n.01', 'name': 'skin_diving'}, {'id': 1247, 'synset': 'scuba_diving.n.01', 'name': 'scuba_diving'}, {'id': 1248, 'synset': 'snorkeling.n.01', 'name': 'snorkeling'}, {'id': 1249, 'synset': 'surfing.n.01', 'name': 'surfing'}, {'id': 1250, 'synset': 'water-skiing.n.01', 'name': 'water-skiing'}, {'id': 1251, 'synset': 'rowing.n.01', 'name': 'rowing'}, {'id': 1252, 'synset': 'sculling.n.01', 'name': 'sculling'}, {'id': 1253, 'synset': 'boxing.n.01', 'name': 'boxing'}, {'id': 1254, 'synset': 'professional_boxing.n.01', 'name': 'professional_boxing'}, {'id': 1255, 'synset': 'in-fighting.n.02', 'name': 'in-fighting'}, {'id': 1256, 'synset': 'fight.n.05', 'name': 'fight'}, {'id': 1257, 'synset': 'rope-a-dope.n.01', 'name': 'rope-a-dope'}, {'id': 1258, 'synset': 'spar.n.03', 'name': 'spar'}, {'id': 1259, 'synset': 'archery.n.01', 'name': 'archery'}, {'id': 1260, 'synset': 'sledding.n.01', 'name': 'sledding'}, {'id': 1261, 'synset': 'tobogganing.n.01', 'name': 'tobogganing'}, {'id': 1262, 'synset': 'luging.n.01', 'name': 'luging'}, {'id': 1263, 'synset': 'bobsledding.n.01', 'name': 'bobsledding'}, {'id': 1264, 'synset': 'wrestling.n.02', 'name': 'wrestling'}, {'id': 1265, 'synset': 'greco-roman_wrestling.n.01', 'name': 'Greco-Roman_wrestling'}, {'id': 1266, 'synset': 'professional_wrestling.n.01', 'name': 'professional_wrestling'}, {'id': 1267, 'synset': 'sumo.n.01', 'name': 'sumo'}, {'id': 1268, 'synset': 'skating.n.01', 'name': 'skating'}, {'id': 1269, 'synset': 'ice_skating.n.01', 'name': 'ice_skating'}, {'id': 1270, 'synset': 'figure_skating.n.01', 'name': 'figure_skating'}, {'id': 1271, 'synset': 'rollerblading.n.01', 'name': 'rollerblading'}, {'id': 1272, 'synset': 'roller_skating.n.01', 'name': 'roller_skating'}, {'id': 1273, 'synset': 'skateboarding.n.01', 'name': 'skateboarding'}, {'id': 1274, 'synset': 'speed_skating.n.01', 'name': 'speed_skating'}, {'id': 1275, 'synset': 'racing.n.01', 'name': 'racing'}, {'id': 1276, 'synset': 'auto_racing.n.01', 'name': 'auto_racing'}, {'id': 1277, 'synset': 'boat_racing.n.01', 'name': 'boat_racing'}, {'id': 1278, 'synset': 'hydroplane_racing.n.01', 'name': 'hydroplane_racing'}, {'id': 1279, 'synset': 'camel_racing.n.01', 'name': 'camel_racing'}, {'id': 1280, 'synset': 'greyhound_racing.n.01', 'name': 'greyhound_racing'}, {'id': 1281, 'synset': 'horse_racing.n.01', 'name': 'horse_racing'}, {'id': 1282, 'synset': 'riding.n.01', 'name': 'riding'}, {'id': 1283, 'synset': 'equestrian_sport.n.01', 'name': 'equestrian_sport'}, {'id': 1284, 'synset': 'pony-trekking.n.01', 'name': 'pony-trekking'}, {'id': 1285, 'synset': 'showjumping.n.01', 'name': 'showjumping'}, {'id': 1286, 'synset': 'cross-country_riding.n.01', 'name': 'cross-country_riding'}, {'id': 1287, 'synset': 'cycling.n.01', 'name': 'cycling'}, {'id': 1288, 'synset': 'bicycling.n.01', 'name': 'bicycling'}, {'id': 1289, 'synset': 'motorcycling.n.01', 'name': 'motorcycling'}, {'id': 1290, 'synset': 'dune_cycling.n.01', 'name': 'dune_cycling'}, {'id': 1291, 'synset': 'blood_sport.n.01', 'name': 'blood_sport'}, {'id': 1292, 'synset': 'bullfighting.n.01', 'name': 'bullfighting'}, {'id': 1293, 'synset': 'cockfighting.n.01', 'name': 'cockfighting'}, {'id': 1294, 'synset': 'hunt.n.08', 'name': 'hunt'}, {'id': 1295, 'synset': 'battue.n.01', 'name': 'battue'}, {'id': 1296, 'synset': 'beagling.n.01', 'name': 'beagling'}, {'id': 1297, 'synset': 'coursing.n.01', 'name': 'coursing'}, {'id': 1298, 'synset': 'deer_hunting.n.01', 'name': 'deer_hunting'}, {'id': 1299, 'synset': 'ducking.n.01', 'name': 'ducking'}, {'id': 1300, 'synset': 'fox_hunting.n.01', 'name': 'fox_hunting'}, {'id': 1301, 'synset': 'pigsticking.n.01', 'name': 'pigsticking'}, {'id': 1302, 'synset': 'fishing.n.01', 'name': 'fishing'}, {'id': 1303, 'synset': 'angling.n.01', 'name': 'angling'}, {'id': 1304, 'synset': 'fly-fishing.n.01', 'name': 'fly-fishing'}, {'id': 1305, 'synset': 'troll.n.04', 'name': 'troll'}, {'id': 1306, 'synset': 'casting.n.03', 'name': 'casting'}, {'id': 1307, 'synset': 'bait_casting.n.01', 'name': 'bait_casting'}, {'id': 1308, 'synset': 'fly_casting.n.01', 'name': 'fly_casting'}, {'id': 1309, 'synset': 'overcast.n.04', 'name': 'overcast'}, {'id': 1310, 'synset': 'surf_casting.n.01', 'name': 'surf_casting'}, {'id': 1311, 'synset': 'day_game.n.01', 'name': 'day_game'}, {'id': 1312, 'synset': 'athletic_game.n.01', 'name': 'athletic_game'}, {'id': 1313, 'synset': 'ice_hockey.n.01', 'name': 'ice_hockey'}, {'id': 1314, 'synset': 'tetherball.n.01', 'name': 'tetherball'}, {'id': 1315, 'synset': 'water_polo.n.01', 'name': 'water_polo'}, {'id': 1316, 'synset': 'outdoor_game.n.01', 'name': 'outdoor_game'}, {'id': 1317, 'synset': 'golf.n.01', 'name': 'golf'}, {'id': 1318, 'synset': 'professional_golf.n.01', 'name': 'professional_golf'}, {'id': 1319, 'synset': 'round_of_golf.n.01', 'name': 'round_of_golf'}, {'id': 1320, 'synset': 'medal_play.n.01', 'name': 'medal_play'}, {'id': 1321, 'synset': 'match_play.n.01', 'name': 'match_play'}, {'id': 1322, 'synset': 'miniature_golf.n.01', 'name': 'miniature_golf'}, {'id': 1323, 'synset': 'croquet.n.01', 'name': 'croquet'}, {'id': 1324, 'synset': 'quoits.n.01', 'name': 'quoits'}, {'id': 1325, 'synset': 'shuffleboard.n.01', 'name': 'shuffleboard'}, {'id': 1326, 'synset': 'field_game.n.01', 'name': 'field_game'}, {'id': 1327, 'synset': 'field_hockey.n.01', 'name': 'field_hockey'}, {'id': 1328, 'synset': 'shinny.n.01', 'name': 'shinny'}, {'id': 1329, 'synset': 'football.n.01', 'name': 'football'}, {'id': 1330, 'synset': 'american_football.n.01', 'name': 'American_football'}, {'id': 1331, 'synset': 'professional_football.n.01', 'name': 'professional_football'}, {'id': 1332, 'synset': 'touch_football.n.01', 'name': 'touch_football'}, {'id': 1333, 'synset': 'hurling.n.01', 'name': 'hurling'}, {'id': 1334, 'synset': 'rugby.n.01', 'name': 'rugby'}, {'id': 1335, 'synset': 'ball_game.n.01', 'name': 'ball_game'}, {'id': 1336, 'synset': 'baseball.n.01', 'name': 'baseball'}, {'id': 1337, 'synset': 'ball.n.11', 'name': 'ball'}, {'id': 1338, 'synset': 'professional_baseball.n.01', 'name': 'professional_baseball'}, {'id': 1339, 'synset': 'hardball.n.02', 'name': 'hardball'}, {'id': 1340, 'synset': 'perfect_game.n.01', 'name': 'perfect_game'}, {'id': 1341, 'synset': 'no-hit_game.n.01', 'name': 'no-hit_game'}, {'id': 1342, 'synset': 'one-hitter.n.01', 'name': 'one-hitter'}, {'id': 1343, 'synset': 'two-hitter.n.01', 'name': 'two-hitter'}, {'id': 1344, 'synset': 'three-hitter.n.01', 'name': 'three-hitter'}, {'id': 1345, 'synset': 'four-hitter.n.01', 'name': 'four-hitter'}, {'id': 1346, 'synset': 'five-hitter.n.01', 'name': 'five-hitter'}, {'id': 1347, 'synset': 'softball.n.02', 'name': 'softball'}, {'id': 1348, 'synset': 'rounders.n.01', 'name': 'rounders'}, {'id': 1349, 'synset': 'stickball.n.01', 'name': 'stickball'}, {'id': 1350, 'synset': 'cricket.n.02', 'name': 'cricket'}, {'id': 1351, 'synset': 'lacrosse.n.01', 'name': 'lacrosse'}, {'id': 1352, 'synset': 'polo.n.02', 'name': 'polo'}, {'id': 1353, 'synset': 'pushball.n.01', 'name': 'pushball'}, {'id': 1354, 'synset': 'soccer.n.01', 'name': 'soccer'}, {'id': 1355, 'synset': 'court_game.n.01', 'name': 'court_game'}, {'id': 1356, 'synset': 'handball.n.02', 'name': 'handball'}, {'id': 1357, 'synset': 'racquetball.n.02', 'name': 'racquetball'}, {'id': 1358, 'synset': 'fives.n.01', 'name': 'fives'}, {'id': 1359, 'synset': 'squash.n.03', 'name': 'squash'}, {'id': 1360, 'synset': 'volleyball.n.01', 'name': 'volleyball'}, {'id': 1361, 'synset': 'jai_alai.n.01', 'name': 'jai_alai'}, {'id': 1362, 'synset': 'badminton.n.01', 'name': 'badminton'}, {'id': 1363, 'synset': 'battledore.n.02', 'name': 'battledore'}, {'id': 1364, 'synset': 'basketball.n.01', 'name': 'basketball'}, {'id': 1365, 'synset': 'professional_basketball.n.01', 'name': 'professional_basketball'}, {'id': 1366, 'synset': 'deck_tennis.n.01', 'name': 'deck_tennis'}, {'id': 1367, 'synset': 'netball.n.01', 'name': 'netball'}, {'id': 1368, 'synset': 'tennis.n.01', 'name': 'tennis'}, {'id': 1369, 'synset': 'professional_tennis.n.01', 'name': 'professional_tennis'}, {'id': 1370, 'synset': 'singles.n.02', 'name': 'singles'}, {'id': 1371, 'synset': 'singles.n.01', 'name': 'singles'}, {'id': 1372, 'synset': 'doubles.n.02', 'name': 'doubles'}, {'id': 1373, 'synset': 'doubles.n.01', 'name': 'doubles'}, {'id': 1374, 'synset': 'royal_tennis.n.01', 'name': 'royal_tennis'}, {'id': 1375, 'synset': 'pallone.n.01', 'name': 'pallone'}, {'id': 1376, 'synset': 'sport.n.01', 'name': 'sport'}, {'id': 1377, 'synset': 'clasp.n.02', 'name': 'clasp'}, {'id': 1378, 'synset': 'judo.n.01', 'name': 'judo'}, {'id': 1379, 'synset': 'team_sport.n.01', 'name': 'team_sport'}, {'id': 1380, 'synset': 'last_supper.n.01', 'name': 'Last_Supper'}, {'id': 1381, 'synset': 'seder.n.01', 'name': 'Seder'}, {'id': 1382, 'synset': 'camping.n.01', 'name': 'camping'}, {'id': 1383, 'synset': 'pest.n.04', 'name': 'pest'}, {'id': 1384, 'synset': 'critter.n.01', 'name': 'critter'}, {'id': 1385, 'synset': 'creepy-crawly.n.01', 'name': 'creepy-crawly'}, {'id': 1386, 'synset': 'darter.n.02', 'name': 'darter'}, {'id': 1387, 'synset': 'peeper.n.03', 'name': 'peeper'}, {'id': 1388, 'synset': 'homeotherm.n.01', 'name': 'homeotherm'}, {'id': 1389, 'synset': 'poikilotherm.n.01', 'name': 'poikilotherm'}, {'id': 1390, 'synset': 'range_animal.n.01', 'name': 'range_animal'}, {'id': 1391, 'synset': 'scavenger.n.03', 'name': 'scavenger'}, {'id': 1392, 'synset': 'bottom-feeder.n.02', 'name': 'bottom-feeder'}, {'id': 1393, 'synset': 'bottom-feeder.n.01', 'name': 'bottom-feeder'}, {'id': 1394, 'synset': 'work_animal.n.01', 'name': 'work_animal'}, {'id': 1395, 'synset': 'beast_of_burden.n.01', 'name': 'beast_of_burden'}, {'id': 1396, 'synset': 'draft_animal.n.01', 'name': 'draft_animal'}, {'id': 1397, 'synset': 'pack_animal.n.01', 'name': 'pack_animal'}, {'id': 1398, 'synset': 'domestic_animal.n.01', 'name': 'domestic_animal'}, {'id': 1399, 'synset': 'feeder.n.01', 'name': 'feeder'}, {'id': 1400, 'synset': 'feeder.n.06', 'name': 'feeder'}, {'id': 1401, 'synset': 'stocker.n.01', 'name': 'stocker'}, {'id': 1402, 'synset': 'hatchling.n.01', 'name': 'hatchling'}, {'id': 1403, 'synset': 'head.n.02', 'name': 'head'}, {'id': 1404, 'synset': 'migrator.n.02', 'name': 'migrator'}, {'id': 1405, 'synset': 'molter.n.01', 'name': 'molter'}, {'id': 1406, 'synset': 'stayer.n.01', 'name': 'stayer'}, {'id': 1407, 'synset': 'stunt.n.02', 'name': 'stunt'}, {'id': 1408, 'synset': 'marine_animal.n.01', 'name': 'marine_animal'}, {'id': 1409, 'synset': 'by-catch.n.01', 'name': 'by-catch'}, {'id': 1410, 'synset': 'female.n.01', 'name': 'female'}, {'id': 1411, 'synset': 'hen.n.04', 'name': 'hen'}, {'id': 1412, 'synset': 'male.n.01', 'name': 'male'}, {'id': 1413, 'synset': 'adult.n.02', 'name': 'adult'}, {'id': 1414, 'synset': 'young.n.01', 'name': 'young'}, {'id': 1415, 'synset': 'orphan.n.04', 'name': 'orphan'}, {'id': 1416, 'synset': 'young_mammal.n.01', 'name': 'young_mammal'}, {'id': 1417, 'synset': 'baby.n.06', 'name': 'baby'}, {'id': 1418, 'synset': 'pup.n.01', 'name': 'pup'}, {'id': 1419, 'synset': 'wolf_pup.n.01', 'name': 'wolf_pup'}, {'id': 1420, 'synset': 'lion_cub.n.01', 'name': 'lion_cub'}, {'id': 1421, 'synset': 'bear_cub.n.01', 'name': 'bear_cub'}, {'id': 1422, 'synset': 'tiger_cub.n.01', 'name': 'tiger_cub'}, {'id': 1423, 'synset': 'kit.n.03', 'name': 'kit'}, {'id': 1424, 'synset': 'suckling.n.03', 'name': 'suckling'}, {'id': 1425, 'synset': 'sire.n.03', 'name': 'sire'}, {'id': 1426, 'synset': 'dam.n.03', 'name': 'dam'}, {'id': 1427, 'synset': 'thoroughbred.n.03', 'name': 'thoroughbred'}, {'id': 1428, 'synset': 'giant.n.01', 'name': 'giant'}, {'id': 1429, 'synset': 'mutant.n.02', 'name': 'mutant'}, {'id': 1430, 'synset': 'carnivore.n.02', 'name': 'carnivore'}, {'id': 1431, 'synset': 'herbivore.n.01', 'name': 'herbivore'}, {'id': 1432, 'synset': 'insectivore.n.02', 'name': 'insectivore'}, {'id': 1433, 'synset': 'acrodont.n.01', 'name': 'acrodont'}, {'id': 1434, 'synset': 'pleurodont.n.01', 'name': 'pleurodont'}, {'id': 1435, 'synset': 'microorganism.n.01', 'name': 'microorganism'}, {'id': 1436, 'synset': 'monohybrid.n.01', 'name': 'monohybrid'}, {'id': 1437, 'synset': 'arbovirus.n.01', 'name': 'arbovirus'}, {'id': 1438, 'synset': 'adenovirus.n.01', 'name': 'adenovirus'}, {'id': 1439, 'synset': 'arenavirus.n.01', 'name': 'arenavirus'}, {'id': 1440, 'synset': 'marburg_virus.n.01', 'name': 'Marburg_virus'}, {'id': 1441, 'synset': 'arenaviridae.n.01', 'name': 'Arenaviridae'}, {'id': 1442, 'synset': 'vesiculovirus.n.01', 'name': 'vesiculovirus'}, {'id': 1443, 'synset': 'reoviridae.n.01', 'name': 'Reoviridae'}, {'id': 1444, 'synset': 'variola_major.n.02', 'name': 'variola_major'}, {'id': 1445, 'synset': 'viroid.n.01', 'name': 'viroid'}, {'id': 1446, 'synset': 'coliphage.n.01', 'name': 'coliphage'}, {'id': 1447, 'synset': 'paramyxovirus.n.01', 'name': 'paramyxovirus'}, {'id': 1448, 'synset': 'poliovirus.n.01', 'name': 'poliovirus'}, {'id': 1449, 'synset': 'herpes.n.02', 'name': 'herpes'}, {'id': 1450, 'synset': 'herpes_simplex_1.n.01', 'name': 'herpes_simplex_1'}, {'id': 1451, 'synset': 'herpes_zoster.n.02', 'name': 'herpes_zoster'}, {'id': 1452, 'synset': 'herpes_varicella_zoster.n.01', 'name': 'herpes_varicella_zoster'}, {'id': 1453, 'synset': 'cytomegalovirus.n.01', 'name': 'cytomegalovirus'}, {'id': 1454, 'synset': 'varicella_zoster_virus.n.01', 'name': 'varicella_zoster_virus'}, {'id': 1455, 'synset': 'polyoma.n.01', 'name': 'polyoma'}, {'id': 1456, 'synset': 'lyssavirus.n.01', 'name': 'lyssavirus'}, {'id': 1457, 'synset': 'reovirus.n.01', 'name': 'reovirus'}, {'id': 1458, 'synset': 'rotavirus.n.01', 'name': 'rotavirus'}, {'id': 1459, 'synset': 'moneran.n.01', 'name': 'moneran'}, {'id': 1460, 'synset': 'archaebacteria.n.01', 'name': 'archaebacteria'}, {'id': 1461, 'synset': 'bacteroid.n.01', 'name': 'bacteroid'}, {'id': 1462, 'synset': 'bacillus_anthracis.n.01', 'name': 'Bacillus_anthracis'}, {'id': 1463, 'synset': 'yersinia_pestis.n.01', 'name': 'Yersinia_pestis'}, {'id': 1464, 'synset': 'brucella.n.01', 'name': 'Brucella'}, {'id': 1465, 'synset': 'spirillum.n.02', 'name': 'spirillum'}, {'id': 1466, 'synset': 'botulinus.n.01', 'name': 'botulinus'}, {'id': 1467, 'synset': 'clostridium_perfringens.n.01', 'name': 'clostridium_perfringens'}, {'id': 1468, 'synset': 'cyanobacteria.n.01', 'name': 'cyanobacteria'}, {'id': 1469, 'synset': 'trichodesmium.n.01', 'name': 'trichodesmium'}, {'id': 1470, 'synset': 'nitric_bacteria.n.01', 'name': 'nitric_bacteria'}, {'id': 1471, 'synset': 'spirillum.n.01', 'name': 'spirillum'}, {'id': 1472, 'synset': 'francisella.n.01', 'name': 'Francisella'}, {'id': 1473, 'synset': 'gonococcus.n.01', 'name': 'gonococcus'}, {'id': 1474, 'synset': 'corynebacterium_diphtheriae.n.01', 'name': 'Corynebacterium_diphtheriae'}, {'id': 1475, 'synset': 'enteric_bacteria.n.01', 'name': 'enteric_bacteria'}, {'id': 1476, 'synset': 'klebsiella.n.01', 'name': 'klebsiella'}, {'id': 1477, 'synset': 'salmonella_typhimurium.n.01', 'name': 'Salmonella_typhimurium'}, {'id': 1478, 'synset': 'typhoid_bacillus.n.01', 'name': 'typhoid_bacillus'}, {'id': 1479, 'synset': 'nitrate_bacterium.n.01', 'name': 'nitrate_bacterium'}, {'id': 1480, 'synset': 'nitrite_bacterium.n.01', 'name': 'nitrite_bacterium'}, {'id': 1481, 'synset': 'actinomycete.n.01', 'name': 'actinomycete'}, {'id': 1482, 'synset': 'streptomyces.n.01', 'name': 'streptomyces'}, {'id': 1483, 'synset': 'streptomyces_erythreus.n.01', 'name': 'Streptomyces_erythreus'}, {'id': 1484, 'synset': 'streptomyces_griseus.n.01', 'name': 'Streptomyces_griseus'}, {'id': 1485, 'synset': 'tubercle_bacillus.n.01', 'name': 'tubercle_bacillus'}, {'id': 1486, 'synset': 'pus-forming_bacteria.n.01', 'name': 'pus-forming_bacteria'}, {'id': 1487, 'synset': 'streptobacillus.n.01', 'name': 'streptobacillus'}, {'id': 1488, 'synset': 'myxobacteria.n.01', 'name': 'myxobacteria'}, {'id': 1489, 'synset': 'staphylococcus.n.01', 'name': 'staphylococcus'}, {'id': 1490, 'synset': 'diplococcus.n.01', 'name': 'diplococcus'}, {'id': 1491, 'synset': 'pneumococcus.n.01', 'name': 'pneumococcus'}, {'id': 1492, 'synset': 'streptococcus.n.01', 'name': 'streptococcus'}, {'id': 1493, 'synset': 'spirochete.n.01', 'name': 'spirochete'}, {'id': 1494, 'synset': 'planktonic_algae.n.01', 'name': 'planktonic_algae'}, {'id': 1495, 'synset': 'zooplankton.n.01', 'name': 'zooplankton'}, {'id': 1496, 'synset': 'parasite.n.01', 'name': 'parasite'}, {'id': 1497, 'synset': 'endoparasite.n.01', 'name': 'endoparasite'}, {'id': 1498, 'synset': 'ectoparasite.n.01', 'name': 'ectoparasite'}, {'id': 1499, 'synset': 'pathogen.n.01', 'name': 'pathogen'}, {'id': 1500, 'synset': 'commensal.n.01', 'name': 'commensal'}, {'id': 1501, 'synset': 'myrmecophile.n.01', 'name': 'myrmecophile'}, {'id': 1502, 'synset': 'protoctist.n.01', 'name': 'protoctist'}, {'id': 1503, 'synset': 'protozoan.n.01', 'name': 'protozoan'}, {'id': 1504, 'synset': 'sarcodinian.n.01', 'name': 'sarcodinian'}, {'id': 1505, 'synset': 'heliozoan.n.01', 'name': 'heliozoan'}, {'id': 1506, 'synset': 'endameba.n.01', 'name': 'endameba'}, {'id': 1507, 'synset': 'ameba.n.01', 'name': 'ameba'}, {'id': 1508, 'synset': 'globigerina.n.01', 'name': 'globigerina'}, {'id': 1509, 'synset': 'testacean.n.01', 'name': 'testacean'}, {'id': 1510, 'synset': 'arcella.n.01', 'name': 'arcella'}, {'id': 1511, 'synset': 'difflugia.n.01', 'name': 'difflugia'}, {'id': 1512, 'synset': 'ciliate.n.01', 'name': 'ciliate'}, {'id': 1513, 'synset': 'paramecium.n.01', 'name': 'paramecium'}, {'id': 1514, 'synset': 'stentor.n.03', 'name': 'stentor'}, {'id': 1515, 'synset': 'alga.n.01', 'name': 'alga'}, {'id': 1516, 'synset': 'arame.n.01', 'name': 'arame'}, {'id': 1517, 'synset': 'seagrass.n.01', 'name': 'seagrass'}, {'id': 1518, 'synset': 'golden_algae.n.01', 'name': 'golden_algae'}, {'id': 1519, 'synset': 'yellow-green_algae.n.01', 'name': 'yellow-green_algae'}, {'id': 1520, 'synset': 'brown_algae.n.01', 'name': 'brown_algae'}, {'id': 1521, 'synset': 'kelp.n.01', 'name': 'kelp'}, {'id': 1522, 'synset': 'fucoid.n.02', 'name': 'fucoid'}, {'id': 1523, 'synset': 'fucoid.n.01', 'name': 'fucoid'}, {'id': 1524, 'synset': 'fucus.n.01', 'name': 'fucus'}, {'id': 1525, 'synset': 'bladderwrack.n.01', 'name': 'bladderwrack'}, {'id': 1526, 'synset': 'green_algae.n.01', 'name': 'green_algae'}, {'id': 1527, 'synset': 'pond_scum.n.01', 'name': 'pond_scum'}, {'id': 1528, 'synset': 'chlorella.n.01', 'name': 'chlorella'}, {'id': 1529, 'synset': 'stonewort.n.01', 'name': 'stonewort'}, {'id': 1530, 'synset': 'desmid.n.01', 'name': 'desmid'}, {'id': 1531, 'synset': 'sea_moss.n.02', 'name': 'sea_moss'}, {'id': 1532, 'synset': 'eukaryote.n.01', 'name': 'eukaryote'}, {'id': 1533, 'synset': 'prokaryote.n.01', 'name': 'prokaryote'}, {'id': 1534, 'synset': 'zooid.n.01', 'name': 'zooid'}, {'id': 1535, 'synset': 'leishmania.n.01', 'name': 'Leishmania'}, {'id': 1536, 'synset': 'zoomastigote.n.01', 'name': 'zoomastigote'}, {'id': 1537, 'synset': 'polymastigote.n.01', 'name': 'polymastigote'}, {'id': 1538, 'synset': 'costia.n.01', 'name': 'costia'}, {'id': 1539, 'synset': 'giardia.n.01', 'name': 'giardia'}, {'id': 1540, 'synset': 'cryptomonad.n.01', 'name': 'cryptomonad'}, {'id': 1541, 'synset': 'sporozoan.n.01', 'name': 'sporozoan'}, {'id': 1542, 'synset': 'sporozoite.n.01', 'name': 'sporozoite'}, {'id': 1543, 'synset': 'trophozoite.n.01', 'name': 'trophozoite'}, {'id': 1544, 'synset': 'merozoite.n.01', 'name': 'merozoite'}, {'id': 1545, 'synset': 'coccidium.n.01', 'name': 'coccidium'}, {'id': 1546, 'synset': 'gregarine.n.01', 'name': 'gregarine'}, {'id': 1547, 'synset': 'plasmodium.n.02', 'name': 'plasmodium'}, {'id': 1548, 'synset': 'leucocytozoan.n.01', 'name': 'leucocytozoan'}, {'id': 1549, 'synset': 'microsporidian.n.01', 'name': 'microsporidian'}, {'id': 1550, 'synset': 'ostariophysi.n.01', 'name': 'Ostariophysi'}, {'id': 1551, 'synset': 'cypriniform_fish.n.01', 'name': 'cypriniform_fish'}, {'id': 1552, 'synset': 'loach.n.01', 'name': 'loach'}, {'id': 1553, 'synset': 'cyprinid.n.01', 'name': 'cyprinid'}, {'id': 1554, 'synset': 'carp.n.02', 'name': 'carp'}, {'id': 1555, 'synset': 'domestic_carp.n.01', 'name': 'domestic_carp'}, {'id': 1556, 'synset': 'leather_carp.n.01', 'name': 'leather_carp'}, {'id': 1557, 'synset': 'mirror_carp.n.01', 'name': 'mirror_carp'}, {'id': 1558, 'synset': 'european_bream.n.01', 'name': 'European_bream'}, {'id': 1559, 'synset': 'tench.n.01', 'name': 'tench'}, {'id': 1560, 'synset': 'dace.n.01', 'name': 'dace'}, {'id': 1561, 'synset': 'chub.n.01', 'name': 'chub'}, {'id': 1562, 'synset': 'shiner.n.04', 'name': 'shiner'}, {'id': 1563, 'synset': 'common_shiner.n.01', 'name': 'common_shiner'}, {'id': 1564, 'synset': 'roach.n.05', 'name': 'roach'}, {'id': 1565, 'synset': 'rudd.n.01', 'name': 'rudd'}, {'id': 1566, 'synset': 'minnow.n.01', 'name': 'minnow'}, {'id': 1567, 'synset': 'gudgeon.n.02', 'name': 'gudgeon'}, {'id': 1568, 'synset': 'crucian_carp.n.01', 'name': 'crucian_carp'}, {'id': 1569, 'synset': 'electric_eel.n.01', 'name': 'electric_eel'}, {'id': 1570, 'synset': 'catostomid.n.01', 'name': 'catostomid'}, {'id': 1571, 'synset': 'buffalo_fish.n.01', 'name': 'buffalo_fish'}, {'id': 1572, 'synset': 'black_buffalo.n.01', 'name': 'black_buffalo'}, {'id': 1573, 'synset': 'hog_sucker.n.01', 'name': 'hog_sucker'}, {'id': 1574, 'synset': 'redhorse.n.01', 'name': 'redhorse'}, {'id': 1575, 'synset': 'cyprinodont.n.01', 'name': 'cyprinodont'}, {'id': 1576, 'synset': 'killifish.n.01', 'name': 'killifish'}, {'id': 1577, 'synset': 'mummichog.n.01', 'name': 'mummichog'}, {'id': 1578, 'synset': 'striped_killifish.n.01', 'name': 'striped_killifish'}, {'id': 1579, 'synset': 'rivulus.n.01', 'name': 'rivulus'}, {'id': 1580, 'synset': 'flagfish.n.01', 'name': 'flagfish'}, {'id': 1581, 'synset': 'swordtail.n.01', 'name': 'swordtail'}, {'id': 1582, 'synset': 'guppy.n.01', 'name': 'guppy'}, {'id': 1583, 'synset': 'topminnow.n.01', 'name': 'topminnow'}, {'id': 1584, 'synset': 'mosquitofish.n.01', 'name': 'mosquitofish'}, {'id': 1585, 'synset': 'platy.n.01', 'name': 'platy'}, {'id': 1586, 'synset': 'mollie.n.01', 'name': 'mollie'}, {'id': 1587, 'synset': 'squirrelfish.n.02', 'name': 'squirrelfish'}, {'id': 1588, 'synset': 'reef_squirrelfish.n.01', 'name': 'reef_squirrelfish'}, {'id': 1589, 'synset': 'deepwater_squirrelfish.n.01', 'name': 'deepwater_squirrelfish'}, {'id': 1590, 'synset': 'holocentrus_ascensionis.n.01', 'name': 'Holocentrus_ascensionis'}, {'id': 1591, 'synset': 'soldierfish.n.01', 'name': 'soldierfish'}, {'id': 1592, 'synset': 'anomalops.n.01', 'name': 'anomalops'}, {'id': 1593, 'synset': 'flashlight_fish.n.01', 'name': 'flashlight_fish'}, {'id': 1594, 'synset': 'john_dory.n.01', 'name': 'John_Dory'}, {'id': 1595, 'synset': 'boarfish.n.02', 'name': 'boarfish'}, {'id': 1596, 'synset': 'boarfish.n.01', 'name': 'boarfish'}, {'id': 1597, 'synset': 'cornetfish.n.01', 'name': 'cornetfish'}, {'id': 1598, 'synset': 'stickleback.n.01', 'name': 'stickleback'}, {'id': 1599, 'synset': 'three-spined_stickleback.n.01', 'name': 'three-spined_stickleback'}, {'id': 1600, 'synset': 'ten-spined_stickleback.n.01', 'name': 'ten-spined_stickleback'}, {'id': 1601, 'synset': 'pipefish.n.01', 'name': 'pipefish'}, {'id': 1602, 'synset': 'dwarf_pipefish.n.01', 'name': 'dwarf_pipefish'}, {'id': 1603, 'synset': 'deepwater_pipefish.n.01', 'name': 'deepwater_pipefish'}, {'id': 1604, 'synset': 'snipefish.n.01', 'name': 'snipefish'}, {'id': 1605, 'synset': 'shrimpfish.n.01', 'name': 'shrimpfish'}, {'id': 1606, 'synset': 'trumpetfish.n.01', 'name': 'trumpetfish'}, {'id': 1607, 'synset': 'pellicle.n.01', 'name': 'pellicle'}, {'id': 1608, 'synset': 'embryo.n.02', 'name': 'embryo'}, {'id': 1609, 'synset': 'fetus.n.01', 'name': 'fetus'}, {'id': 1610, 'synset': 'abortus.n.01', 'name': 'abortus'}, {'id': 1611, 'synset': 'spawn.n.01', 'name': 'spawn'}, {'id': 1612, 'synset': 'blastula.n.01', 'name': 'blastula'}, {'id': 1613, 'synset': 'blastocyst.n.01', 'name': 'blastocyst'}, {'id': 1614, 'synset': 'gastrula.n.01', 'name': 'gastrula'}, {'id': 1615, 'synset': 'morula.n.01', 'name': 'morula'}, {'id': 1616, 'synset': 'yolk.n.02', 'name': 'yolk'}, {'id': 1617, 'synset': 'chordate.n.01', 'name': 'chordate'}, {'id': 1618, 'synset': 'cephalochordate.n.01', 'name': 'cephalochordate'}, {'id': 1619, 'synset': 'lancelet.n.01', 'name': 'lancelet'}, {'id': 1620, 'synset': 'tunicate.n.01', 'name': 'tunicate'}, {'id': 1621, 'synset': 'ascidian.n.01', 'name': 'ascidian'}, {'id': 1622, 'synset': 'sea_squirt.n.01', 'name': 'sea_squirt'}, {'id': 1623, 'synset': 'salp.n.01', 'name': 'salp'}, {'id': 1624, 'synset': 'doliolum.n.01', 'name': 'doliolum'}, {'id': 1625, 'synset': 'larvacean.n.01', 'name': 'larvacean'}, {'id': 1626, 'synset': 'appendicularia.n.01', 'name': 'appendicularia'}, {'id': 1627, 'synset': 'ascidian_tadpole.n.01', 'name': 'ascidian_tadpole'}, {'id': 1628, 'synset': 'vertebrate.n.01', 'name': 'vertebrate'}, {'id': 1629, 'synset': 'amniota.n.01', 'name': 'Amniota'}, {'id': 1630, 'synset': 'amniote.n.01', 'name': 'amniote'}, {'id': 1631, 'synset': 'aquatic_vertebrate.n.01', 'name': 'aquatic_vertebrate'}, {'id': 1632, 'synset': 'jawless_vertebrate.n.01', 'name': 'jawless_vertebrate'}, {'id': 1633, 'synset': 'ostracoderm.n.01', 'name': 'ostracoderm'}, {'id': 1634, 'synset': 'heterostracan.n.01', 'name': 'heterostracan'}, {'id': 1635, 'synset': 'anaspid.n.01', 'name': 'anaspid'}, {'id': 1636, 'synset': 'conodont.n.02', 'name': 'conodont'}, {'id': 1637, 'synset': 'cyclostome.n.01', 'name': 'cyclostome'}, {'id': 1638, 'synset': 'lamprey.n.01', 'name': 'lamprey'}, {'id': 1639, 'synset': 'sea_lamprey.n.01', 'name': 'sea_lamprey'}, {'id': 1640, 'synset': 'hagfish.n.01', 'name': 'hagfish'}, {'id': 1641, 'synset': 'myxine_glutinosa.n.01', 'name': 'Myxine_glutinosa'}, {'id': 1642, 'synset': 'eptatretus.n.01', 'name': 'eptatretus'}, {'id': 1643, 'synset': 'gnathostome.n.01', 'name': 'gnathostome'}, {'id': 1644, 'synset': 'placoderm.n.01', 'name': 'placoderm'}, {'id': 1645, 'synset': 'cartilaginous_fish.n.01', 'name': 'cartilaginous_fish'}, {'id': 1646, 'synset': 'holocephalan.n.01', 'name': 'holocephalan'}, {'id': 1647, 'synset': 'chimaera.n.03', 'name': 'chimaera'}, {'id': 1648, 'synset': 'rabbitfish.n.01', 'name': 'rabbitfish'}, {'id': 1649, 'synset': 'elasmobranch.n.01', 'name': 'elasmobranch'}, {'id': 1650, 'synset': 'cow_shark.n.01', 'name': 'cow_shark'}, {'id': 1651, 'synset': 'mackerel_shark.n.01', 'name': 'mackerel_shark'}, {'id': 1652, 'synset': 'porbeagle.n.01', 'name': 'porbeagle'}, {'id': 1653, 'synset': 'mako.n.01', 'name': 'mako'}, {'id': 1654, 'synset': 'shortfin_mako.n.01', 'name': 'shortfin_mako'}, {'id': 1655, 'synset': 'longfin_mako.n.01', 'name': 'longfin_mako'}, {'id': 1656, 'synset': 'bonito_shark.n.01', 'name': 'bonito_shark'}, {'id': 1657, 'synset': 'great_white_shark.n.01', 'name': 'great_white_shark'}, {'id': 1658, 'synset': 'basking_shark.n.01', 'name': 'basking_shark'}, {'id': 1659, 'synset': 'thresher.n.02', 'name': 'thresher'}, {'id': 1660, 'synset': 'carpet_shark.n.01', 'name': 'carpet_shark'}, {'id': 1661, 'synset': 'nurse_shark.n.01', 'name': 'nurse_shark'}, {'id': 1662, 'synset': 'sand_tiger.n.01', 'name': 'sand_tiger'}, {'id': 1663, 'synset': 'whale_shark.n.01', 'name': 'whale_shark'}, {'id': 1664, 'synset': 'requiem_shark.n.01', 'name': 'requiem_shark'}, {'id': 1665, 'synset': 'bull_shark.n.01', 'name': 'bull_shark'}, {'id': 1666, 'synset': 'sandbar_shark.n.02', 'name': 'sandbar_shark'}, {'id': 1667, 'synset': 'blacktip_shark.n.01', 'name': 'blacktip_shark'}, {'id': 1668, 'synset': 'whitetip_shark.n.02', 'name': 'whitetip_shark'}, {'id': 1669, 'synset': 'dusky_shark.n.01', 'name': 'dusky_shark'}, {'id': 1670, 'synset': 'lemon_shark.n.01', 'name': 'lemon_shark'}, {'id': 1671, 'synset': 'blue_shark.n.01', 'name': 'blue_shark'}, {'id': 1672, 'synset': 'tiger_shark.n.01', 'name': 'tiger_shark'}, {'id': 1673, 'synset': 'soupfin_shark.n.01', 'name': 'soupfin_shark'}, {'id': 1674, 'synset': 'dogfish.n.02', 'name': 'dogfish'}, {'id': 1675, 'synset': 'smooth_dogfish.n.01', 'name': 'smooth_dogfish'}, {'id': 1676, 'synset': 'smoothhound.n.01', 'name': 'smoothhound'}, {'id': 1677, 'synset': 'american_smooth_dogfish.n.01', 'name': 'American_smooth_dogfish'}, {'id': 1678, 'synset': 'florida_smoothhound.n.01', 'name': 'Florida_smoothhound'}, {'id': 1679, 'synset': 'whitetip_shark.n.01', 'name': 'whitetip_shark'}, {'id': 1680, 'synset': 'spiny_dogfish.n.01', 'name': 'spiny_dogfish'}, {'id': 1681, 'synset': 'atlantic_spiny_dogfish.n.01', 'name': 'Atlantic_spiny_dogfish'}, {'id': 1682, 'synset': 'pacific_spiny_dogfish.n.01', 'name': 'Pacific_spiny_dogfish'}, {'id': 1683, 'synset': 'hammerhead.n.03', 'name': 'hammerhead'}, {'id': 1684, 'synset': 'smooth_hammerhead.n.01', 'name': 'smooth_hammerhead'}, {'id': 1685, 'synset': 'smalleye_hammerhead.n.01', 'name': 'smalleye_hammerhead'}, {'id': 1686, 'synset': 'shovelhead.n.01', 'name': 'shovelhead'}, {'id': 1687, 'synset': 'angel_shark.n.01', 'name': 'angel_shark'}, {'id': 1688, 'synset': 'ray.n.07', 'name': 'ray'}, {'id': 1689, 'synset': 'electric_ray.n.01', 'name': 'electric_ray'}, {'id': 1690, 'synset': 'sawfish.n.01', 'name': 'sawfish'}, {'id': 1691, 'synset': 'smalltooth_sawfish.n.01', 'name': 'smalltooth_sawfish'}, {'id': 1692, 'synset': 'guitarfish.n.01', 'name': 'guitarfish'}, {'id': 1693, 'synset': 'stingray.n.01', 'name': 'stingray'}, {'id': 1694, 'synset': 'roughtail_stingray.n.01', 'name': 'roughtail_stingray'}, {'id': 1695, 'synset': 'butterfly_ray.n.01', 'name': 'butterfly_ray'}, {'id': 1696, 'synset': 'eagle_ray.n.01', 'name': 'eagle_ray'}, {'id': 1697, 'synset': 'spotted_eagle_ray.n.01', 'name': 'spotted_eagle_ray'}, {'id': 1698, 'synset': 'cownose_ray.n.01', 'name': 'cownose_ray'}, {'id': 1699, 'synset': 'manta.n.02', 'name': 'manta'}, {'id': 1700, 'synset': 'atlantic_manta.n.01', 'name': 'Atlantic_manta'}, {'id': 1701, 'synset': 'devil_ray.n.01', 'name': 'devil_ray'}, {'id': 1702, 'synset': 'skate.n.02', 'name': 'skate'}, {'id': 1703, 'synset': 'grey_skate.n.01', 'name': 'grey_skate'}, {'id': 1704, 'synset': 'little_skate.n.01', 'name': 'little_skate'}, {'id': 1705, 'synset': 'thorny_skate.n.01', 'name': 'thorny_skate'}, {'id': 1706, 'synset': 'barndoor_skate.n.01', 'name': 'barndoor_skate'}, {'id': 1707, 'synset': 'dickeybird.n.01', 'name': 'dickeybird'}, {'id': 1708, 'synset': 'fledgling.n.02', 'name': 'fledgling'}, {'id': 1709, 'synset': 'nestling.n.01', 'name': 'nestling'}, {'id': 1710, 'synset': 'cock.n.05', 'name': 'cock'}, {'id': 1711, 'synset': 'gamecock.n.01', 'name': 'gamecock'}, {'id': 1712, 'synset': 'hen.n.02', 'name': 'hen'}, {'id': 1713, 'synset': 'nester.n.02', 'name': 'nester'}, {'id': 1714, 'synset': 'night_bird.n.01', 'name': 'night_bird'}, {'id': 1715, 'synset': 'night_raven.n.02', 'name': 'night_raven'}, {'id': 1716, 'synset': 'bird_of_passage.n.02', 'name': 'bird_of_passage'}, {'id': 1717, 'synset': 'archaeopteryx.n.01', 'name': 'archaeopteryx'}, {'id': 1718, 'synset': 'archaeornis.n.01', 'name': 'archaeornis'}, {'id': 1719, 'synset': 'ratite.n.01', 'name': 'ratite'}, {'id': 1720, 'synset': 'carinate.n.01', 'name': 'carinate'}, {'id': 1721, 'synset': 'cassowary.n.01', 'name': 'cassowary'}, {'id': 1722, 'synset': 'emu.n.02', 'name': 'emu'}, {'id': 1723, 'synset': 'kiwi.n.04', 'name': 'kiwi'}, {'id': 1724, 'synset': 'rhea.n.03', 'name': 'rhea'}, {'id': 1725, 'synset': 'rhea.n.02', 'name': 'rhea'}, {'id': 1726, 'synset': 'elephant_bird.n.01', 'name': 'elephant_bird'}, {'id': 1727, 'synset': 'moa.n.01', 'name': 'moa'}, {'id': 1728, 'synset': 'passerine.n.01', 'name': 'passerine'}, {'id': 1729, 'synset': 'nonpasserine_bird.n.01', 'name': 'nonpasserine_bird'}, {'id': 1730, 'synset': 'oscine.n.01', 'name': 'oscine'}, {'id': 1731, 'synset': 'songbird.n.01', 'name': 'songbird'}, {'id': 1732, 'synset': 'honey_eater.n.01', 'name': 'honey_eater'}, {'id': 1733, 'synset': 'accentor.n.01', 'name': 'accentor'}, {'id': 1734, 'synset': 'hedge_sparrow.n.01', 'name': 'hedge_sparrow'}, {'id': 1735, 'synset': 'lark.n.03', 'name': 'lark'}, {'id': 1736, 'synset': 'skylark.n.01', 'name': 'skylark'}, {'id': 1737, 'synset': 'wagtail.n.01', 'name': 'wagtail'}, {'id': 1738, 'synset': 'pipit.n.01', 'name': 'pipit'}, {'id': 1739, 'synset': 'meadow_pipit.n.01', 'name': 'meadow_pipit'}, {'id': 1740, 'synset': 'finch.n.01', 'name': 'finch'}, {'id': 1741, 'synset': 'chaffinch.n.01', 'name': 'chaffinch'}, {'id': 1742, 'synset': 'brambling.n.01', 'name': 'brambling'}, {'id': 1743, 'synset': 'goldfinch.n.02', 'name': 'goldfinch'}, {'id': 1744, 'synset': 'linnet.n.02', 'name': 'linnet'}, {'id': 1745, 'synset': 'siskin.n.01', 'name': 'siskin'}, {'id': 1746, 'synset': 'red_siskin.n.01', 'name': 'red_siskin'}, {'id': 1747, 'synset': 'redpoll.n.02', 'name': 'redpoll'}, {'id': 1748, 'synset': 'redpoll.n.01', 'name': 'redpoll'}, {'id': 1749, 'synset': 'new_world_goldfinch.n.01', 'name': 'New_World_goldfinch'}, {'id': 1750, 'synset': 'pine_siskin.n.01', 'name': 'pine_siskin'}, {'id': 1751, 'synset': 'house_finch.n.01', 'name': 'house_finch'}, {'id': 1752, 'synset': 'purple_finch.n.01', 'name': 'purple_finch'}, {'id': 1753, 'synset': 'canary.n.04', 'name': 'canary'}, {'id': 1754, 'synset': 'common_canary.n.01', 'name': 'common_canary'}, {'id': 1755, 'synset': 'serin.n.01', 'name': 'serin'}, {'id': 1756, 'synset': 'crossbill.n.01', 'name': 'crossbill'}, {'id': 1757, 'synset': 'bullfinch.n.02', 'name': 'bullfinch'}, {'id': 1758, 'synset': 'junco.n.01', 'name': 'junco'}, {'id': 1759, 'synset': 'dark-eyed_junco.n.01', 'name': 'dark-eyed_junco'}, {'id': 1760, 'synset': 'new_world_sparrow.n.01', 'name': 'New_World_sparrow'}, {'id': 1761, 'synset': 'vesper_sparrow.n.01', 'name': 'vesper_sparrow'}, {'id': 1762, 'synset': 'white-throated_sparrow.n.01', 'name': 'white-throated_sparrow'}, {'id': 1763, 'synset': 'white-crowned_sparrow.n.01', 'name': 'white-crowned_sparrow'}, {'id': 1764, 'synset': 'chipping_sparrow.n.01', 'name': 'chipping_sparrow'}, {'id': 1765, 'synset': 'field_sparrow.n.01', 'name': 'field_sparrow'}, {'id': 1766, 'synset': 'tree_sparrow.n.02', 'name': 'tree_sparrow'}, {'id': 1767, 'synset': 'song_sparrow.n.01', 'name': 'song_sparrow'}, {'id': 1768, 'synset': 'swamp_sparrow.n.01', 'name': 'swamp_sparrow'}, {'id': 1769, 'synset': 'bunting.n.02', 'name': 'bunting'}, {'id': 1770, 'synset': 'indigo_bunting.n.01', 'name': 'indigo_bunting'}, {'id': 1771, 'synset': 'ortolan.n.01', 'name': 'ortolan'}, {'id': 1772, 'synset': 'reed_bunting.n.01', 'name': 'reed_bunting'}, {'id': 1773, 'synset': 'yellowhammer.n.02', 'name': 'yellowhammer'}, {'id': 1774, 'synset': 'yellow-breasted_bunting.n.01', 'name': 'yellow-breasted_bunting'}, {'id': 1775, 'synset': 'snow_bunting.n.01', 'name': 'snow_bunting'}, {'id': 1776, 'synset': 'honeycreeper.n.02', 'name': 'honeycreeper'}, {'id': 1777, 'synset': 'banana_quit.n.01', 'name': 'banana_quit'}, {'id': 1778, 'synset': 'sparrow.n.01', 'name': 'sparrow'}, {'id': 1779, 'synset': 'english_sparrow.n.01', 'name': 'English_sparrow'}, {'id': 1780, 'synset': 'tree_sparrow.n.01', 'name': 'tree_sparrow'}, {'id': 1781, 'synset': 'grosbeak.n.01', 'name': 'grosbeak'}, {'id': 1782, 'synset': 'evening_grosbeak.n.01', 'name': 'evening_grosbeak'}, {'id': 1783, 'synset': 'hawfinch.n.01', 'name': 'hawfinch'}, {'id': 1784, 'synset': 'pine_grosbeak.n.01', 'name': 'pine_grosbeak'}, {'id': 1785, 'synset': 'cardinal.n.04', 'name': 'cardinal'}, {'id': 1786, 'synset': 'pyrrhuloxia.n.01', 'name': 'pyrrhuloxia'}, {'id': 1787, 'synset': 'towhee.n.01', 'name': 'towhee'}, {'id': 1788, 'synset': 'chewink.n.01', 'name': 'chewink'}, {'id': 1789, 'synset': 'green-tailed_towhee.n.01', 'name': 'green-tailed_towhee'}, {'id': 1790, 'synset': 'weaver.n.02', 'name': 'weaver'}, {'id': 1791, 'synset': 'baya.n.01', 'name': 'baya'}, {'id': 1792, 'synset': 'whydah.n.01', 'name': 'whydah'}, {'id': 1793, 'synset': 'java_sparrow.n.01', 'name': 'Java_sparrow'}, {'id': 1794, 'synset': 'avadavat.n.01', 'name': 'avadavat'}, {'id': 1795, 'synset': 'grassfinch.n.01', 'name': 'grassfinch'}, {'id': 1796, 'synset': 'zebra_finch.n.01', 'name': 'zebra_finch'}, {'id': 1797, 'synset': 'honeycreeper.n.01', 'name': 'honeycreeper'}, {'id': 1798, 'synset': 'lyrebird.n.01', 'name': 'lyrebird'}, {'id': 1799, 'synset': 'scrubbird.n.01', 'name': 'scrubbird'}, {'id': 1800, 'synset': 'broadbill.n.04', 'name': 'broadbill'}, {'id': 1801, 'synset': 'tyrannid.n.01', 'name': 'tyrannid'}, {'id': 1802, 'synset': 'new_world_flycatcher.n.01', 'name': 'New_World_flycatcher'}, {'id': 1803, 'synset': 'kingbird.n.01', 'name': 'kingbird'}, {'id': 1804, 'synset': 'arkansas_kingbird.n.01', 'name': 'Arkansas_kingbird'}, {'id': 1805, 'synset': "cassin's_kingbird.n.01", 'name': "Cassin's_kingbird"}, {'id': 1806, 'synset': 'eastern_kingbird.n.01', 'name': 'eastern_kingbird'}, {'id': 1807, 'synset': 'grey_kingbird.n.01', 'name': 'grey_kingbird'}, {'id': 1808, 'synset': 'pewee.n.01', 'name': 'pewee'}, {'id': 1809, 'synset': 'western_wood_pewee.n.01', 'name': 'western_wood_pewee'}, {'id': 1810, 'synset': 'phoebe.n.03', 'name': 'phoebe'}, {'id': 1811, 'synset': 'vermillion_flycatcher.n.01', 'name': 'vermillion_flycatcher'}, {'id': 1812, 'synset': 'cotinga.n.01', 'name': 'cotinga'}, {'id': 1813, 'synset': 'cock_of_the_rock.n.02', 'name': 'cock_of_the_rock'}, {'id': 1814, 'synset': 'cock_of_the_rock.n.01', 'name': 'cock_of_the_rock'}, {'id': 1815, 'synset': 'manakin.n.03', 'name': 'manakin'}, {'id': 1816, 'synset': 'bellbird.n.01', 'name': 'bellbird'}, {'id': 1817, 'synset': 'umbrella_bird.n.01', 'name': 'umbrella_bird'}, {'id': 1818, 'synset': 'ovenbird.n.02', 'name': 'ovenbird'}, {'id': 1819, 'synset': 'antbird.n.01', 'name': 'antbird'}, {'id': 1820, 'synset': 'ant_thrush.n.01', 'name': 'ant_thrush'}, {'id': 1821, 'synset': 'ant_shrike.n.01', 'name': 'ant_shrike'}, {'id': 1822, 'synset': 'spotted_antbird.n.01', 'name': 'spotted_antbird'}, {'id': 1823, 'synset': 'woodhewer.n.01', 'name': 'woodhewer'}, {'id': 1824, 'synset': 'pitta.n.01', 'name': 'pitta'}, {'id': 1825, 'synset': 'scissortail.n.01', 'name': 'scissortail'}, {'id': 1826, 'synset': 'old_world_flycatcher.n.01', 'name': 'Old_World_flycatcher'}, {'id': 1827, 'synset': 'spotted_flycatcher.n.01', 'name': 'spotted_flycatcher'}, {'id': 1828, 'synset': 'thickhead.n.01', 'name': 'thickhead'}, {'id': 1829, 'synset': 'thrush.n.03', 'name': 'thrush'}, {'id': 1830, 'synset': 'missel_thrush.n.01', 'name': 'missel_thrush'}, {'id': 1831, 'synset': 'song_thrush.n.01', 'name': 'song_thrush'}, {'id': 1832, 'synset': 'fieldfare.n.01', 'name': 'fieldfare'}, {'id': 1833, 'synset': 'redwing.n.02', 'name': 'redwing'}, {'id': 1834, 'synset': 'blackbird.n.02', 'name': 'blackbird'}, {'id': 1835, 'synset': 'ring_ouzel.n.01', 'name': 'ring_ouzel'}, {'id': 1836, 'synset': 'robin.n.02', 'name': 'robin'}, {'id': 1837, 'synset': 'clay-colored_robin.n.01', 'name': 'clay-colored_robin'}, {'id': 1838, 'synset': 'hermit_thrush.n.01', 'name': 'hermit_thrush'}, {'id': 1839, 'synset': 'veery.n.01', 'name': 'veery'}, {'id': 1840, 'synset': 'wood_thrush.n.01', 'name': 'wood_thrush'}, {'id': 1841, 'synset': 'nightingale.n.01', 'name': 'nightingale'}, {'id': 1842, 'synset': 'thrush_nightingale.n.01', 'name': 'thrush_nightingale'}, {'id': 1843, 'synset': 'bulbul.n.01', 'name': 'bulbul'}, {'id': 1844, 'synset': 'old_world_chat.n.01', 'name': 'Old_World_chat'}, {'id': 1845, 'synset': 'stonechat.n.01', 'name': 'stonechat'}, {'id': 1846, 'synset': 'whinchat.n.01', 'name': 'whinchat'}, {'id': 1847, 'synset': 'solitaire.n.03', 'name': 'solitaire'}, {'id': 1848, 'synset': 'redstart.n.02', 'name': 'redstart'}, {'id': 1849, 'synset': 'wheatear.n.01', 'name': 'wheatear'}, {'id': 1850, 'synset': 'bluebird.n.02', 'name': 'bluebird'}, {'id': 1851, 'synset': 'robin.n.01', 'name': 'robin'}, {'id': 1852, 'synset': 'bluethroat.n.01', 'name': 'bluethroat'}, {'id': 1853, 'synset': 'warbler.n.02', 'name': 'warbler'}, {'id': 1854, 'synset': 'gnatcatcher.n.01', 'name': 'gnatcatcher'}, {'id': 1855, 'synset': 'kinglet.n.01', 'name': 'kinglet'}, {'id': 1856, 'synset': 'goldcrest.n.01', 'name': 'goldcrest'}, {'id': 1857, 'synset': 'gold-crowned_kinglet.n.01', 'name': 'gold-crowned_kinglet'}, {'id': 1858, 'synset': 'ruby-crowned_kinglet.n.01', 'name': 'ruby-crowned_kinglet'}, {'id': 1859, 'synset': 'old_world_warbler.n.01', 'name': 'Old_World_warbler'}, {'id': 1860, 'synset': 'blackcap.n.04', 'name': 'blackcap'}, {'id': 1861, 'synset': 'greater_whitethroat.n.01', 'name': 'greater_whitethroat'}, {'id': 1862, 'synset': 'lesser_whitethroat.n.01', 'name': 'lesser_whitethroat'}, {'id': 1863, 'synset': 'wood_warbler.n.02', 'name': 'wood_warbler'}, {'id': 1864, 'synset': 'sedge_warbler.n.01', 'name': 'sedge_warbler'}, {'id': 1865, 'synset': 'wren_warbler.n.01', 'name': 'wren_warbler'}, {'id': 1866, 'synset': 'tailorbird.n.01', 'name': 'tailorbird'}, {'id': 1867, 'synset': 'babbler.n.02', 'name': 'babbler'}, {'id': 1868, 'synset': 'new_world_warbler.n.01', 'name': 'New_World_warbler'}, {'id': 1869, 'synset': 'parula_warbler.n.01', 'name': 'parula_warbler'}, {'id': 1870, 'synset': "wilson's_warbler.n.01", 'name': "Wilson's_warbler"}, {'id': 1871, 'synset': 'flycatching_warbler.n.01', 'name': 'flycatching_warbler'}, {'id': 1872, 'synset': 'american_redstart.n.01', 'name': 'American_redstart'}, {'id': 1873, 'synset': 'cape_may_warbler.n.01', 'name': 'Cape_May_warbler'}, {'id': 1874, 'synset': 'yellow_warbler.n.01', 'name': 'yellow_warbler'}, {'id': 1875, 'synset': 'blackburn.n.01', 'name': 'Blackburn'}, {'id': 1876, 'synset': "audubon's_warbler.n.01", 'name': "Audubon's_warbler"}, {'id': 1877, 'synset': 'myrtle_warbler.n.01', 'name': 'myrtle_warbler'}, {'id': 1878, 'synset': 'blackpoll.n.01', 'name': 'blackpoll'}, {'id': 1879, 'synset': 'new_world_chat.n.01', 'name': 'New_World_chat'}, {'id': 1880, 'synset': 'yellow-breasted_chat.n.01', 'name': 'yellow-breasted_chat'}, {'id': 1881, 'synset': 'ovenbird.n.01', 'name': 'ovenbird'}, {'id': 1882, 'synset': 'water_thrush.n.01', 'name': 'water_thrush'}, {'id': 1883, 'synset': 'yellowthroat.n.01', 'name': 'yellowthroat'}, {'id': 1884, 'synset': 'common_yellowthroat.n.01', 'name': 'common_yellowthroat'}, {'id': 1885, 'synset': 'riflebird.n.01', 'name': 'riflebird'}, {'id': 1886, 'synset': 'new_world_oriole.n.01', 'name': 'New_World_oriole'}, {'id': 1887, 'synset': 'northern_oriole.n.01', 'name': 'northern_oriole'}, {'id': 1888, 'synset': 'baltimore_oriole.n.01', 'name': 'Baltimore_oriole'}, {'id': 1889, 'synset': "bullock's_oriole.n.01", 'name': "Bullock's_oriole"}, {'id': 1890, 'synset': 'orchard_oriole.n.01', 'name': 'orchard_oriole'}, {'id': 1891, 'synset': 'meadowlark.n.01', 'name': 'meadowlark'}, {'id': 1892, 'synset': 'eastern_meadowlark.n.01', 'name': 'eastern_meadowlark'}, {'id': 1893, 'synset': 'western_meadowlark.n.01', 'name': 'western_meadowlark'}, {'id': 1894, 'synset': 'cacique.n.01', 'name': 'cacique'}, {'id': 1895, 'synset': 'bobolink.n.01', 'name': 'bobolink'}, {'id': 1896, 'synset': 'new_world_blackbird.n.01', 'name': 'New_World_blackbird'}, {'id': 1897, 'synset': 'grackle.n.02', 'name': 'grackle'}, {'id': 1898, 'synset': 'purple_grackle.n.01', 'name': 'purple_grackle'}, {'id': 1899, 'synset': 'rusty_blackbird.n.01', 'name': 'rusty_blackbird'}, {'id': 1900, 'synset': 'cowbird.n.01', 'name': 'cowbird'}, {'id': 1901, 'synset': 'red-winged_blackbird.n.01', 'name': 'red-winged_blackbird'}, {'id': 1902, 'synset': 'old_world_oriole.n.01', 'name': 'Old_World_oriole'}, {'id': 1903, 'synset': 'golden_oriole.n.01', 'name': 'golden_oriole'}, {'id': 1904, 'synset': 'fig-bird.n.01', 'name': 'fig-bird'}, {'id': 1905, 'synset': 'starling.n.01', 'name': 'starling'}, {'id': 1906, 'synset': 'common_starling.n.01', 'name': 'common_starling'}, {'id': 1907, 'synset': 'rose-colored_starling.n.01', 'name': 'rose-colored_starling'}, {'id': 1908, 'synset': 'myna.n.01', 'name': 'myna'}, {'id': 1909, 'synset': 'crested_myna.n.01', 'name': 'crested_myna'}, {'id': 1910, 'synset': 'hill_myna.n.01', 'name': 'hill_myna'}, {'id': 1911, 'synset': 'corvine_bird.n.01', 'name': 'corvine_bird'}, {'id': 1912, 'synset': 'american_crow.n.01', 'name': 'American_crow'}, {'id': 1913, 'synset': 'raven.n.01', 'name': 'raven'}, {'id': 1914, 'synset': 'rook.n.02', 'name': 'rook'}, {'id': 1915, 'synset': 'jackdaw.n.01', 'name': 'jackdaw'}, {'id': 1916, 'synset': 'chough.n.01', 'name': 'chough'}, {'id': 1917, 'synset': 'jay.n.02', 'name': 'jay'}, {'id': 1918, 'synset': 'old_world_jay.n.01', 'name': 'Old_World_jay'}, {'id': 1919, 'synset': 'common_european_jay.n.01', 'name': 'common_European_jay'}, {'id': 1920, 'synset': 'new_world_jay.n.01', 'name': 'New_World_jay'}, {'id': 1921, 'synset': 'blue_jay.n.01', 'name': 'blue_jay'}, {'id': 1922, 'synset': 'canada_jay.n.01', 'name': 'Canada_jay'}, {'id': 1923, 'synset': 'rocky_mountain_jay.n.01', 'name': 'Rocky_Mountain_jay'}, {'id': 1924, 'synset': 'nutcracker.n.03', 'name': 'nutcracker'}, {'id': 1925, 'synset': 'common_nutcracker.n.01', 'name': 'common_nutcracker'}, {'id': 1926, 'synset': "clark's_nutcracker.n.01", 'name': "Clark's_nutcracker"}, {'id': 1927, 'synset': 'magpie.n.01', 'name': 'magpie'}, {'id': 1928, 'synset': 'european_magpie.n.01', 'name': 'European_magpie'}, {'id': 1929, 'synset': 'american_magpie.n.01', 'name': 'American_magpie'}, {'id': 1930, 'synset': 'australian_magpie.n.01', 'name': 'Australian_magpie'}, {'id': 1931, 'synset': 'butcherbird.n.02', 'name': 'butcherbird'}, {'id': 1932, 'synset': 'currawong.n.01', 'name': 'currawong'}, {'id': 1933, 'synset': 'piping_crow.n.01', 'name': 'piping_crow'}, {'id': 1934, 'synset': 'wren.n.02', 'name': 'wren'}, {'id': 1935, 'synset': 'winter_wren.n.01', 'name': 'winter_wren'}, {'id': 1936, 'synset': 'house_wren.n.01', 'name': 'house_wren'}, {'id': 1937, 'synset': 'marsh_wren.n.01', 'name': 'marsh_wren'}, {'id': 1938, 'synset': 'long-billed_marsh_wren.n.01', 'name': 'long-billed_marsh_wren'}, {'id': 1939, 'synset': 'sedge_wren.n.01', 'name': 'sedge_wren'}, {'id': 1940, 'synset': 'rock_wren.n.02', 'name': 'rock_wren'}, {'id': 1941, 'synset': 'carolina_wren.n.01', 'name': 'Carolina_wren'}, {'id': 1942, 'synset': 'cactus_wren.n.01', 'name': 'cactus_wren'}, {'id': 1943, 'synset': 'mockingbird.n.01', 'name': 'mockingbird'}, {'id': 1944, 'synset': 'blue_mockingbird.n.01', 'name': 'blue_mockingbird'}, {'id': 1945, 'synset': 'catbird.n.02', 'name': 'catbird'}, {'id': 1946, 'synset': 'thrasher.n.02', 'name': 'thrasher'}, {'id': 1947, 'synset': 'brown_thrasher.n.01', 'name': 'brown_thrasher'}, {'id': 1948, 'synset': 'new_zealand_wren.n.01', 'name': 'New_Zealand_wren'}, {'id': 1949, 'synset': 'rock_wren.n.01', 'name': 'rock_wren'}, {'id': 1950, 'synset': 'rifleman_bird.n.01', 'name': 'rifleman_bird'}, {'id': 1951, 'synset': 'creeper.n.03', 'name': 'creeper'}, {'id': 1952, 'synset': 'brown_creeper.n.01', 'name': 'brown_creeper'}, {'id': 1953, 'synset': 'european_creeper.n.01', 'name': 'European_creeper'}, {'id': 1954, 'synset': 'wall_creeper.n.01', 'name': 'wall_creeper'}, {'id': 1955, 'synset': 'european_nuthatch.n.01', 'name': 'European_nuthatch'}, {'id': 1956, 'synset': 'red-breasted_nuthatch.n.01', 'name': 'red-breasted_nuthatch'}, {'id': 1957, 'synset': 'white-breasted_nuthatch.n.01', 'name': 'white-breasted_nuthatch'}, {'id': 1958, 'synset': 'titmouse.n.01', 'name': 'titmouse'}, {'id': 1959, 'synset': 'chickadee.n.01', 'name': 'chickadee'}, {'id': 1960, 'synset': 'black-capped_chickadee.n.01', 'name': 'black-capped_chickadee'}, {'id': 1961, 'synset': 'tufted_titmouse.n.01', 'name': 'tufted_titmouse'}, {'id': 1962, 'synset': 'carolina_chickadee.n.01', 'name': 'Carolina_chickadee'}, {'id': 1963, 'synset': 'blue_tit.n.01', 'name': 'blue_tit'}, {'id': 1964, 'synset': 'bushtit.n.01', 'name': 'bushtit'}, {'id': 1965, 'synset': 'wren-tit.n.01', 'name': 'wren-tit'}, {'id': 1966, 'synset': 'verdin.n.01', 'name': 'verdin'}, {'id': 1967, 'synset': 'fairy_bluebird.n.01', 'name': 'fairy_bluebird'}, {'id': 1968, 'synset': 'swallow.n.03', 'name': 'swallow'}, {'id': 1969, 'synset': 'barn_swallow.n.01', 'name': 'barn_swallow'}, {'id': 1970, 'synset': 'cliff_swallow.n.01', 'name': 'cliff_swallow'}, {'id': 1971, 'synset': 'tree_swallow.n.02', 'name': 'tree_swallow'}, {'id': 1972, 'synset': 'white-bellied_swallow.n.01', 'name': 'white-bellied_swallow'}, {'id': 1973, 'synset': 'martin.n.05', 'name': 'martin'}, {'id': 1974, 'synset': 'house_martin.n.01', 'name': 'house_martin'}, {'id': 1975, 'synset': 'bank_martin.n.01', 'name': 'bank_martin'}, {'id': 1976, 'synset': 'purple_martin.n.01', 'name': 'purple_martin'}, {'id': 1977, 'synset': 'wood_swallow.n.01', 'name': 'wood_swallow'}, {'id': 1978, 'synset': 'tanager.n.01', 'name': 'tanager'}, {'id': 1979, 'synset': 'scarlet_tanager.n.01', 'name': 'scarlet_tanager'}, {'id': 1980, 'synset': 'western_tanager.n.01', 'name': 'western_tanager'}, {'id': 1981, 'synset': 'summer_tanager.n.01', 'name': 'summer_tanager'}, {'id': 1982, 'synset': 'hepatic_tanager.n.01', 'name': 'hepatic_tanager'}, {'id': 1983, 'synset': 'shrike.n.01', 'name': 'shrike'}, {'id': 1984, 'synset': 'butcherbird.n.01', 'name': 'butcherbird'}, {'id': 1985, 'synset': 'european_shrike.n.01', 'name': 'European_shrike'}, {'id': 1986, 'synset': 'northern_shrike.n.01', 'name': 'northern_shrike'}, {'id': 1987, 'synset': 'white-rumped_shrike.n.01', 'name': 'white-rumped_shrike'}, {'id': 1988, 'synset': 'loggerhead_shrike.n.01', 'name': 'loggerhead_shrike'}, {'id': 1989, 'synset': 'migrant_shrike.n.01', 'name': 'migrant_shrike'}, {'id': 1990, 'synset': 'bush_shrike.n.01', 'name': 'bush_shrike'}, {'id': 1991, 'synset': 'black-fronted_bush_shrike.n.01', 'name': 'black-fronted_bush_shrike'}, {'id': 1992, 'synset': 'bowerbird.n.01', 'name': 'bowerbird'}, {'id': 1993, 'synset': 'satin_bowerbird.n.01', 'name': 'satin_bowerbird'}, {'id': 1994, 'synset': 'great_bowerbird.n.01', 'name': 'great_bowerbird'}, {'id': 1995, 'synset': 'water_ouzel.n.01', 'name': 'water_ouzel'}, {'id': 1996, 'synset': 'european_water_ouzel.n.01', 'name': 'European_water_ouzel'}, {'id': 1997, 'synset': 'american_water_ouzel.n.01', 'name': 'American_water_ouzel'}, {'id': 1998, 'synset': 'vireo.n.01', 'name': 'vireo'}, {'id': 1999, 'synset': 'red-eyed_vireo.n.01', 'name': 'red-eyed_vireo'}, {'id': 2000, 'synset': 'solitary_vireo.n.01', 'name': 'solitary_vireo'}, {'id': 2001, 'synset': 'blue-headed_vireo.n.01', 'name': 'blue-headed_vireo'}, {'id': 2002, 'synset': 'waxwing.n.01', 'name': 'waxwing'}, {'id': 2003, 'synset': 'cedar_waxwing.n.01', 'name': 'cedar_waxwing'}, {'id': 2004, 'synset': 'bohemian_waxwing.n.01', 'name': 'Bohemian_waxwing'}, {'id': 2005, 'synset': 'bird_of_prey.n.01', 'name': 'bird_of_prey'}, {'id': 2006, 'synset': 'accipitriformes.n.01', 'name': 'Accipitriformes'}, {'id': 2007, 'synset': 'hawk.n.01', 'name': 'hawk'}, {'id': 2008, 'synset': 'eyas.n.01', 'name': 'eyas'}, {'id': 2009, 'synset': 'tiercel.n.01', 'name': 'tiercel'}, {'id': 2010, 'synset': 'goshawk.n.01', 'name': 'goshawk'}, {'id': 2011, 'synset': 'sparrow_hawk.n.02', 'name': 'sparrow_hawk'}, {'id': 2012, 'synset': "cooper's_hawk.n.01", 'name': "Cooper's_hawk"}, {'id': 2013, 'synset': 'chicken_hawk.n.01', 'name': 'chicken_hawk'}, {'id': 2014, 'synset': 'buteonine.n.01', 'name': 'buteonine'}, {'id': 2015, 'synset': 'redtail.n.01', 'name': 'redtail'}, {'id': 2016, 'synset': 'rough-legged_hawk.n.01', 'name': 'rough-legged_hawk'}, {'id': 2017, 'synset': 'red-shouldered_hawk.n.01', 'name': 'red-shouldered_hawk'}, {'id': 2018, 'synset': 'buzzard.n.02', 'name': 'buzzard'}, {'id': 2019, 'synset': 'honey_buzzard.n.01', 'name': 'honey_buzzard'}, {'id': 2020, 'synset': 'kite.n.04', 'name': 'kite'}, {'id': 2021, 'synset': 'black_kite.n.01', 'name': 'black_kite'}, {'id': 2022, 'synset': 'swallow-tailed_kite.n.01', 'name': 'swallow-tailed_kite'}, {'id': 2023, 'synset': 'white-tailed_kite.n.01', 'name': 'white-tailed_kite'}, {'id': 2024, 'synset': 'harrier.n.03', 'name': 'harrier'}, {'id': 2025, 'synset': 'marsh_harrier.n.01', 'name': 'marsh_harrier'}, {'id': 2026, 'synset': "montagu's_harrier.n.01", 'name': "Montagu's_harrier"}, {'id': 2027, 'synset': 'marsh_hawk.n.01', 'name': 'marsh_hawk'}, {'id': 2028, 'synset': 'harrier_eagle.n.01', 'name': 'harrier_eagle'}, {'id': 2029, 'synset': 'peregrine.n.01', 'name': 'peregrine'}, {'id': 2030, 'synset': 'falcon-gentle.n.01', 'name': 'falcon-gentle'}, {'id': 2031, 'synset': 'gyrfalcon.n.01', 'name': 'gyrfalcon'}, {'id': 2032, 'synset': 'kestrel.n.02', 'name': 'kestrel'}, {'id': 2033, 'synset': 'sparrow_hawk.n.01', 'name': 'sparrow_hawk'}, {'id': 2034, 'synset': 'pigeon_hawk.n.01', 'name': 'pigeon_hawk'}, {'id': 2035, 'synset': 'hobby.n.03', 'name': 'hobby'}, {'id': 2036, 'synset': 'caracara.n.01', 'name': 'caracara'}, {'id': 2037, 'synset': "audubon's_caracara.n.01", 'name': "Audubon's_caracara"}, {'id': 2038, 'synset': 'carancha.n.01', 'name': 'carancha'}, {'id': 2039, 'synset': 'young_bird.n.01', 'name': 'young_bird'}, {'id': 2040, 'synset': 'eaglet.n.01', 'name': 'eaglet'}, {'id': 2041, 'synset': 'harpy.n.04', 'name': 'harpy'}, {'id': 2042, 'synset': 'golden_eagle.n.01', 'name': 'golden_eagle'}, {'id': 2043, 'synset': 'tawny_eagle.n.01', 'name': 'tawny_eagle'}, {'id': 2044, 'synset': 'bald_eagle.n.01', 'name': 'bald_eagle'}, {'id': 2045, 'synset': 'sea_eagle.n.02', 'name': 'sea_eagle'}, {'id': 2046, 'synset': 'kamchatkan_sea_eagle.n.01', 'name': 'Kamchatkan_sea_eagle'}, {'id': 2047, 'synset': 'ern.n.01', 'name': 'ern'}, {'id': 2048, 'synset': 'fishing_eagle.n.01', 'name': 'fishing_eagle'}, {'id': 2049, 'synset': 'osprey.n.01', 'name': 'osprey'}, {'id': 2050, 'synset': 'aegypiidae.n.01', 'name': 'Aegypiidae'}, {'id': 2051, 'synset': 'old_world_vulture.n.01', 'name': 'Old_World_vulture'}, {'id': 2052, 'synset': 'griffon_vulture.n.01', 'name': 'griffon_vulture'}, {'id': 2053, 'synset': 'bearded_vulture.n.01', 'name': 'bearded_vulture'}, {'id': 2054, 'synset': 'egyptian_vulture.n.01', 'name': 'Egyptian_vulture'}, {'id': 2055, 'synset': 'black_vulture.n.02', 'name': 'black_vulture'}, {'id': 2056, 'synset': 'secretary_bird.n.01', 'name': 'secretary_bird'}, {'id': 2057, 'synset': 'new_world_vulture.n.01', 'name': 'New_World_vulture'}, {'id': 2058, 'synset': 'buzzard.n.01', 'name': 'buzzard'}, {'id': 2059, 'synset': 'condor.n.01', 'name': 'condor'}, {'id': 2060, 'synset': 'andean_condor.n.01', 'name': 'Andean_condor'}, {'id': 2061, 'synset': 'california_condor.n.01', 'name': 'California_condor'}, {'id': 2062, 'synset': 'black_vulture.n.01', 'name': 'black_vulture'}, {'id': 2063, 'synset': 'king_vulture.n.01', 'name': 'king_vulture'}, {'id': 2064, 'synset': 'owlet.n.01', 'name': 'owlet'}, {'id': 2065, 'synset': 'little_owl.n.01', 'name': 'little_owl'}, {'id': 2066, 'synset': 'horned_owl.n.01', 'name': 'horned_owl'}, {'id': 2067, 'synset': 'great_horned_owl.n.01', 'name': 'great_horned_owl'}, {'id': 2068, 'synset': 'great_grey_owl.n.01', 'name': 'great_grey_owl'}, {'id': 2069, 'synset': 'tawny_owl.n.01', 'name': 'tawny_owl'}, {'id': 2070, 'synset': 'barred_owl.n.01', 'name': 'barred_owl'}, {'id': 2071, 'synset': 'screech_owl.n.02', 'name': 'screech_owl'}, {'id': 2072, 'synset': 'screech_owl.n.01', 'name': 'screech_owl'}, {'id': 2073, 'synset': 'scops_owl.n.01', 'name': 'scops_owl'}, {'id': 2074, 'synset': 'spotted_owl.n.01', 'name': 'spotted_owl'}, {'id': 2075, 'synset': 'old_world_scops_owl.n.01', 'name': 'Old_World_scops_owl'}, {'id': 2076, 'synset': 'oriental_scops_owl.n.01', 'name': 'Oriental_scops_owl'}, {'id': 2077, 'synset': 'hoot_owl.n.01', 'name': 'hoot_owl'}, {'id': 2078, 'synset': 'hawk_owl.n.01', 'name': 'hawk_owl'}, {'id': 2079, 'synset': 'long-eared_owl.n.01', 'name': 'long-eared_owl'}, {'id': 2080, 'synset': 'laughing_owl.n.01', 'name': 'laughing_owl'}, {'id': 2081, 'synset': 'barn_owl.n.01', 'name': 'barn_owl'}, {'id': 2082, 'synset': 'amphibian.n.03', 'name': 'amphibian'}, {'id': 2083, 'synset': 'ichyostega.n.01', 'name': 'Ichyostega'}, {'id': 2084, 'synset': 'urodele.n.01', 'name': 'urodele'}, {'id': 2085, 'synset': 'salamander.n.01', 'name': 'salamander'}, {'id': 2086, 'synset': 'european_fire_salamander.n.01', 'name': 'European_fire_salamander'}, {'id': 2087, 'synset': 'spotted_salamander.n.02', 'name': 'spotted_salamander'}, {'id': 2088, 'synset': 'alpine_salamander.n.01', 'name': 'alpine_salamander'}, {'id': 2089, 'synset': 'newt.n.01', 'name': 'newt'}, {'id': 2090, 'synset': 'common_newt.n.01', 'name': 'common_newt'}, {'id': 2091, 'synset': 'red_eft.n.01', 'name': 'red_eft'}, {'id': 2092, 'synset': 'pacific_newt.n.01', 'name': 'Pacific_newt'}, {'id': 2093, 'synset': 'rough-skinned_newt.n.01', 'name': 'rough-skinned_newt'}, {'id': 2094, 'synset': 'california_newt.n.01', 'name': 'California_newt'}, {'id': 2095, 'synset': 'eft.n.01', 'name': 'eft'}, {'id': 2096, 'synset': 'ambystomid.n.01', 'name': 'ambystomid'}, {'id': 2097, 'synset': 'mole_salamander.n.01', 'name': 'mole_salamander'}, {'id': 2098, 'synset': 'spotted_salamander.n.01', 'name': 'spotted_salamander'}, {'id': 2099, 'synset': 'tiger_salamander.n.01', 'name': 'tiger_salamander'}, {'id': 2100, 'synset': 'axolotl.n.01', 'name': 'axolotl'}, {'id': 2101, 'synset': 'waterdog.n.01', 'name': 'waterdog'}, {'id': 2102, 'synset': 'hellbender.n.01', 'name': 'hellbender'}, {'id': 2103, 'synset': 'giant_salamander.n.01', 'name': 'giant_salamander'}, {'id': 2104, 'synset': 'olm.n.01', 'name': 'olm'}, {'id': 2105, 'synset': 'mud_puppy.n.01', 'name': 'mud_puppy'}, {'id': 2106, 'synset': 'dicamptodon.n.01', 'name': 'dicamptodon'}, {'id': 2107, 'synset': 'pacific_giant_salamander.n.01', 'name': 'Pacific_giant_salamander'}, {'id': 2108, 'synset': 'olympic_salamander.n.01', 'name': 'olympic_salamander'}, {'id': 2109, 'synset': 'lungless_salamander.n.01', 'name': 'lungless_salamander'}, {'id': 2110, 'synset': 'eastern_red-backed_salamander.n.01', 'name': 'eastern_red-backed_salamander'}, {'id': 2111, 'synset': 'western_red-backed_salamander.n.01', 'name': 'western_red-backed_salamander'}, {'id': 2112, 'synset': 'dusky_salamander.n.01', 'name': 'dusky_salamander'}, {'id': 2113, 'synset': 'climbing_salamander.n.01', 'name': 'climbing_salamander'}, {'id': 2114, 'synset': 'arboreal_salamander.n.01', 'name': 'arboreal_salamander'}, {'id': 2115, 'synset': 'slender_salamander.n.01', 'name': 'slender_salamander'}, {'id': 2116, 'synset': 'web-toed_salamander.n.01', 'name': 'web-toed_salamander'}, {'id': 2117, 'synset': 'shasta_salamander.n.01', 'name': 'Shasta_salamander'}, {'id': 2118, 'synset': 'limestone_salamander.n.01', 'name': 'limestone_salamander'}, {'id': 2119, 'synset': 'amphiuma.n.01', 'name': 'amphiuma'}, {'id': 2120, 'synset': 'siren.n.05', 'name': 'siren'}, {'id': 2121, 'synset': 'true_frog.n.01', 'name': 'true_frog'}, {'id': 2122, 'synset': 'wood-frog.n.01', 'name': 'wood-frog'}, {'id': 2123, 'synset': 'leopard_frog.n.01', 'name': 'leopard_frog'}, {'id': 2124, 'synset': 'bullfrog.n.01', 'name': 'bullfrog'}, {'id': 2125, 'synset': 'green_frog.n.01', 'name': 'green_frog'}, {'id': 2126, 'synset': 'cascades_frog.n.01', 'name': 'cascades_frog'}, {'id': 2127, 'synset': 'goliath_frog.n.01', 'name': 'goliath_frog'}, {'id': 2128, 'synset': 'pickerel_frog.n.01', 'name': 'pickerel_frog'}, {'id': 2129, 'synset': 'tarahumara_frog.n.01', 'name': 'tarahumara_frog'}, {'id': 2130, 'synset': 'grass_frog.n.01', 'name': 'grass_frog'}, {'id': 2131, 'synset': 'leptodactylid_frog.n.01', 'name': 'leptodactylid_frog'}, {'id': 2132, 'synset': 'robber_frog.n.02', 'name': 'robber_frog'}, {'id': 2133, 'synset': 'barking_frog.n.01', 'name': 'barking_frog'}, {'id': 2134, 'synset': 'crapaud.n.01', 'name': 'crapaud'}, {'id': 2135, 'synset': 'tree_frog.n.02', 'name': 'tree_frog'}, {'id': 2136, 'synset': 'tailed_frog.n.01', 'name': 'tailed_frog'}, {'id': 2137, 'synset': 'liopelma_hamiltoni.n.01', 'name': 'Liopelma_hamiltoni'}, {'id': 2138, 'synset': 'true_toad.n.01', 'name': 'true_toad'}, {'id': 2139, 'synset': 'bufo.n.01', 'name': 'bufo'}, {'id': 2140, 'synset': 'agua.n.01', 'name': 'agua'}, {'id': 2141, 'synset': 'european_toad.n.01', 'name': 'European_toad'}, {'id': 2142, 'synset': 'natterjack.n.01', 'name': 'natterjack'}, {'id': 2143, 'synset': 'american_toad.n.01', 'name': 'American_toad'}, {'id': 2144, 'synset': 'eurasian_green_toad.n.01', 'name': 'Eurasian_green_toad'}, {'id': 2145, 'synset': 'american_green_toad.n.01', 'name': 'American_green_toad'}, {'id': 2146, 'synset': 'yosemite_toad.n.01', 'name': 'Yosemite_toad'}, {'id': 2147, 'synset': 'texas_toad.n.01', 'name': 'Texas_toad'}, {'id': 2148, 'synset': 'southwestern_toad.n.01', 'name': 'southwestern_toad'}, {'id': 2149, 'synset': 'western_toad.n.01', 'name': 'western_toad'}, {'id': 2150, 'synset': 'obstetrical_toad.n.01', 'name': 'obstetrical_toad'}, {'id': 2151, 'synset': 'midwife_toad.n.01', 'name': 'midwife_toad'}, {'id': 2152, 'synset': 'fire-bellied_toad.n.01', 'name': 'fire-bellied_toad'}, {'id': 2153, 'synset': 'spadefoot.n.01', 'name': 'spadefoot'}, {'id': 2154, 'synset': 'western_spadefoot.n.01', 'name': 'western_spadefoot'}, {'id': 2155, 'synset': 'southern_spadefoot.n.01', 'name': 'southern_spadefoot'}, {'id': 2156, 'synset': 'plains_spadefoot.n.01', 'name': 'plains_spadefoot'}, {'id': 2157, 'synset': 'tree_toad.n.01', 'name': 'tree_toad'}, {'id': 2158, 'synset': 'spring_peeper.n.01', 'name': 'spring_peeper'}, {'id': 2159, 'synset': 'pacific_tree_toad.n.01', 'name': 'Pacific_tree_toad'}, {'id': 2160, 'synset': 'canyon_treefrog.n.01', 'name': 'canyon_treefrog'}, {'id': 2161, 'synset': 'chameleon_tree_frog.n.01', 'name': 'chameleon_tree_frog'}, {'id': 2162, 'synset': 'cricket_frog.n.01', 'name': 'cricket_frog'}, {'id': 2163, 'synset': 'northern_cricket_frog.n.01', 'name': 'northern_cricket_frog'}, {'id': 2164, 'synset': 'eastern_cricket_frog.n.01', 'name': 'eastern_cricket_frog'}, {'id': 2165, 'synset': 'chorus_frog.n.01', 'name': 'chorus_frog'}, {'id': 2166, 'synset': 'lowland_burrowing_treefrog.n.01', 'name': 'lowland_burrowing_treefrog'}, {'id': 2167, 'synset': 'western_narrow-mouthed_toad.n.01', 'name': 'western_narrow-mouthed_toad'}, {'id': 2168, 'synset': 'eastern_narrow-mouthed_toad.n.01', 'name': 'eastern_narrow-mouthed_toad'}, {'id': 2169, 'synset': 'sheep_frog.n.01', 'name': 'sheep_frog'}, {'id': 2170, 'synset': 'tongueless_frog.n.01', 'name': 'tongueless_frog'}, {'id': 2171, 'synset': 'surinam_toad.n.01', 'name': 'Surinam_toad'}, {'id': 2172, 'synset': 'african_clawed_frog.n.01', 'name': 'African_clawed_frog'}, {'id': 2173, 'synset': 'south_american_poison_toad.n.01', 'name': 'South_American_poison_toad'}, {'id': 2174, 'synset': 'caecilian.n.01', 'name': 'caecilian'}, {'id': 2175, 'synset': 'reptile.n.01', 'name': 'reptile'}, {'id': 2176, 'synset': 'anapsid.n.01', 'name': 'anapsid'}, {'id': 2177, 'synset': 'diapsid.n.01', 'name': 'diapsid'}, {'id': 2178, 'synset': 'diapsida.n.01', 'name': 'Diapsida'}, {'id': 2179, 'synset': 'chelonian.n.01', 'name': 'chelonian'}, {'id': 2180, 'synset': 'sea_turtle.n.01', 'name': 'sea_turtle'}, {'id': 2181, 'synset': 'green_turtle.n.01', 'name': 'green_turtle'}, {'id': 2182, 'synset': 'loggerhead.n.02', 'name': 'loggerhead'}, {'id': 2183, 'synset': 'ridley.n.01', 'name': 'ridley'}, {'id': 2184, 'synset': 'atlantic_ridley.n.01', 'name': 'Atlantic_ridley'}, {'id': 2185, 'synset': 'pacific_ridley.n.01', 'name': 'Pacific_ridley'}, {'id': 2186, 'synset': 'hawksbill_turtle.n.01', 'name': 'hawksbill_turtle'}, {'id': 2187, 'synset': 'leatherback_turtle.n.01', 'name': 'leatherback_turtle'}, {'id': 2188, 'synset': 'snapping_turtle.n.01', 'name': 'snapping_turtle'}, {'id': 2189, 'synset': 'common_snapping_turtle.n.01', 'name': 'common_snapping_turtle'}, {'id': 2190, 'synset': 'alligator_snapping_turtle.n.01', 'name': 'alligator_snapping_turtle'}, {'id': 2191, 'synset': 'mud_turtle.n.01', 'name': 'mud_turtle'}, {'id': 2192, 'synset': 'musk_turtle.n.01', 'name': 'musk_turtle'}, {'id': 2193, 'synset': 'terrapin.n.01', 'name': 'terrapin'}, {'id': 2194, 'synset': 'diamondback_terrapin.n.01', 'name': 'diamondback_terrapin'}, {'id': 2195, 'synset': 'red-bellied_terrapin.n.01', 'name': 'red-bellied_terrapin'}, {'id': 2196, 'synset': 'slider.n.03', 'name': 'slider'}, {'id': 2197, 'synset': 'cooter.n.01', 'name': 'cooter'}, {'id': 2198, 'synset': 'box_turtle.n.01', 'name': 'box_turtle'}, {'id': 2199, 'synset': 'western_box_turtle.n.01', 'name': 'Western_box_turtle'}, {'id': 2200, 'synset': 'painted_turtle.n.01', 'name': 'painted_turtle'}, {'id': 2201, 'synset': 'tortoise.n.01', 'name': 'tortoise'}, {'id': 2202, 'synset': 'european_tortoise.n.01', 'name': 'European_tortoise'}, {'id': 2203, 'synset': 'giant_tortoise.n.01', 'name': 'giant_tortoise'}, {'id': 2204, 'synset': 'gopher_tortoise.n.01', 'name': 'gopher_tortoise'}, {'id': 2205, 'synset': 'desert_tortoise.n.01', 'name': 'desert_tortoise'}, {'id': 2206, 'synset': 'texas_tortoise.n.01', 'name': 'Texas_tortoise'}, {'id': 2207, 'synset': 'soft-shelled_turtle.n.01', 'name': 'soft-shelled_turtle'}, {'id': 2208, 'synset': 'spiny_softshell.n.01', 'name': 'spiny_softshell'}, {'id': 2209, 'synset': 'smooth_softshell.n.01', 'name': 'smooth_softshell'}, {'id': 2210, 'synset': 'tuatara.n.01', 'name': 'tuatara'}, {'id': 2211, 'synset': 'saurian.n.01', 'name': 'saurian'}, {'id': 2212, 'synset': 'gecko.n.01', 'name': 'gecko'}, {'id': 2213, 'synset': 'flying_gecko.n.01', 'name': 'flying_gecko'}, {'id': 2214, 'synset': 'banded_gecko.n.01', 'name': 'banded_gecko'}, {'id': 2215, 'synset': 'iguanid.n.01', 'name': 'iguanid'}, {'id': 2216, 'synset': 'common_iguana.n.01', 'name': 'common_iguana'}, {'id': 2217, 'synset': 'marine_iguana.n.01', 'name': 'marine_iguana'}, {'id': 2218, 'synset': 'desert_iguana.n.01', 'name': 'desert_iguana'}, {'id': 2219, 'synset': 'chuckwalla.n.01', 'name': 'chuckwalla'}, {'id': 2220, 'synset': 'zebra-tailed_lizard.n.01', 'name': 'zebra-tailed_lizard'}, {'id': 2221, 'synset': 'fringe-toed_lizard.n.01', 'name': 'fringe-toed_lizard'}, {'id': 2222, 'synset': 'earless_lizard.n.01', 'name': 'earless_lizard'}, {'id': 2223, 'synset': 'collared_lizard.n.01', 'name': 'collared_lizard'}, {'id': 2224, 'synset': 'leopard_lizard.n.01', 'name': 'leopard_lizard'}, {'id': 2225, 'synset': 'spiny_lizard.n.02', 'name': 'spiny_lizard'}, {'id': 2226, 'synset': 'fence_lizard.n.01', 'name': 'fence_lizard'}, {'id': 2227, 'synset': 'western_fence_lizard.n.01', 'name': 'western_fence_lizard'}, {'id': 2228, 'synset': 'eastern_fence_lizard.n.01', 'name': 'eastern_fence_lizard'}, {'id': 2229, 'synset': 'sagebrush_lizard.n.01', 'name': 'sagebrush_lizard'}, {'id': 2230, 'synset': 'side-blotched_lizard.n.01', 'name': 'side-blotched_lizard'}, {'id': 2231, 'synset': 'tree_lizard.n.01', 'name': 'tree_lizard'}, {'id': 2232, 'synset': 'horned_lizard.n.01', 'name': 'horned_lizard'}, {'id': 2233, 'synset': 'texas_horned_lizard.n.01', 'name': 'Texas_horned_lizard'}, {'id': 2234, 'synset': 'basilisk.n.03', 'name': 'basilisk'}, {'id': 2235, 'synset': 'american_chameleon.n.01', 'name': 'American_chameleon'}, {'id': 2236, 'synset': 'worm_lizard.n.01', 'name': 'worm_lizard'}, {'id': 2237, 'synset': 'night_lizard.n.01', 'name': 'night_lizard'}, {'id': 2238, 'synset': 'skink.n.01', 'name': 'skink'}, {'id': 2239, 'synset': 'western_skink.n.01', 'name': 'western_skink'}, {'id': 2240, 'synset': 'mountain_skink.n.01', 'name': 'mountain_skink'}, {'id': 2241, 'synset': 'teiid_lizard.n.01', 'name': 'teiid_lizard'}, {'id': 2242, 'synset': 'whiptail.n.01', 'name': 'whiptail'}, {'id': 2243, 'synset': 'racerunner.n.01', 'name': 'racerunner'}, {'id': 2244, 'synset': 'plateau_striped_whiptail.n.01', 'name': 'plateau_striped_whiptail'}, {'id': 2245, 'synset': 'chihuahuan_spotted_whiptail.n.01', 'name': 'Chihuahuan_spotted_whiptail'}, {'id': 2246, 'synset': 'western_whiptail.n.01', 'name': 'western_whiptail'}, {'id': 2247, 'synset': 'checkered_whiptail.n.01', 'name': 'checkered_whiptail'}, {'id': 2248, 'synset': 'teju.n.01', 'name': 'teju'}, {'id': 2249, 'synset': 'caiman_lizard.n.01', 'name': 'caiman_lizard'}, {'id': 2250, 'synset': 'agamid.n.01', 'name': 'agamid'}, {'id': 2251, 'synset': 'agama.n.01', 'name': 'agama'}, {'id': 2252, 'synset': 'frilled_lizard.n.01', 'name': 'frilled_lizard'}, {'id': 2253, 'synset': 'moloch.n.03', 'name': 'moloch'}, {'id': 2254, 'synset': 'mountain_devil.n.02', 'name': 'mountain_devil'}, {'id': 2255, 'synset': 'anguid_lizard.n.01', 'name': 'anguid_lizard'}, {'id': 2256, 'synset': 'alligator_lizard.n.01', 'name': 'alligator_lizard'}, {'id': 2257, 'synset': 'blindworm.n.01', 'name': 'blindworm'}, {'id': 2258, 'synset': 'glass_lizard.n.01', 'name': 'glass_lizard'}, {'id': 2259, 'synset': 'legless_lizard.n.01', 'name': 'legless_lizard'}, {'id': 2260, 'synset': 'lanthanotus_borneensis.n.01', 'name': 'Lanthanotus_borneensis'}, {'id': 2261, 'synset': 'venomous_lizard.n.01', 'name': 'venomous_lizard'}, {'id': 2262, 'synset': 'gila_monster.n.01', 'name': 'Gila_monster'}, {'id': 2263, 'synset': 'beaded_lizard.n.01', 'name': 'beaded_lizard'}, {'id': 2264, 'synset': 'lacertid_lizard.n.01', 'name': 'lacertid_lizard'}, {'id': 2265, 'synset': 'sand_lizard.n.01', 'name': 'sand_lizard'}, {'id': 2266, 'synset': 'green_lizard.n.01', 'name': 'green_lizard'}, {'id': 2267, 'synset': 'chameleon.n.03', 'name': 'chameleon'}, {'id': 2268, 'synset': 'african_chameleon.n.01', 'name': 'African_chameleon'}, {'id': 2269, 'synset': 'horned_chameleon.n.01', 'name': 'horned_chameleon'}, {'id': 2270, 'synset': 'monitor.n.07', 'name': 'monitor'}, {'id': 2271, 'synset': 'african_monitor.n.01', 'name': 'African_monitor'}, {'id': 2272, 'synset': 'komodo_dragon.n.01', 'name': 'Komodo_dragon'}, {'id': 2273, 'synset': 'crocodilian_reptile.n.01', 'name': 'crocodilian_reptile'}, {'id': 2274, 'synset': 'crocodile.n.01', 'name': 'crocodile'}, {'id': 2275, 'synset': 'african_crocodile.n.01', 'name': 'African_crocodile'}, {'id': 2276, 'synset': 'asian_crocodile.n.01', 'name': 'Asian_crocodile'}, {'id': 2277, 'synset': "morlett's_crocodile.n.01", 'name': "Morlett's_crocodile"}, {'id': 2278, 'synset': 'false_gavial.n.01', 'name': 'false_gavial'}, {'id': 2279, 'synset': 'american_alligator.n.01', 'name': 'American_alligator'}, {'id': 2280, 'synset': 'chinese_alligator.n.01', 'name': 'Chinese_alligator'}, {'id': 2281, 'synset': 'caiman.n.01', 'name': 'caiman'}, {'id': 2282, 'synset': 'spectacled_caiman.n.01', 'name': 'spectacled_caiman'}, {'id': 2283, 'synset': 'gavial.n.01', 'name': 'gavial'}, {'id': 2284, 'synset': 'armored_dinosaur.n.01', 'name': 'armored_dinosaur'}, {'id': 2285, 'synset': 'stegosaur.n.01', 'name': 'stegosaur'}, {'id': 2286, 'synset': 'ankylosaur.n.01', 'name': 'ankylosaur'}, {'id': 2287, 'synset': 'edmontonia.n.01', 'name': 'Edmontonia'}, {'id': 2288, 'synset': 'bone-headed_dinosaur.n.01', 'name': 'bone-headed_dinosaur'}, {'id': 2289, 'synset': 'pachycephalosaur.n.01', 'name': 'pachycephalosaur'}, {'id': 2290, 'synset': 'ceratopsian.n.01', 'name': 'ceratopsian'}, {'id': 2291, 'synset': 'protoceratops.n.01', 'name': 'protoceratops'}, {'id': 2292, 'synset': 'triceratops.n.01', 'name': 'triceratops'}, {'id': 2293, 'synset': 'styracosaur.n.01', 'name': 'styracosaur'}, {'id': 2294, 'synset': 'psittacosaur.n.01', 'name': 'psittacosaur'}, {'id': 2295, 'synset': 'ornithopod.n.01', 'name': 'ornithopod'}, {'id': 2296, 'synset': 'hadrosaur.n.01', 'name': 'hadrosaur'}, {'id': 2297, 'synset': 'trachodon.n.01', 'name': 'trachodon'}, {'id': 2298, 'synset': 'saurischian.n.01', 'name': 'saurischian'}, {'id': 2299, 'synset': 'sauropod.n.01', 'name': 'sauropod'}, {'id': 2300, 'synset': 'apatosaur.n.01', 'name': 'apatosaur'}, {'id': 2301, 'synset': 'barosaur.n.01', 'name': 'barosaur'}, {'id': 2302, 'synset': 'diplodocus.n.01', 'name': 'diplodocus'}, {'id': 2303, 'synset': 'argentinosaur.n.01', 'name': 'argentinosaur'}, {'id': 2304, 'synset': 'theropod.n.01', 'name': 'theropod'}, {'id': 2305, 'synset': 'ceratosaur.n.01', 'name': 'ceratosaur'}, {'id': 2306, 'synset': 'coelophysis.n.01', 'name': 'coelophysis'}, {'id': 2307, 'synset': 'tyrannosaur.n.01', 'name': 'tyrannosaur'}, {'id': 2308, 'synset': 'allosaur.n.01', 'name': 'allosaur'}, {'id': 2309, 'synset': 'ornithomimid.n.01', 'name': 'ornithomimid'}, {'id': 2310, 'synset': 'maniraptor.n.01', 'name': 'maniraptor'}, {'id': 2311, 'synset': 'oviraptorid.n.01', 'name': 'oviraptorid'}, {'id': 2312, 'synset': 'velociraptor.n.01', 'name': 'velociraptor'}, {'id': 2313, 'synset': 'deinonychus.n.01', 'name': 'deinonychus'}, {'id': 2314, 'synset': 'utahraptor.n.01', 'name': 'utahraptor'}, {'id': 2315, 'synset': 'synapsid.n.01', 'name': 'synapsid'}, {'id': 2316, 'synset': 'dicynodont.n.01', 'name': 'dicynodont'}, {'id': 2317, 'synset': 'pelycosaur.n.01', 'name': 'pelycosaur'}, {'id': 2318, 'synset': 'dimetrodon.n.01', 'name': 'dimetrodon'}, {'id': 2319, 'synset': 'pterosaur.n.01', 'name': 'pterosaur'}, {'id': 2320, 'synset': 'pterodactyl.n.01', 'name': 'pterodactyl'}, {'id': 2321, 'synset': 'ichthyosaur.n.01', 'name': 'ichthyosaur'}, {'id': 2322, 'synset': 'ichthyosaurus.n.01', 'name': 'ichthyosaurus'}, {'id': 2323, 'synset': 'stenopterygius.n.01', 'name': 'stenopterygius'}, {'id': 2324, 'synset': 'plesiosaur.n.01', 'name': 'plesiosaur'}, {'id': 2325, 'synset': 'nothosaur.n.01', 'name': 'nothosaur'}, {'id': 2326, 'synset': 'colubrid_snake.n.01', 'name': 'colubrid_snake'}, {'id': 2327, 'synset': 'hoop_snake.n.01', 'name': 'hoop_snake'}, {'id': 2328, 'synset': 'thunder_snake.n.01', 'name': 'thunder_snake'}, {'id': 2329, 'synset': 'ringneck_snake.n.01', 'name': 'ringneck_snake'}, {'id': 2330, 'synset': 'hognose_snake.n.01', 'name': 'hognose_snake'}, {'id': 2331, 'synset': 'leaf-nosed_snake.n.01', 'name': 'leaf-nosed_snake'}, {'id': 2332, 'synset': 'green_snake.n.02', 'name': 'green_snake'}, {'id': 2333, 'synset': 'smooth_green_snake.n.01', 'name': 'smooth_green_snake'}, {'id': 2334, 'synset': 'rough_green_snake.n.01', 'name': 'rough_green_snake'}, {'id': 2335, 'synset': 'green_snake.n.01', 'name': 'green_snake'}, {'id': 2336, 'synset': 'racer.n.04', 'name': 'racer'}, {'id': 2337, 'synset': 'blacksnake.n.02', 'name': 'blacksnake'}, {'id': 2338, 'synset': 'blue_racer.n.01', 'name': 'blue_racer'}, {'id': 2339, 'synset': 'horseshoe_whipsnake.n.01', 'name': 'horseshoe_whipsnake'}, {'id': 2340, 'synset': 'whip-snake.n.01', 'name': 'whip-snake'}, {'id': 2341, 'synset': 'coachwhip.n.02', 'name': 'coachwhip'}, {'id': 2342, 'synset': 'california_whipsnake.n.01', 'name': 'California_whipsnake'}, {'id': 2343, 'synset': 'sonoran_whipsnake.n.01', 'name': 'Sonoran_whipsnake'}, {'id': 2344, 'synset': 'rat_snake.n.01', 'name': 'rat_snake'}, {'id': 2345, 'synset': 'corn_snake.n.01', 'name': 'corn_snake'}, {'id': 2346, 'synset': 'black_rat_snake.n.01', 'name': 'black_rat_snake'}, {'id': 2347, 'synset': 'chicken_snake.n.01', 'name': 'chicken_snake'}, {'id': 2348, 'synset': 'indian_rat_snake.n.01', 'name': 'Indian_rat_snake'}, {'id': 2349, 'synset': 'glossy_snake.n.01', 'name': 'glossy_snake'}, {'id': 2350, 'synset': 'bull_snake.n.01', 'name': 'bull_snake'}, {'id': 2351, 'synset': 'gopher_snake.n.02', 'name': 'gopher_snake'}, {'id': 2352, 'synset': 'pine_snake.n.01', 'name': 'pine_snake'}, {'id': 2353, 'synset': 'king_snake.n.01', 'name': 'king_snake'}, {'id': 2354, 'synset': 'common_kingsnake.n.01', 'name': 'common_kingsnake'}, {'id': 2355, 'synset': 'milk_snake.n.01', 'name': 'milk_snake'}, {'id': 2356, 'synset': 'garter_snake.n.01', 'name': 'garter_snake'}, {'id': 2357, 'synset': 'common_garter_snake.n.01', 'name': 'common_garter_snake'}, {'id': 2358, 'synset': 'ribbon_snake.n.01', 'name': 'ribbon_snake'}, {'id': 2359, 'synset': 'western_ribbon_snake.n.01', 'name': 'Western_ribbon_snake'}, {'id': 2360, 'synset': 'lined_snake.n.01', 'name': 'lined_snake'}, {'id': 2361, 'synset': 'ground_snake.n.01', 'name': 'ground_snake'}, {'id': 2362, 'synset': 'eastern_ground_snake.n.01', 'name': 'eastern_ground_snake'}, {'id': 2363, 'synset': 'water_snake.n.01', 'name': 'water_snake'}, {'id': 2364, 'synset': 'common_water_snake.n.01', 'name': 'common_water_snake'}, {'id': 2365, 'synset': 'water_moccasin.n.02', 'name': 'water_moccasin'}, {'id': 2366, 'synset': 'grass_snake.n.01', 'name': 'grass_snake'}, {'id': 2367, 'synset': 'viperine_grass_snake.n.01', 'name': 'viperine_grass_snake'}, {'id': 2368, 'synset': 'red-bellied_snake.n.01', 'name': 'red-bellied_snake'}, {'id': 2369, 'synset': 'sand_snake.n.01', 'name': 'sand_snake'}, {'id': 2370, 'synset': 'banded_sand_snake.n.01', 'name': 'banded_sand_snake'}, {'id': 2371, 'synset': 'black-headed_snake.n.01', 'name': 'black-headed_snake'}, {'id': 2372, 'synset': 'vine_snake.n.01', 'name': 'vine_snake'}, {'id': 2373, 'synset': 'lyre_snake.n.01', 'name': 'lyre_snake'}, {'id': 2374, 'synset': 'sonoran_lyre_snake.n.01', 'name': 'Sonoran_lyre_snake'}, {'id': 2375, 'synset': 'night_snake.n.01', 'name': 'night_snake'}, {'id': 2376, 'synset': 'blind_snake.n.01', 'name': 'blind_snake'}, {'id': 2377, 'synset': 'western_blind_snake.n.01', 'name': 'western_blind_snake'}, {'id': 2378, 'synset': 'indigo_snake.n.01', 'name': 'indigo_snake'}, {'id': 2379, 'synset': 'eastern_indigo_snake.n.01', 'name': 'eastern_indigo_snake'}, {'id': 2380, 'synset': 'constrictor.n.01', 'name': 'constrictor'}, {'id': 2381, 'synset': 'boa.n.02', 'name': 'boa'}, {'id': 2382, 'synset': 'boa_constrictor.n.01', 'name': 'boa_constrictor'}, {'id': 2383, 'synset': 'rubber_boa.n.01', 'name': 'rubber_boa'}, {'id': 2384, 'synset': 'rosy_boa.n.01', 'name': 'rosy_boa'}, {'id': 2385, 'synset': 'anaconda.n.01', 'name': 'anaconda'}, {'id': 2386, 'synset': 'python.n.01', 'name': 'python'}, {'id': 2387, 'synset': 'carpet_snake.n.01', 'name': 'carpet_snake'}, {'id': 2388, 'synset': 'reticulated_python.n.01', 'name': 'reticulated_python'}, {'id': 2389, 'synset': 'indian_python.n.01', 'name': 'Indian_python'}, {'id': 2390, 'synset': 'rock_python.n.01', 'name': 'rock_python'}, {'id': 2391, 'synset': 'amethystine_python.n.01', 'name': 'amethystine_python'}, {'id': 2392, 'synset': 'elapid.n.01', 'name': 'elapid'}, {'id': 2393, 'synset': 'coral_snake.n.02', 'name': 'coral_snake'}, {'id': 2394, 'synset': 'eastern_coral_snake.n.01', 'name': 'eastern_coral_snake'}, {'id': 2395, 'synset': 'western_coral_snake.n.01', 'name': 'western_coral_snake'}, {'id': 2396, 'synset': 'coral_snake.n.01', 'name': 'coral_snake'}, {'id': 2397, 'synset': 'african_coral_snake.n.01', 'name': 'African_coral_snake'}, {'id': 2398, 'synset': 'australian_coral_snake.n.01', 'name': 'Australian_coral_snake'}, {'id': 2399, 'synset': 'copperhead.n.02', 'name': 'copperhead'}, {'id': 2400, 'synset': 'cobra.n.01', 'name': 'cobra'}, {'id': 2401, 'synset': 'indian_cobra.n.01', 'name': 'Indian_cobra'}, {'id': 2402, 'synset': 'asp.n.02', 'name': 'asp'}, {'id': 2403, 'synset': 'black-necked_cobra.n.01', 'name': 'black-necked_cobra'}, {'id': 2404, 'synset': 'hamadryad.n.02', 'name': 'hamadryad'}, {'id': 2405, 'synset': 'ringhals.n.01', 'name': 'ringhals'}, {'id': 2406, 'synset': 'mamba.n.01', 'name': 'mamba'}, {'id': 2407, 'synset': 'black_mamba.n.01', 'name': 'black_mamba'}, {'id': 2408, 'synset': 'green_mamba.n.01', 'name': 'green_mamba'}, {'id': 2409, 'synset': 'death_adder.n.01', 'name': 'death_adder'}, {'id': 2410, 'synset': 'tiger_snake.n.01', 'name': 'tiger_snake'}, {'id': 2411, 'synset': 'australian_blacksnake.n.01', 'name': 'Australian_blacksnake'}, {'id': 2412, 'synset': 'krait.n.01', 'name': 'krait'}, {'id': 2413, 'synset': 'banded_krait.n.01', 'name': 'banded_krait'}, {'id': 2414, 'synset': 'taipan.n.01', 'name': 'taipan'}, {'id': 2415, 'synset': 'sea_snake.n.01', 'name': 'sea_snake'}, {'id': 2416, 'synset': 'viper.n.01', 'name': 'viper'}, {'id': 2417, 'synset': 'adder.n.03', 'name': 'adder'}, {'id': 2418, 'synset': 'asp.n.01', 'name': 'asp'}, {'id': 2419, 'synset': 'puff_adder.n.01', 'name': 'puff_adder'}, {'id': 2420, 'synset': 'gaboon_viper.n.01', 'name': 'gaboon_viper'}, {'id': 2421, 'synset': 'horned_viper.n.01', 'name': 'horned_viper'}, {'id': 2422, 'synset': 'pit_viper.n.01', 'name': 'pit_viper'}, {'id': 2423, 'synset': 'copperhead.n.01', 'name': 'copperhead'}, {'id': 2424, 'synset': 'water_moccasin.n.01', 'name': 'water_moccasin'}, {'id': 2425, 'synset': 'rattlesnake.n.01', 'name': 'rattlesnake'}, {'id': 2426, 'synset': 'diamondback.n.01', 'name': 'diamondback'}, {'id': 2427, 'synset': 'timber_rattlesnake.n.01', 'name': 'timber_rattlesnake'}, {'id': 2428, 'synset': 'canebrake_rattlesnake.n.01', 'name': 'canebrake_rattlesnake'}, {'id': 2429, 'synset': 'prairie_rattlesnake.n.01', 'name': 'prairie_rattlesnake'}, {'id': 2430, 'synset': 'sidewinder.n.01', 'name': 'sidewinder'}, {'id': 2431, 'synset': 'western_diamondback.n.01', 'name': 'Western_diamondback'}, {'id': 2432, 'synset': 'rock_rattlesnake.n.01', 'name': 'rock_rattlesnake'}, {'id': 2433, 'synset': 'tiger_rattlesnake.n.01', 'name': 'tiger_rattlesnake'}, {'id': 2434, 'synset': 'mojave_rattlesnake.n.01', 'name': 'Mojave_rattlesnake'}, {'id': 2435, 'synset': 'speckled_rattlesnake.n.01', 'name': 'speckled_rattlesnake'}, {'id': 2436, 'synset': 'massasauga.n.02', 'name': 'massasauga'}, {'id': 2437, 'synset': 'ground_rattler.n.01', 'name': 'ground_rattler'}, {'id': 2438, 'synset': 'fer-de-lance.n.01', 'name': 'fer-de-lance'}, {'id': 2439, 'synset': 'carcase.n.01', 'name': 'carcase'}, {'id': 2440, 'synset': 'carrion.n.01', 'name': 'carrion'}, {'id': 2441, 'synset': 'arthropod.n.01', 'name': 'arthropod'}, {'id': 2442, 'synset': 'trilobite.n.01', 'name': 'trilobite'}, {'id': 2443, 'synset': 'arachnid.n.01', 'name': 'arachnid'}, {'id': 2444, 'synset': 'harvestman.n.01', 'name': 'harvestman'}, {'id': 2445, 'synset': 'scorpion.n.03', 'name': 'scorpion'}, {'id': 2446, 'synset': 'false_scorpion.n.01', 'name': 'false_scorpion'}, {'id': 2447, 'synset': 'book_scorpion.n.01', 'name': 'book_scorpion'}, {'id': 2448, 'synset': 'whip-scorpion.n.01', 'name': 'whip-scorpion'}, {'id': 2449, 'synset': 'vinegarroon.n.01', 'name': 'vinegarroon'}, {'id': 2450, 'synset': 'orb-weaving_spider.n.01', 'name': 'orb-weaving_spider'}, {'id': 2451, 'synset': 'black_and_gold_garden_spider.n.01', 'name': 'black_and_gold_garden_spider'}, {'id': 2452, 'synset': 'barn_spider.n.01', 'name': 'barn_spider'}, {'id': 2453, 'synset': 'garden_spider.n.01', 'name': 'garden_spider'}, {'id': 2454, 'synset': 'comb-footed_spider.n.01', 'name': 'comb-footed_spider'}, {'id': 2455, 'synset': 'black_widow.n.01', 'name': 'black_widow'}, {'id': 2456, 'synset': 'tarantula.n.02', 'name': 'tarantula'}, {'id': 2457, 'synset': 'wolf_spider.n.01', 'name': 'wolf_spider'}, {'id': 2458, 'synset': 'european_wolf_spider.n.01', 'name': 'European_wolf_spider'}, {'id': 2459, 'synset': 'trap-door_spider.n.01', 'name': 'trap-door_spider'}, {'id': 2460, 'synset': 'acarine.n.01', 'name': 'acarine'}, {'id': 2461, 'synset': 'tick.n.02', 'name': 'tick'}, {'id': 2462, 'synset': 'hard_tick.n.01', 'name': 'hard_tick'}, {'id': 2463, 'synset': 'ixodes_dammini.n.01', 'name': 'Ixodes_dammini'}, {'id': 2464, 'synset': 'ixodes_neotomae.n.01', 'name': 'Ixodes_neotomae'}, {'id': 2465, 'synset': 'ixodes_pacificus.n.01', 'name': 'Ixodes_pacificus'}, {'id': 2466, 'synset': 'ixodes_scapularis.n.01', 'name': 'Ixodes_scapularis'}, {'id': 2467, 'synset': 'sheep-tick.n.02', 'name': 'sheep-tick'}, {'id': 2468, 'synset': 'ixodes_persulcatus.n.01', 'name': 'Ixodes_persulcatus'}, {'id': 2469, 'synset': 'ixodes_dentatus.n.01', 'name': 'Ixodes_dentatus'}, {'id': 2470, 'synset': 'ixodes_spinipalpis.n.01', 'name': 'Ixodes_spinipalpis'}, {'id': 2471, 'synset': 'wood_tick.n.01', 'name': 'wood_tick'}, {'id': 2472, 'synset': 'soft_tick.n.01', 'name': 'soft_tick'}, {'id': 2473, 'synset': 'mite.n.02', 'name': 'mite'}, {'id': 2474, 'synset': 'web-spinning_mite.n.01', 'name': 'web-spinning_mite'}, {'id': 2475, 'synset': 'acarid.n.01', 'name': 'acarid'}, {'id': 2476, 'synset': 'trombidiid.n.01', 'name': 'trombidiid'}, {'id': 2477, 'synset': 'trombiculid.n.01', 'name': 'trombiculid'}, {'id': 2478, 'synset': 'harvest_mite.n.01', 'name': 'harvest_mite'}, {'id': 2479, 'synset': 'acarus.n.01', 'name': 'acarus'}, {'id': 2480, 'synset': 'itch_mite.n.01', 'name': 'itch_mite'}, {'id': 2481, 'synset': 'rust_mite.n.01', 'name': 'rust_mite'}, {'id': 2482, 'synset': 'spider_mite.n.01', 'name': 'spider_mite'}, {'id': 2483, 'synset': 'red_spider.n.01', 'name': 'red_spider'}, {'id': 2484, 'synset': 'myriapod.n.01', 'name': 'myriapod'}, {'id': 2485, 'synset': 'garden_centipede.n.01', 'name': 'garden_centipede'}, {'id': 2486, 'synset': 'tardigrade.n.01', 'name': 'tardigrade'}, {'id': 2487, 'synset': 'centipede.n.01', 'name': 'centipede'}, {'id': 2488, 'synset': 'house_centipede.n.01', 'name': 'house_centipede'}, {'id': 2489, 'synset': 'millipede.n.01', 'name': 'millipede'}, {'id': 2490, 'synset': 'sea_spider.n.01', 'name': 'sea_spider'}, {'id': 2491, 'synset': 'merostomata.n.01', 'name': 'Merostomata'}, {'id': 2492, 'synset': 'horseshoe_crab.n.01', 'name': 'horseshoe_crab'}, {'id': 2493, 'synset': 'asian_horseshoe_crab.n.01', 'name': 'Asian_horseshoe_crab'}, {'id': 2494, 'synset': 'eurypterid.n.01', 'name': 'eurypterid'}, {'id': 2495, 'synset': 'tongue_worm.n.01', 'name': 'tongue_worm'}, {'id': 2496, 'synset': 'gallinaceous_bird.n.01', 'name': 'gallinaceous_bird'}, {'id': 2497, 'synset': 'domestic_fowl.n.01', 'name': 'domestic_fowl'}, {'id': 2498, 'synset': 'dorking.n.01', 'name': 'Dorking'}, {'id': 2499, 'synset': 'plymouth_rock.n.02', 'name': 'Plymouth_Rock'}, {'id': 2500, 'synset': 'cornish.n.02', 'name': 'Cornish'}, {'id': 2501, 'synset': 'rock_cornish.n.01', 'name': 'Rock_Cornish'}, {'id': 2502, 'synset': 'game_fowl.n.01', 'name': 'game_fowl'}, {'id': 2503, 'synset': 'cochin.n.01', 'name': 'cochin'}, {'id': 2504, 'synset': 'jungle_fowl.n.01', 'name': 'jungle_fowl'}, {'id': 2505, 'synset': 'jungle_cock.n.01', 'name': 'jungle_cock'}, {'id': 2506, 'synset': 'jungle_hen.n.01', 'name': 'jungle_hen'}, {'id': 2507, 'synset': 'red_jungle_fowl.n.01', 'name': 'red_jungle_fowl'}, {'id': 2508, 'synset': 'bantam.n.01', 'name': 'bantam'}, {'id': 2509, 'synset': 'chick.n.01', 'name': 'chick'}, {'id': 2510, 'synset': 'cockerel.n.01', 'name': 'cockerel'}, {'id': 2511, 'synset': 'capon.n.02', 'name': 'capon'}, {'id': 2512, 'synset': 'hen.n.01', 'name': 'hen'}, {'id': 2513, 'synset': 'cackler.n.01', 'name': 'cackler'}, {'id': 2514, 'synset': 'brood_hen.n.01', 'name': 'brood_hen'}, {'id': 2515, 'synset': 'mother_hen.n.02', 'name': 'mother_hen'}, {'id': 2516, 'synset': 'layer.n.04', 'name': 'layer'}, {'id': 2517, 'synset': 'pullet.n.02', 'name': 'pullet'}, {'id': 2518, 'synset': 'spring_chicken.n.02', 'name': 'spring_chicken'}, {'id': 2519, 'synset': 'rhode_island_red.n.01', 'name': 'Rhode_Island_red'}, {'id': 2520, 'synset': 'dominique.n.01', 'name': 'Dominique'}, {'id': 2521, 'synset': 'orpington.n.01', 'name': 'Orpington'}, {'id': 2522, 'synset': 'turkey.n.01', 'name': 'turkey'}, {'id': 2523, 'synset': 'turkey_cock.n.01', 'name': 'turkey_cock'}, {'id': 2524, 'synset': 'ocellated_turkey.n.01', 'name': 'ocellated_turkey'}, {'id': 2525, 'synset': 'grouse.n.02', 'name': 'grouse'}, {'id': 2526, 'synset': 'black_grouse.n.01', 'name': 'black_grouse'}, {'id': 2527, 'synset': 'european_black_grouse.n.01', 'name': 'European_black_grouse'}, {'id': 2528, 'synset': 'asian_black_grouse.n.01', 'name': 'Asian_black_grouse'}, {'id': 2529, 'synset': 'blackcock.n.01', 'name': 'blackcock'}, {'id': 2530, 'synset': 'greyhen.n.01', 'name': 'greyhen'}, {'id': 2531, 'synset': 'ptarmigan.n.01', 'name': 'ptarmigan'}, {'id': 2532, 'synset': 'red_grouse.n.01', 'name': 'red_grouse'}, {'id': 2533, 'synset': 'moorhen.n.02', 'name': 'moorhen'}, {'id': 2534, 'synset': 'capercaillie.n.01', 'name': 'capercaillie'}, {'id': 2535, 'synset': 'spruce_grouse.n.01', 'name': 'spruce_grouse'}, {'id': 2536, 'synset': 'sage_grouse.n.01', 'name': 'sage_grouse'}, {'id': 2537, 'synset': 'ruffed_grouse.n.01', 'name': 'ruffed_grouse'}, {'id': 2538, 'synset': 'sharp-tailed_grouse.n.01', 'name': 'sharp-tailed_grouse'}, {'id': 2539, 'synset': 'prairie_chicken.n.01', 'name': 'prairie_chicken'}, {'id': 2540, 'synset': 'greater_prairie_chicken.n.01', 'name': 'greater_prairie_chicken'}, {'id': 2541, 'synset': 'lesser_prairie_chicken.n.01', 'name': 'lesser_prairie_chicken'}, {'id': 2542, 'synset': 'heath_hen.n.01', 'name': 'heath_hen'}, {'id': 2543, 'synset': 'guan.n.01', 'name': 'guan'}, {'id': 2544, 'synset': 'curassow.n.01', 'name': 'curassow'}, {'id': 2545, 'synset': 'piping_guan.n.01', 'name': 'piping_guan'}, {'id': 2546, 'synset': 'chachalaca.n.01', 'name': 'chachalaca'}, {'id': 2547, 'synset': 'texas_chachalaca.n.01', 'name': 'Texas_chachalaca'}, {'id': 2548, 'synset': 'megapode.n.01', 'name': 'megapode'}, {'id': 2549, 'synset': 'mallee_fowl.n.01', 'name': 'mallee_fowl'}, {'id': 2550, 'synset': 'mallee_hen.n.01', 'name': 'mallee_hen'}, {'id': 2551, 'synset': 'brush_turkey.n.01', 'name': 'brush_turkey'}, {'id': 2552, 'synset': 'maleo.n.01', 'name': 'maleo'}, {'id': 2553, 'synset': 'phasianid.n.01', 'name': 'phasianid'}, {'id': 2554, 'synset': 'pheasant.n.01', 'name': 'pheasant'}, {'id': 2555, 'synset': 'ring-necked_pheasant.n.01', 'name': 'ring-necked_pheasant'}, {'id': 2556, 'synset': 'afropavo.n.01', 'name': 'afropavo'}, {'id': 2557, 'synset': 'argus.n.02', 'name': 'argus'}, {'id': 2558, 'synset': 'golden_pheasant.n.01', 'name': 'golden_pheasant'}, {'id': 2559, 'synset': 'bobwhite.n.01', 'name': 'bobwhite'}, {'id': 2560, 'synset': 'northern_bobwhite.n.01', 'name': 'northern_bobwhite'}, {'id': 2561, 'synset': 'old_world_quail.n.01', 'name': 'Old_World_quail'}, {'id': 2562, 'synset': 'migratory_quail.n.01', 'name': 'migratory_quail'}, {'id': 2563, 'synset': 'monal.n.01', 'name': 'monal'}, {'id': 2564, 'synset': 'peafowl.n.01', 'name': 'peafowl'}, {'id': 2565, 'synset': 'peachick.n.01', 'name': 'peachick'}, {'id': 2566, 'synset': 'peacock.n.02', 'name': 'peacock'}, {'id': 2567, 'synset': 'peahen.n.01', 'name': 'peahen'}, {'id': 2568, 'synset': 'blue_peafowl.n.01', 'name': 'blue_peafowl'}, {'id': 2569, 'synset': 'green_peafowl.n.01', 'name': 'green_peafowl'}, {'id': 2570, 'synset': 'quail.n.02', 'name': 'quail'}, {'id': 2571, 'synset': 'california_quail.n.01', 'name': 'California_quail'}, {'id': 2572, 'synset': 'tragopan.n.01', 'name': 'tragopan'}, {'id': 2573, 'synset': 'partridge.n.03', 'name': 'partridge'}, {'id': 2574, 'synset': 'hungarian_partridge.n.01', 'name': 'Hungarian_partridge'}, {'id': 2575, 'synset': 'red-legged_partridge.n.01', 'name': 'red-legged_partridge'}, {'id': 2576, 'synset': 'greek_partridge.n.01', 'name': 'Greek_partridge'}, {'id': 2577, 'synset': 'mountain_quail.n.01', 'name': 'mountain_quail'}, {'id': 2578, 'synset': 'guinea_fowl.n.01', 'name': 'guinea_fowl'}, {'id': 2579, 'synset': 'guinea_hen.n.02', 'name': 'guinea_hen'}, {'id': 2580, 'synset': 'hoatzin.n.01', 'name': 'hoatzin'}, {'id': 2581, 'synset': 'tinamou.n.01', 'name': 'tinamou'}, {'id': 2582, 'synset': 'columbiform_bird.n.01', 'name': 'columbiform_bird'}, {'id': 2583, 'synset': 'dodo.n.02', 'name': 'dodo'}, {'id': 2584, 'synset': 'pouter_pigeon.n.01', 'name': 'pouter_pigeon'}, {'id': 2585, 'synset': 'rock_dove.n.01', 'name': 'rock_dove'}, {'id': 2586, 'synset': 'band-tailed_pigeon.n.01', 'name': 'band-tailed_pigeon'}, {'id': 2587, 'synset': 'wood_pigeon.n.01', 'name': 'wood_pigeon'}, {'id': 2588, 'synset': 'turtledove.n.02', 'name': 'turtledove'}, {'id': 2589, 'synset': 'streptopelia_turtur.n.01', 'name': 'Streptopelia_turtur'}, {'id': 2590, 'synset': 'ringdove.n.01', 'name': 'ringdove'}, {'id': 2591, 'synset': 'australian_turtledove.n.01', 'name': 'Australian_turtledove'}, {'id': 2592, 'synset': 'mourning_dove.n.01', 'name': 'mourning_dove'}, {'id': 2593, 'synset': 'domestic_pigeon.n.01', 'name': 'domestic_pigeon'}, {'id': 2594, 'synset': 'squab.n.03', 'name': 'squab'}, {'id': 2595, 'synset': 'fairy_swallow.n.01', 'name': 'fairy_swallow'}, {'id': 2596, 'synset': 'roller.n.07', 'name': 'roller'}, {'id': 2597, 'synset': 'homing_pigeon.n.01', 'name': 'homing_pigeon'}, {'id': 2598, 'synset': 'carrier_pigeon.n.01', 'name': 'carrier_pigeon'}, {'id': 2599, 'synset': 'passenger_pigeon.n.01', 'name': 'passenger_pigeon'}, {'id': 2600, 'synset': 'sandgrouse.n.01', 'name': 'sandgrouse'}, {'id': 2601, 'synset': 'painted_sandgrouse.n.01', 'name': 'painted_sandgrouse'}, {'id': 2602, 'synset': 'pin-tailed_sandgrouse.n.01', 'name': 'pin-tailed_sandgrouse'}, {'id': 2603, 'synset': "pallas's_sandgrouse.n.01", 'name': "pallas's_sandgrouse"}, {'id': 2604, 'synset': 'popinjay.n.02', 'name': 'popinjay'}, {'id': 2605, 'synset': 'poll.n.04', 'name': 'poll'}, {'id': 2606, 'synset': 'african_grey.n.01', 'name': 'African_grey'}, {'id': 2607, 'synset': 'amazon.n.04', 'name': 'amazon'}, {'id': 2608, 'synset': 'macaw.n.01', 'name': 'macaw'}, {'id': 2609, 'synset': 'kea.n.01', 'name': 'kea'}, {'id': 2610, 'synset': 'cockatoo.n.01', 'name': 'cockatoo'}, {'id': 2611, 'synset': 'sulphur-crested_cockatoo.n.01', 'name': 'sulphur-crested_cockatoo'}, {'id': 2612, 'synset': 'pink_cockatoo.n.01', 'name': 'pink_cockatoo'}, {'id': 2613, 'synset': 'cockateel.n.01', 'name': 'cockateel'}, {'id': 2614, 'synset': 'lovebird.n.02', 'name': 'lovebird'}, {'id': 2615, 'synset': 'lory.n.01', 'name': 'lory'}, {'id': 2616, 'synset': 'lorikeet.n.01', 'name': 'lorikeet'}, {'id': 2617, 'synset': 'varied_lorikeet.n.01', 'name': 'varied_Lorikeet'}, {'id': 2618, 'synset': 'rainbow_lorikeet.n.01', 'name': 'rainbow_lorikeet'}, {'id': 2619, 'synset': 'carolina_parakeet.n.01', 'name': 'Carolina_parakeet'}, {'id': 2620, 'synset': 'budgerigar.n.01', 'name': 'budgerigar'}, {'id': 2621, 'synset': 'ring-necked_parakeet.n.01', 'name': 'ring-necked_parakeet'}, {'id': 2622, 'synset': 'cuculiform_bird.n.01', 'name': 'cuculiform_bird'}, {'id': 2623, 'synset': 'cuckoo.n.02', 'name': 'cuckoo'}, {'id': 2624, 'synset': 'european_cuckoo.n.01', 'name': 'European_cuckoo'}, {'id': 2625, 'synset': 'black-billed_cuckoo.n.01', 'name': 'black-billed_cuckoo'}, {'id': 2626, 'synset': 'roadrunner.n.01', 'name': 'roadrunner'}, {'id': 2627, 'synset': 'ani.n.01', 'name': 'ani'}, {'id': 2628, 'synset': 'coucal.n.01', 'name': 'coucal'}, {'id': 2629, 'synset': 'crow_pheasant.n.01', 'name': 'crow_pheasant'}, {'id': 2630, 'synset': 'touraco.n.01', 'name': 'touraco'}, {'id': 2631, 'synset': 'coraciiform_bird.n.01', 'name': 'coraciiform_bird'}, {'id': 2632, 'synset': 'roller.n.06', 'name': 'roller'}, {'id': 2633, 'synset': 'european_roller.n.01', 'name': 'European_roller'}, {'id': 2634, 'synset': 'ground_roller.n.01', 'name': 'ground_roller'}, {'id': 2635, 'synset': 'kingfisher.n.01', 'name': 'kingfisher'}, {'id': 2636, 'synset': 'eurasian_kingfisher.n.01', 'name': 'Eurasian_kingfisher'}, {'id': 2637, 'synset': 'belted_kingfisher.n.01', 'name': 'belted_kingfisher'}, {'id': 2638, 'synset': 'kookaburra.n.01', 'name': 'kookaburra'}, {'id': 2639, 'synset': 'bee_eater.n.01', 'name': 'bee_eater'}, {'id': 2640, 'synset': 'hornbill.n.01', 'name': 'hornbill'}, {'id': 2641, 'synset': 'hoopoe.n.01', 'name': 'hoopoe'}, {'id': 2642, 'synset': 'euopean_hoopoe.n.01', 'name': 'Euopean_hoopoe'}, {'id': 2643, 'synset': 'wood_hoopoe.n.01', 'name': 'wood_hoopoe'}, {'id': 2644, 'synset': 'motmot.n.01', 'name': 'motmot'}, {'id': 2645, 'synset': 'tody.n.01', 'name': 'tody'}, {'id': 2646, 'synset': 'apodiform_bird.n.01', 'name': 'apodiform_bird'}, {'id': 2647, 'synset': 'swift.n.03', 'name': 'swift'}, {'id': 2648, 'synset': 'european_swift.n.01', 'name': 'European_swift'}, {'id': 2649, 'synset': 'chimney_swift.n.01', 'name': 'chimney_swift'}, {'id': 2650, 'synset': 'swiftlet.n.01', 'name': 'swiftlet'}, {'id': 2651, 'synset': 'tree_swift.n.01', 'name': 'tree_swift'}, {'id': 2652, 'synset': 'archilochus_colubris.n.01', 'name': 'Archilochus_colubris'}, {'id': 2653, 'synset': 'thornbill.n.01', 'name': 'thornbill'}, {'id': 2654, 'synset': 'goatsucker.n.01', 'name': 'goatsucker'}, {'id': 2655, 'synset': 'european_goatsucker.n.01', 'name': 'European_goatsucker'}, {'id': 2656, 'synset': "chuck-will's-widow.n.01", 'name': "chuck-will's-widow"}, {'id': 2657, 'synset': 'whippoorwill.n.01', 'name': 'whippoorwill'}, {'id': 2658, 'synset': 'poorwill.n.01', 'name': 'poorwill'}, {'id': 2659, 'synset': 'frogmouth.n.01', 'name': 'frogmouth'}, {'id': 2660, 'synset': 'oilbird.n.01', 'name': 'oilbird'}, {'id': 2661, 'synset': 'piciform_bird.n.01', 'name': 'piciform_bird'}, {'id': 2662, 'synset': 'woodpecker.n.01', 'name': 'woodpecker'}, {'id': 2663, 'synset': 'green_woodpecker.n.01', 'name': 'green_woodpecker'}, {'id': 2664, 'synset': 'downy_woodpecker.n.01', 'name': 'downy_woodpecker'}, {'id': 2665, 'synset': 'flicker.n.02', 'name': 'flicker'}, {'id': 2666, 'synset': 'yellow-shafted_flicker.n.01', 'name': 'yellow-shafted_flicker'}, {'id': 2667, 'synset': 'gilded_flicker.n.01', 'name': 'gilded_flicker'}, {'id': 2668, 'synset': 'red-shafted_flicker.n.01', 'name': 'red-shafted_flicker'}, {'id': 2669, 'synset': 'ivorybill.n.01', 'name': 'ivorybill'}, {'id': 2670, 'synset': 'redheaded_woodpecker.n.01', 'name': 'redheaded_woodpecker'}, {'id': 2671, 'synset': 'sapsucker.n.01', 'name': 'sapsucker'}, {'id': 2672, 'synset': 'yellow-bellied_sapsucker.n.01', 'name': 'yellow-bellied_sapsucker'}, {'id': 2673, 'synset': 'red-breasted_sapsucker.n.01', 'name': 'red-breasted_sapsucker'}, {'id': 2674, 'synset': 'wryneck.n.02', 'name': 'wryneck'}, {'id': 2675, 'synset': 'piculet.n.01', 'name': 'piculet'}, {'id': 2676, 'synset': 'barbet.n.01', 'name': 'barbet'}, {'id': 2677, 'synset': 'puffbird.n.01', 'name': 'puffbird'}, {'id': 2678, 'synset': 'honey_guide.n.01', 'name': 'honey_guide'}, {'id': 2679, 'synset': 'jacamar.n.01', 'name': 'jacamar'}, {'id': 2680, 'synset': 'toucan.n.01', 'name': 'toucan'}, {'id': 2681, 'synset': 'toucanet.n.01', 'name': 'toucanet'}, {'id': 2682, 'synset': 'trogon.n.01', 'name': 'trogon'}, {'id': 2683, 'synset': 'quetzal.n.02', 'name': 'quetzal'}, {'id': 2684, 'synset': 'resplendent_quetzel.n.01', 'name': 'resplendent_quetzel'}, {'id': 2685, 'synset': 'aquatic_bird.n.01', 'name': 'aquatic_bird'}, {'id': 2686, 'synset': 'waterfowl.n.01', 'name': 'waterfowl'}, {'id': 2687, 'synset': 'anseriform_bird.n.01', 'name': 'anseriform_bird'}, {'id': 2688, 'synset': 'drake.n.02', 'name': 'drake'}, {'id': 2689, 'synset': 'quack-quack.n.01', 'name': 'quack-quack'}, {'id': 2690, 'synset': 'diving_duck.n.01', 'name': 'diving_duck'}, {'id': 2691, 'synset': 'dabbling_duck.n.01', 'name': 'dabbling_duck'}, {'id': 2692, 'synset': 'black_duck.n.01', 'name': 'black_duck'}, {'id': 2693, 'synset': 'teal.n.02', 'name': 'teal'}, {'id': 2694, 'synset': 'greenwing.n.01', 'name': 'greenwing'}, {'id': 2695, 'synset': 'bluewing.n.01', 'name': 'bluewing'}, {'id': 2696, 'synset': 'garganey.n.01', 'name': 'garganey'}, {'id': 2697, 'synset': 'widgeon.n.01', 'name': 'widgeon'}, {'id': 2698, 'synset': 'american_widgeon.n.01', 'name': 'American_widgeon'}, {'id': 2699, 'synset': 'shoveler.n.02', 'name': 'shoveler'}, {'id': 2700, 'synset': 'pintail.n.01', 'name': 'pintail'}, {'id': 2701, 'synset': 'sheldrake.n.02', 'name': 'sheldrake'}, {'id': 2702, 'synset': 'shelduck.n.01', 'name': 'shelduck'}, {'id': 2703, 'synset': 'ruddy_duck.n.01', 'name': 'ruddy_duck'}, {'id': 2704, 'synset': 'bufflehead.n.01', 'name': 'bufflehead'}, {'id': 2705, 'synset': 'goldeneye.n.02', 'name': 'goldeneye'}, {'id': 2706, 'synset': "barrow's_goldeneye.n.01", 'name': "Barrow's_goldeneye"}, {'id': 2707, 'synset': 'canvasback.n.01', 'name': 'canvasback'}, {'id': 2708, 'synset': 'pochard.n.01', 'name': 'pochard'}, {'id': 2709, 'synset': 'redhead.n.02', 'name': 'redhead'}, {'id': 2710, 'synset': 'scaup.n.01', 'name': 'scaup'}, {'id': 2711, 'synset': 'greater_scaup.n.01', 'name': 'greater_scaup'}, {'id': 2712, 'synset': 'lesser_scaup.n.01', 'name': 'lesser_scaup'}, {'id': 2713, 'synset': 'wild_duck.n.01', 'name': 'wild_duck'}, {'id': 2714, 'synset': 'wood_duck.n.01', 'name': 'wood_duck'}, {'id': 2715, 'synset': 'wood_drake.n.01', 'name': 'wood_drake'}, {'id': 2716, 'synset': 'mandarin_duck.n.01', 'name': 'mandarin_duck'}, {'id': 2717, 'synset': 'muscovy_duck.n.01', 'name': 'muscovy_duck'}, {'id': 2718, 'synset': 'sea_duck.n.01', 'name': 'sea_duck'}, {'id': 2719, 'synset': 'eider.n.01', 'name': 'eider'}, {'id': 2720, 'synset': 'scoter.n.01', 'name': 'scoter'}, {'id': 2721, 'synset': 'common_scoter.n.01', 'name': 'common_scoter'}, {'id': 2722, 'synset': 'old_squaw.n.01', 'name': 'old_squaw'}, {'id': 2723, 'synset': 'merganser.n.01', 'name': 'merganser'}, {'id': 2724, 'synset': 'goosander.n.01', 'name': 'goosander'}, {'id': 2725, 'synset': 'american_merganser.n.01', 'name': 'American_merganser'}, {'id': 2726, 'synset': 'red-breasted_merganser.n.01', 'name': 'red-breasted_merganser'}, {'id': 2727, 'synset': 'smew.n.01', 'name': 'smew'}, {'id': 2728, 'synset': 'hooded_merganser.n.01', 'name': 'hooded_merganser'}, {'id': 2729, 'synset': 'gosling.n.01', 'name': 'gosling'}, {'id': 2730, 'synset': 'gander.n.01', 'name': 'gander'}, {'id': 2731, 'synset': 'chinese_goose.n.01', 'name': 'Chinese_goose'}, {'id': 2732, 'synset': 'greylag.n.01', 'name': 'greylag'}, {'id': 2733, 'synset': 'blue_goose.n.01', 'name': 'blue_goose'}, {'id': 2734, 'synset': 'snow_goose.n.01', 'name': 'snow_goose'}, {'id': 2735, 'synset': 'brant.n.01', 'name': 'brant'}, {'id': 2736, 'synset': 'common_brant_goose.n.01', 'name': 'common_brant_goose'}, {'id': 2737, 'synset': 'honker.n.03', 'name': 'honker'}, {'id': 2738, 'synset': 'barnacle_goose.n.01', 'name': 'barnacle_goose'}, {'id': 2739, 'synset': 'coscoroba.n.01', 'name': 'coscoroba'}, {'id': 2740, 'synset': 'swan.n.01', 'name': 'swan'}, {'id': 2741, 'synset': 'cob.n.04', 'name': 'cob'}, {'id': 2742, 'synset': 'pen.n.05', 'name': 'pen'}, {'id': 2743, 'synset': 'cygnet.n.01', 'name': 'cygnet'}, {'id': 2744, 'synset': 'mute_swan.n.01', 'name': 'mute_swan'}, {'id': 2745, 'synset': 'whooper.n.02', 'name': 'whooper'}, {'id': 2746, 'synset': 'tundra_swan.n.01', 'name': 'tundra_swan'}, {'id': 2747, 'synset': 'whistling_swan.n.01', 'name': 'whistling_swan'}, {'id': 2748, 'synset': "bewick's_swan.n.01", 'name': "Bewick's_swan"}, {'id': 2749, 'synset': 'trumpeter.n.04', 'name': 'trumpeter'}, {'id': 2750, 'synset': 'black_swan.n.01', 'name': 'black_swan'}, {'id': 2751, 'synset': 'screamer.n.03', 'name': 'screamer'}, {'id': 2752, 'synset': 'horned_screamer.n.01', 'name': 'horned_screamer'}, {'id': 2753, 'synset': 'crested_screamer.n.01', 'name': 'crested_screamer'}, {'id': 2754, 'synset': 'chaja.n.01', 'name': 'chaja'}, {'id': 2755, 'synset': 'mammal.n.01', 'name': 'mammal'}, {'id': 2756, 'synset': 'female_mammal.n.01', 'name': 'female_mammal'}, {'id': 2757, 'synset': 'tusker.n.01', 'name': 'tusker'}, {'id': 2758, 'synset': 'prototherian.n.01', 'name': 'prototherian'}, {'id': 2759, 'synset': 'monotreme.n.01', 'name': 'monotreme'}, {'id': 2760, 'synset': 'echidna.n.02', 'name': 'echidna'}, {'id': 2761, 'synset': 'echidna.n.01', 'name': 'echidna'}, {'id': 2762, 'synset': 'platypus.n.01', 'name': 'platypus'}, {'id': 2763, 'synset': 'marsupial.n.01', 'name': 'marsupial'}, {'id': 2764, 'synset': 'opossum.n.02', 'name': 'opossum'}, {'id': 2765, 'synset': 'common_opossum.n.01', 'name': 'common_opossum'}, {'id': 2766, 'synset': 'crab-eating_opossum.n.01', 'name': 'crab-eating_opossum'}, {'id': 2767, 'synset': 'opossum_rat.n.01', 'name': 'opossum_rat'}, {'id': 2768, 'synset': 'bandicoot.n.01', 'name': 'bandicoot'}, {'id': 2769, 'synset': 'rabbit-eared_bandicoot.n.01', 'name': 'rabbit-eared_bandicoot'}, {'id': 2770, 'synset': 'kangaroo.n.01', 'name': 'kangaroo'}, {'id': 2771, 'synset': 'giant_kangaroo.n.01', 'name': 'giant_kangaroo'}, {'id': 2772, 'synset': 'wallaby.n.01', 'name': 'wallaby'}, {'id': 2773, 'synset': 'common_wallaby.n.01', 'name': 'common_wallaby'}, {'id': 2774, 'synset': 'hare_wallaby.n.01', 'name': 'hare_wallaby'}, {'id': 2775, 'synset': 'nail-tailed_wallaby.n.01', 'name': 'nail-tailed_wallaby'}, {'id': 2776, 'synset': 'rock_wallaby.n.01', 'name': 'rock_wallaby'}, {'id': 2777, 'synset': 'pademelon.n.01', 'name': 'pademelon'}, {'id': 2778, 'synset': 'tree_wallaby.n.01', 'name': 'tree_wallaby'}, {'id': 2779, 'synset': 'musk_kangaroo.n.01', 'name': 'musk_kangaroo'}, {'id': 2780, 'synset': 'rat_kangaroo.n.01', 'name': 'rat_kangaroo'}, {'id': 2781, 'synset': 'potoroo.n.01', 'name': 'potoroo'}, {'id': 2782, 'synset': 'bettong.n.01', 'name': 'bettong'}, {'id': 2783, 'synset': 'jerboa_kangaroo.n.01', 'name': 'jerboa_kangaroo'}, {'id': 2784, 'synset': 'phalanger.n.01', 'name': 'phalanger'}, {'id': 2785, 'synset': 'cuscus.n.01', 'name': 'cuscus'}, {'id': 2786, 'synset': 'brush-tailed_phalanger.n.01', 'name': 'brush-tailed_phalanger'}, {'id': 2787, 'synset': 'flying_phalanger.n.01', 'name': 'flying_phalanger'}, {'id': 2788, 'synset': 'wombat.n.01', 'name': 'wombat'}, {'id': 2789, 'synset': 'dasyurid_marsupial.n.01', 'name': 'dasyurid_marsupial'}, {'id': 2790, 'synset': 'dasyure.n.01', 'name': 'dasyure'}, {'id': 2791, 'synset': 'eastern_dasyure.n.01', 'name': 'eastern_dasyure'}, {'id': 2792, 'synset': 'native_cat.n.01', 'name': 'native_cat'}, {'id': 2793, 'synset': 'thylacine.n.01', 'name': 'thylacine'}, {'id': 2794, 'synset': 'tasmanian_devil.n.01', 'name': 'Tasmanian_devil'}, {'id': 2795, 'synset': 'pouched_mouse.n.01', 'name': 'pouched_mouse'}, {'id': 2796, 'synset': 'numbat.n.01', 'name': 'numbat'}, {'id': 2797, 'synset': 'pouched_mole.n.01', 'name': 'pouched_mole'}, {'id': 2798, 'synset': 'placental.n.01', 'name': 'placental'}, {'id': 2799, 'synset': 'livestock.n.01', 'name': 'livestock'}, {'id': 2800, 'synset': 'cow.n.02', 'name': 'cow'}, {'id': 2801, 'synset': 'calf.n.04', 'name': 'calf'}, {'id': 2802, 'synset': 'yearling.n.03', 'name': 'yearling'}, {'id': 2803, 'synset': 'buck.n.05', 'name': 'buck'}, {'id': 2804, 'synset': 'doe.n.02', 'name': 'doe'}, {'id': 2805, 'synset': 'insectivore.n.01', 'name': 'insectivore'}, {'id': 2806, 'synset': 'mole.n.06', 'name': 'mole'}, {'id': 2807, 'synset': 'starnose_mole.n.01', 'name': 'starnose_mole'}, {'id': 2808, 'synset': "brewer's_mole.n.01", 'name': "brewer's_mole"}, {'id': 2809, 'synset': 'golden_mole.n.01', 'name': 'golden_mole'}, {'id': 2810, 'synset': 'shrew_mole.n.01', 'name': 'shrew_mole'}, {'id': 2811, 'synset': 'asiatic_shrew_mole.n.01', 'name': 'Asiatic_shrew_mole'}, {'id': 2812, 'synset': 'american_shrew_mole.n.01', 'name': 'American_shrew_mole'}, {'id': 2813, 'synset': 'shrew.n.02', 'name': 'shrew'}, {'id': 2814, 'synset': 'common_shrew.n.01', 'name': 'common_shrew'}, {'id': 2815, 'synset': 'masked_shrew.n.01', 'name': 'masked_shrew'}, {'id': 2816, 'synset': 'short-tailed_shrew.n.01', 'name': 'short-tailed_shrew'}, {'id': 2817, 'synset': 'water_shrew.n.01', 'name': 'water_shrew'}, {'id': 2818, 'synset': 'american_water_shrew.n.01', 'name': 'American_water_shrew'}, {'id': 2819, 'synset': 'european_water_shrew.n.01', 'name': 'European_water_shrew'}, {'id': 2820, 'synset': 'mediterranean_water_shrew.n.01', 'name': 'Mediterranean_water_shrew'}, {'id': 2821, 'synset': 'least_shrew.n.01', 'name': 'least_shrew'}, {'id': 2822, 'synset': 'hedgehog.n.02', 'name': 'hedgehog'}, {'id': 2823, 'synset': 'tenrec.n.01', 'name': 'tenrec'}, {'id': 2824, 'synset': 'tailless_tenrec.n.01', 'name': 'tailless_tenrec'}, {'id': 2825, 'synset': 'otter_shrew.n.01', 'name': 'otter_shrew'}, {'id': 2826, 'synset': 'eiderdown.n.02', 'name': 'eiderdown'}, {'id': 2827, 'synset': 'aftershaft.n.01', 'name': 'aftershaft'}, {'id': 2828, 'synset': 'sickle_feather.n.01', 'name': 'sickle_feather'}, {'id': 2829, 'synset': 'contour_feather.n.01', 'name': 'contour_feather'}, {'id': 2830, 'synset': 'bastard_wing.n.01', 'name': 'bastard_wing'}, {'id': 2831, 'synset': 'saddle_hackle.n.01', 'name': 'saddle_hackle'}, {'id': 2832, 'synset': 'encolure.n.01', 'name': 'encolure'}, {'id': 2833, 'synset': 'hair.n.06', 'name': 'hair'}, {'id': 2834, 'synset': 'squama.n.01', 'name': 'squama'}, {'id': 2835, 'synset': 'scute.n.01', 'name': 'scute'}, {'id': 2836, 'synset': 'sclerite.n.01', 'name': 'sclerite'}, {'id': 2837, 'synset': 'plastron.n.05', 'name': 'plastron'}, {'id': 2838, 'synset': 'scallop_shell.n.01', 'name': 'scallop_shell'}, {'id': 2839, 'synset': 'oyster_shell.n.01', 'name': 'oyster_shell'}, {'id': 2840, 'synset': 'theca.n.02', 'name': 'theca'}, {'id': 2841, 'synset': 'invertebrate.n.01', 'name': 'invertebrate'}, {'id': 2842, 'synset': 'sponge.n.04', 'name': 'sponge'}, {'id': 2843, 'synset': 'choanocyte.n.01', 'name': 'choanocyte'}, {'id': 2844, 'synset': 'glass_sponge.n.01', 'name': 'glass_sponge'}, {'id': 2845, 'synset': "venus's_flower_basket.n.01", 'name': "Venus's_flower_basket"}, {'id': 2846, 'synset': 'metazoan.n.01', 'name': 'metazoan'}, {'id': 2847, 'synset': 'coelenterate.n.01', 'name': 'coelenterate'}, {'id': 2848, 'synset': 'planula.n.01', 'name': 'planula'}, {'id': 2849, 'synset': 'polyp.n.02', 'name': 'polyp'}, {'id': 2850, 'synset': 'medusa.n.02', 'name': 'medusa'}, {'id': 2851, 'synset': 'jellyfish.n.02', 'name': 'jellyfish'}, {'id': 2852, 'synset': 'scyphozoan.n.01', 'name': 'scyphozoan'}, {'id': 2853, 'synset': 'chrysaora_quinquecirrha.n.01', 'name': 'Chrysaora_quinquecirrha'}, {'id': 2854, 'synset': 'hydrozoan.n.01', 'name': 'hydrozoan'}, {'id': 2855, 'synset': 'hydra.n.04', 'name': 'hydra'}, {'id': 2856, 'synset': 'siphonophore.n.01', 'name': 'siphonophore'}, {'id': 2857, 'synset': 'nanomia.n.01', 'name': 'nanomia'}, {'id': 2858, 'synset': 'portuguese_man-of-war.n.01', 'name': 'Portuguese_man-of-war'}, {'id': 2859, 'synset': 'praya.n.01', 'name': 'praya'}, {'id': 2860, 'synset': 'apolemia.n.01', 'name': 'apolemia'}, {'id': 2861, 'synset': 'anthozoan.n.01', 'name': 'anthozoan'}, {'id': 2862, 'synset': 'sea_anemone.n.01', 'name': 'sea_anemone'}, {'id': 2863, 'synset': 'actinia.n.02', 'name': 'actinia'}, {'id': 2864, 'synset': 'sea_pen.n.01', 'name': 'sea_pen'}, {'id': 2865, 'synset': 'coral.n.04', 'name': 'coral'}, {'id': 2866, 'synset': 'gorgonian.n.01', 'name': 'gorgonian'}, {'id': 2867, 'synset': 'sea_feather.n.01', 'name': 'sea_feather'}, {'id': 2868, 'synset': 'sea_fan.n.01', 'name': 'sea_fan'}, {'id': 2869, 'synset': 'red_coral.n.02', 'name': 'red_coral'}, {'id': 2870, 'synset': 'stony_coral.n.01', 'name': 'stony_coral'}, {'id': 2871, 'synset': 'brain_coral.n.01', 'name': 'brain_coral'}, {'id': 2872, 'synset': 'staghorn_coral.n.01', 'name': 'staghorn_coral'}, {'id': 2873, 'synset': 'mushroom_coral.n.01', 'name': 'mushroom_coral'}, {'id': 2874, 'synset': 'ctenophore.n.01', 'name': 'ctenophore'}, {'id': 2875, 'synset': 'beroe.n.01', 'name': 'beroe'}, {'id': 2876, 'synset': 'platyctenean.n.01', 'name': 'platyctenean'}, {'id': 2877, 'synset': 'sea_gooseberry.n.01', 'name': 'sea_gooseberry'}, {'id': 2878, 'synset': "venus's_girdle.n.01", 'name': "Venus's_girdle"}, {'id': 2879, 'synset': 'worm.n.01', 'name': 'worm'}, {'id': 2880, 'synset': 'helminth.n.01', 'name': 'helminth'}, {'id': 2881, 'synset': 'woodworm.n.01', 'name': 'woodworm'}, {'id': 2882, 'synset': 'woodborer.n.01', 'name': 'woodborer'}, {'id': 2883, 'synset': 'acanthocephalan.n.01', 'name': 'acanthocephalan'}, {'id': 2884, 'synset': 'arrowworm.n.01', 'name': 'arrowworm'}, {'id': 2885, 'synset': 'bladder_worm.n.01', 'name': 'bladder_worm'}, {'id': 2886, 'synset': 'flatworm.n.01', 'name': 'flatworm'}, {'id': 2887, 'synset': 'planarian.n.01', 'name': 'planarian'}, {'id': 2888, 'synset': 'fluke.n.05', 'name': 'fluke'}, {'id': 2889, 'synset': 'cercaria.n.01', 'name': 'cercaria'}, {'id': 2890, 'synset': 'liver_fluke.n.01', 'name': 'liver_fluke'}, {'id': 2891, 'synset': 'fasciolopsis_buski.n.01', 'name': 'Fasciolopsis_buski'}, {'id': 2892, 'synset': 'schistosome.n.01', 'name': 'schistosome'}, {'id': 2893, 'synset': 'tapeworm.n.01', 'name': 'tapeworm'}, {'id': 2894, 'synset': 'echinococcus.n.01', 'name': 'echinococcus'}, {'id': 2895, 'synset': 'taenia.n.02', 'name': 'taenia'}, {'id': 2896, 'synset': 'ribbon_worm.n.01', 'name': 'ribbon_worm'}, {'id': 2897, 'synset': 'beard_worm.n.01', 'name': 'beard_worm'}, {'id': 2898, 'synset': 'rotifer.n.01', 'name': 'rotifer'}, {'id': 2899, 'synset': 'nematode.n.01', 'name': 'nematode'}, {'id': 2900, 'synset': 'common_roundworm.n.01', 'name': 'common_roundworm'}, {'id': 2901, 'synset': 'chicken_roundworm.n.01', 'name': 'chicken_roundworm'}, {'id': 2902, 'synset': 'pinworm.n.01', 'name': 'pinworm'}, {'id': 2903, 'synset': 'eelworm.n.01', 'name': 'eelworm'}, {'id': 2904, 'synset': 'vinegar_eel.n.01', 'name': 'vinegar_eel'}, {'id': 2905, 'synset': 'trichina.n.01', 'name': 'trichina'}, {'id': 2906, 'synset': 'hookworm.n.01', 'name': 'hookworm'}, {'id': 2907, 'synset': 'filaria.n.02', 'name': 'filaria'}, {'id': 2908, 'synset': 'guinea_worm.n.02', 'name': 'Guinea_worm'}, {'id': 2909, 'synset': 'annelid.n.01', 'name': 'annelid'}, {'id': 2910, 'synset': 'archiannelid.n.01', 'name': 'archiannelid'}, {'id': 2911, 'synset': 'oligochaete.n.01', 'name': 'oligochaete'}, {'id': 2912, 'synset': 'earthworm.n.01', 'name': 'earthworm'}, {'id': 2913, 'synset': 'polychaete.n.01', 'name': 'polychaete'}, {'id': 2914, 'synset': 'lugworm.n.01', 'name': 'lugworm'}, {'id': 2915, 'synset': 'sea_mouse.n.01', 'name': 'sea_mouse'}, {'id': 2916, 'synset': 'bloodworm.n.01', 'name': 'bloodworm'}, {'id': 2917, 'synset': 'leech.n.01', 'name': 'leech'}, {'id': 2918, 'synset': 'medicinal_leech.n.01', 'name': 'medicinal_leech'}, {'id': 2919, 'synset': 'horseleech.n.01', 'name': 'horseleech'}, {'id': 2920, 'synset': 'mollusk.n.01', 'name': 'mollusk'}, {'id': 2921, 'synset': 'scaphopod.n.01', 'name': 'scaphopod'}, {'id': 2922, 'synset': 'tooth_shell.n.01', 'name': 'tooth_shell'}, {'id': 2923, 'synset': 'gastropod.n.01', 'name': 'gastropod'}, {'id': 2924, 'synset': 'abalone.n.01', 'name': 'abalone'}, {'id': 2925, 'synset': 'ormer.n.01', 'name': 'ormer'}, {'id': 2926, 'synset': 'scorpion_shell.n.01', 'name': 'scorpion_shell'}, {'id': 2927, 'synset': 'conch.n.01', 'name': 'conch'}, {'id': 2928, 'synset': 'giant_conch.n.01', 'name': 'giant_conch'}, {'id': 2929, 'synset': 'snail.n.01', 'name': 'snail'}, {'id': 2930, 'synset': 'edible_snail.n.01', 'name': 'edible_snail'}, {'id': 2931, 'synset': 'garden_snail.n.01', 'name': 'garden_snail'}, {'id': 2932, 'synset': 'brown_snail.n.01', 'name': 'brown_snail'}, {'id': 2933, 'synset': 'helix_hortensis.n.01', 'name': 'Helix_hortensis'}, {'id': 2934, 'synset': 'slug.n.07', 'name': 'slug'}, {'id': 2935, 'synset': 'seasnail.n.02', 'name': 'seasnail'}, {'id': 2936, 'synset': 'neritid.n.01', 'name': 'neritid'}, {'id': 2937, 'synset': 'nerita.n.01', 'name': 'nerita'}, {'id': 2938, 'synset': 'bleeding_tooth.n.01', 'name': 'bleeding_tooth'}, {'id': 2939, 'synset': 'neritina.n.01', 'name': 'neritina'}, {'id': 2940, 'synset': 'whelk.n.02', 'name': 'whelk'}, {'id': 2941, 'synset': 'moon_shell.n.01', 'name': 'moon_shell'}, {'id': 2942, 'synset': 'periwinkle.n.04', 'name': 'periwinkle'}, {'id': 2943, 'synset': 'limpet.n.02', 'name': 'limpet'}, {'id': 2944, 'synset': 'common_limpet.n.01', 'name': 'common_limpet'}, {'id': 2945, 'synset': 'keyhole_limpet.n.01', 'name': 'keyhole_limpet'}, {'id': 2946, 'synset': 'river_limpet.n.01', 'name': 'river_limpet'}, {'id': 2947, 'synset': 'sea_slug.n.01', 'name': 'sea_slug'}, {'id': 2948, 'synset': 'sea_hare.n.01', 'name': 'sea_hare'}, {'id': 2949, 'synset': 'hermissenda_crassicornis.n.01', 'name': 'Hermissenda_crassicornis'}, {'id': 2950, 'synset': 'bubble_shell.n.01', 'name': 'bubble_shell'}, {'id': 2951, 'synset': 'physa.n.01', 'name': 'physa'}, {'id': 2952, 'synset': 'cowrie.n.01', 'name': 'cowrie'}, {'id': 2953, 'synset': 'money_cowrie.n.01', 'name': 'money_cowrie'}, {'id': 2954, 'synset': 'tiger_cowrie.n.01', 'name': 'tiger_cowrie'}, {'id': 2955, 'synset': 'solenogaster.n.01', 'name': 'solenogaster'}, {'id': 2956, 'synset': 'chiton.n.02', 'name': 'chiton'}, {'id': 2957, 'synset': 'bivalve.n.01', 'name': 'bivalve'}, {'id': 2958, 'synset': 'spat.n.03', 'name': 'spat'}, {'id': 2959, 'synset': 'clam.n.01', 'name': 'clam'}, {'id': 2960, 'synset': 'soft-shell_clam.n.02', 'name': 'soft-shell_clam'}, {'id': 2961, 'synset': 'quahog.n.02', 'name': 'quahog'}, {'id': 2962, 'synset': 'littleneck.n.02', 'name': 'littleneck'}, {'id': 2963, 'synset': 'cherrystone.n.02', 'name': 'cherrystone'}, {'id': 2964, 'synset': 'geoduck.n.01', 'name': 'geoduck'}, {'id': 2965, 'synset': 'razor_clam.n.01', 'name': 'razor_clam'}, {'id': 2966, 'synset': 'giant_clam.n.01', 'name': 'giant_clam'}, {'id': 2967, 'synset': 'cockle.n.02', 'name': 'cockle'}, {'id': 2968, 'synset': 'edible_cockle.n.01', 'name': 'edible_cockle'}, {'id': 2969, 'synset': 'oyster.n.01', 'name': 'oyster'}, {'id': 2970, 'synset': 'japanese_oyster.n.01', 'name': 'Japanese_oyster'}, {'id': 2971, 'synset': 'virginia_oyster.n.01', 'name': 'Virginia_oyster'}, {'id': 2972, 'synset': 'pearl_oyster.n.01', 'name': 'pearl_oyster'}, {'id': 2973, 'synset': 'saddle_oyster.n.01', 'name': 'saddle_oyster'}, {'id': 2974, 'synset': 'window_oyster.n.01', 'name': 'window_oyster'}, {'id': 2975, 'synset': 'ark_shell.n.01', 'name': 'ark_shell'}, {'id': 2976, 'synset': 'blood_clam.n.01', 'name': 'blood_clam'}, {'id': 2977, 'synset': 'mussel.n.02', 'name': 'mussel'}, {'id': 2978, 'synset': 'marine_mussel.n.01', 'name': 'marine_mussel'}, {'id': 2979, 'synset': 'edible_mussel.n.01', 'name': 'edible_mussel'}, {'id': 2980, 'synset': 'freshwater_mussel.n.01', 'name': 'freshwater_mussel'}, {'id': 2981, 'synset': 'pearly-shelled_mussel.n.01', 'name': 'pearly-shelled_mussel'}, {'id': 2982, 'synset': 'thin-shelled_mussel.n.01', 'name': 'thin-shelled_mussel'}, {'id': 2983, 'synset': 'zebra_mussel.n.01', 'name': 'zebra_mussel'}, {'id': 2984, 'synset': 'scallop.n.04', 'name': 'scallop'}, {'id': 2985, 'synset': 'bay_scallop.n.02', 'name': 'bay_scallop'}, {'id': 2986, 'synset': 'sea_scallop.n.02', 'name': 'sea_scallop'}, {'id': 2987, 'synset': 'shipworm.n.01', 'name': 'shipworm'}, {'id': 2988, 'synset': 'teredo.n.01', 'name': 'teredo'}, {'id': 2989, 'synset': 'piddock.n.01', 'name': 'piddock'}, {'id': 2990, 'synset': 'cephalopod.n.01', 'name': 'cephalopod'}, {'id': 2991, 'synset': 'chambered_nautilus.n.01', 'name': 'chambered_nautilus'}, {'id': 2992, 'synset': 'octopod.n.01', 'name': 'octopod'}, {'id': 2993, 'synset': 'paper_nautilus.n.01', 'name': 'paper_nautilus'}, {'id': 2994, 'synset': 'decapod.n.02', 'name': 'decapod'}, {'id': 2995, 'synset': 'squid.n.02', 'name': 'squid'}, {'id': 2996, 'synset': 'loligo.n.01', 'name': 'loligo'}, {'id': 2997, 'synset': 'ommastrephes.n.01', 'name': 'ommastrephes'}, {'id': 2998, 'synset': 'architeuthis.n.01', 'name': 'architeuthis'}, {'id': 2999, 'synset': 'cuttlefish.n.01', 'name': 'cuttlefish'}, {'id': 3000, 'synset': 'spirula.n.01', 'name': 'spirula'}, {'id': 3001, 'synset': 'crustacean.n.01', 'name': 'crustacean'}, {'id': 3002, 'synset': 'malacostracan_crustacean.n.01', 'name': 'malacostracan_crustacean'}, {'id': 3003, 'synset': 'decapod_crustacean.n.01', 'name': 'decapod_crustacean'}, {'id': 3004, 'synset': 'brachyuran.n.01', 'name': 'brachyuran'}, {'id': 3005, 'synset': 'stone_crab.n.02', 'name': 'stone_crab'}, {'id': 3006, 'synset': 'hard-shell_crab.n.01', 'name': 'hard-shell_crab'}, {'id': 3007, 'synset': 'soft-shell_crab.n.02', 'name': 'soft-shell_crab'}, {'id': 3008, 'synset': 'dungeness_crab.n.02', 'name': 'Dungeness_crab'}, {'id': 3009, 'synset': 'rock_crab.n.01', 'name': 'rock_crab'}, {'id': 3010, 'synset': 'jonah_crab.n.01', 'name': 'Jonah_crab'}, {'id': 3011, 'synset': 'swimming_crab.n.01', 'name': 'swimming_crab'}, {'id': 3012, 'synset': 'english_lady_crab.n.01', 'name': 'English_lady_crab'}, {'id': 3013, 'synset': 'american_lady_crab.n.01', 'name': 'American_lady_crab'}, {'id': 3014, 'synset': 'blue_crab.n.02', 'name': 'blue_crab'}, {'id': 3015, 'synset': 'fiddler_crab.n.01', 'name': 'fiddler_crab'}, {'id': 3016, 'synset': 'pea_crab.n.01', 'name': 'pea_crab'}, {'id': 3017, 'synset': 'king_crab.n.03', 'name': 'king_crab'}, {'id': 3018, 'synset': 'spider_crab.n.01', 'name': 'spider_crab'}, {'id': 3019, 'synset': 'european_spider_crab.n.01', 'name': 'European_spider_crab'}, {'id': 3020, 'synset': 'giant_crab.n.01', 'name': 'giant_crab'}, {'id': 3021, 'synset': 'lobster.n.02', 'name': 'lobster'}, {'id': 3022, 'synset': 'true_lobster.n.01', 'name': 'true_lobster'}, {'id': 3023, 'synset': 'american_lobster.n.02', 'name': 'American_lobster'}, {'id': 3024, 'synset': 'european_lobster.n.02', 'name': 'European_lobster'}, {'id': 3025, 'synset': 'cape_lobster.n.01', 'name': 'Cape_lobster'}, {'id': 3026, 'synset': 'norway_lobster.n.01', 'name': 'Norway_lobster'}, {'id': 3027, 'synset': 'crayfish.n.03', 'name': 'crayfish'}, {'id': 3028, 'synset': 'old_world_crayfish.n.01', 'name': 'Old_World_crayfish'}, {'id': 3029, 'synset': 'american_crayfish.n.01', 'name': 'American_crayfish'}, {'id': 3030, 'synset': 'hermit_crab.n.01', 'name': 'hermit_crab'}, {'id': 3031, 'synset': 'shrimp.n.03', 'name': 'shrimp'}, {'id': 3032, 'synset': 'snapping_shrimp.n.01', 'name': 'snapping_shrimp'}, {'id': 3033, 'synset': 'prawn.n.02', 'name': 'prawn'}, {'id': 3034, 'synset': 'long-clawed_prawn.n.01', 'name': 'long-clawed_prawn'}, {'id': 3035, 'synset': 'tropical_prawn.n.01', 'name': 'tropical_prawn'}, {'id': 3036, 'synset': 'krill.n.01', 'name': 'krill'}, {'id': 3037, 'synset': 'euphausia_pacifica.n.01', 'name': 'Euphausia_pacifica'}, {'id': 3038, 'synset': 'opossum_shrimp.n.01', 'name': 'opossum_shrimp'}, {'id': 3039, 'synset': 'stomatopod.n.01', 'name': 'stomatopod'}, {'id': 3040, 'synset': 'mantis_shrimp.n.01', 'name': 'mantis_shrimp'}, {'id': 3041, 'synset': 'squilla.n.01', 'name': 'squilla'}, {'id': 3042, 'synset': 'isopod.n.01', 'name': 'isopod'}, {'id': 3043, 'synset': 'woodlouse.n.01', 'name': 'woodlouse'}, {'id': 3044, 'synset': 'pill_bug.n.01', 'name': 'pill_bug'}, {'id': 3045, 'synset': 'sow_bug.n.01', 'name': 'sow_bug'}, {'id': 3046, 'synset': 'sea_louse.n.01', 'name': 'sea_louse'}, {'id': 3047, 'synset': 'amphipod.n.01', 'name': 'amphipod'}, {'id': 3048, 'synset': 'skeleton_shrimp.n.01', 'name': 'skeleton_shrimp'}, {'id': 3049, 'synset': 'whale_louse.n.01', 'name': 'whale_louse'}, {'id': 3050, 'synset': 'daphnia.n.01', 'name': 'daphnia'}, {'id': 3051, 'synset': 'fairy_shrimp.n.01', 'name': 'fairy_shrimp'}, {'id': 3052, 'synset': 'brine_shrimp.n.01', 'name': 'brine_shrimp'}, {'id': 3053, 'synset': 'tadpole_shrimp.n.01', 'name': 'tadpole_shrimp'}, {'id': 3054, 'synset': 'copepod.n.01', 'name': 'copepod'}, {'id': 3055, 'synset': 'cyclops.n.02', 'name': 'cyclops'}, {'id': 3056, 'synset': 'seed_shrimp.n.01', 'name': 'seed_shrimp'}, {'id': 3057, 'synset': 'barnacle.n.01', 'name': 'barnacle'}, {'id': 3058, 'synset': 'acorn_barnacle.n.01', 'name': 'acorn_barnacle'}, {'id': 3059, 'synset': 'goose_barnacle.n.01', 'name': 'goose_barnacle'}, {'id': 3060, 'synset': 'onychophoran.n.01', 'name': 'onychophoran'}, {'id': 3061, 'synset': 'wading_bird.n.01', 'name': 'wading_bird'}, {'id': 3062, 'synset': 'stork.n.01', 'name': 'stork'}, {'id': 3063, 'synset': 'white_stork.n.01', 'name': 'white_stork'}, {'id': 3064, 'synset': 'black_stork.n.01', 'name': 'black_stork'}, {'id': 3065, 'synset': 'adjutant_bird.n.01', 'name': 'adjutant_bird'}, {'id': 3066, 'synset': 'marabou.n.01', 'name': 'marabou'}, {'id': 3067, 'synset': 'openbill.n.01', 'name': 'openbill'}, {'id': 3068, 'synset': 'jabiru.n.03', 'name': 'jabiru'}, {'id': 3069, 'synset': 'saddlebill.n.01', 'name': 'saddlebill'}, {'id': 3070, 'synset': 'policeman_bird.n.01', 'name': 'policeman_bird'}, {'id': 3071, 'synset': 'wood_ibis.n.02', 'name': 'wood_ibis'}, {'id': 3072, 'synset': 'shoebill.n.01', 'name': 'shoebill'}, {'id': 3073, 'synset': 'ibis.n.01', 'name': 'ibis'}, {'id': 3074, 'synset': 'wood_ibis.n.01', 'name': 'wood_ibis'}, {'id': 3075, 'synset': 'sacred_ibis.n.01', 'name': 'sacred_ibis'}, {'id': 3076, 'synset': 'spoonbill.n.01', 'name': 'spoonbill'}, {'id': 3077, 'synset': 'common_spoonbill.n.01', 'name': 'common_spoonbill'}, {'id': 3078, 'synset': 'roseate_spoonbill.n.01', 'name': 'roseate_spoonbill'}, {'id': 3079, 'synset': 'great_blue_heron.n.01', 'name': 'great_blue_heron'}, {'id': 3080, 'synset': 'great_white_heron.n.03', 'name': 'great_white_heron'}, {'id': 3081, 'synset': 'egret.n.01', 'name': 'egret'}, {'id': 3082, 'synset': 'little_blue_heron.n.01', 'name': 'little_blue_heron'}, {'id': 3083, 'synset': 'snowy_egret.n.01', 'name': 'snowy_egret'}, {'id': 3084, 'synset': 'little_egret.n.01', 'name': 'little_egret'}, {'id': 3085, 'synset': 'great_white_heron.n.02', 'name': 'great_white_heron'}, {'id': 3086, 'synset': 'american_egret.n.01', 'name': 'American_egret'}, {'id': 3087, 'synset': 'cattle_egret.n.01', 'name': 'cattle_egret'}, {'id': 3088, 'synset': 'night_heron.n.01', 'name': 'night_heron'}, {'id': 3089, 'synset': 'black-crowned_night_heron.n.01', 'name': 'black-crowned_night_heron'}, {'id': 3090, 'synset': 'yellow-crowned_night_heron.n.01', 'name': 'yellow-crowned_night_heron'}, {'id': 3091, 'synset': 'boatbill.n.01', 'name': 'boatbill'}, {'id': 3092, 'synset': 'bittern.n.01', 'name': 'bittern'}, {'id': 3093, 'synset': 'american_bittern.n.01', 'name': 'American_bittern'}, {'id': 3094, 'synset': 'european_bittern.n.01', 'name': 'European_bittern'}, {'id': 3095, 'synset': 'least_bittern.n.01', 'name': 'least_bittern'}, {'id': 3096, 'synset': 'crane.n.05', 'name': 'crane'}, {'id': 3097, 'synset': 'whooping_crane.n.01', 'name': 'whooping_crane'}, {'id': 3098, 'synset': 'courlan.n.01', 'name': 'courlan'}, {'id': 3099, 'synset': 'limpkin.n.01', 'name': 'limpkin'}, {'id': 3100, 'synset': 'crested_cariama.n.01', 'name': 'crested_cariama'}, {'id': 3101, 'synset': 'chunga.n.01', 'name': 'chunga'}, {'id': 3102, 'synset': 'rail.n.05', 'name': 'rail'}, {'id': 3103, 'synset': 'weka.n.01', 'name': 'weka'}, {'id': 3104, 'synset': 'crake.n.01', 'name': 'crake'}, {'id': 3105, 'synset': 'corncrake.n.01', 'name': 'corncrake'}, {'id': 3106, 'synset': 'spotted_crake.n.01', 'name': 'spotted_crake'}, {'id': 3107, 'synset': 'gallinule.n.01', 'name': 'gallinule'}, {'id': 3108, 'synset': 'florida_gallinule.n.01', 'name': 'Florida_gallinule'}, {'id': 3109, 'synset': 'moorhen.n.01', 'name': 'moorhen'}, {'id': 3110, 'synset': 'purple_gallinule.n.01', 'name': 'purple_gallinule'}, {'id': 3111, 'synset': 'european_gallinule.n.01', 'name': 'European_gallinule'}, {'id': 3112, 'synset': 'american_gallinule.n.01', 'name': 'American_gallinule'}, {'id': 3113, 'synset': 'notornis.n.01', 'name': 'notornis'}, {'id': 3114, 'synset': 'coot.n.01', 'name': 'coot'}, {'id': 3115, 'synset': 'american_coot.n.01', 'name': 'American_coot'}, {'id': 3116, 'synset': 'old_world_coot.n.01', 'name': 'Old_World_coot'}, {'id': 3117, 'synset': 'bustard.n.01', 'name': 'bustard'}, {'id': 3118, 'synset': 'great_bustard.n.01', 'name': 'great_bustard'}, {'id': 3119, 'synset': 'plain_turkey.n.01', 'name': 'plain_turkey'}, {'id': 3120, 'synset': 'button_quail.n.01', 'name': 'button_quail'}, {'id': 3121, 'synset': 'striped_button_quail.n.01', 'name': 'striped_button_quail'}, {'id': 3122, 'synset': 'plain_wanderer.n.01', 'name': 'plain_wanderer'}, {'id': 3123, 'synset': 'trumpeter.n.03', 'name': 'trumpeter'}, {'id': 3124, 'synset': 'brazilian_trumpeter.n.01', 'name': 'Brazilian_trumpeter'}, {'id': 3125, 'synset': 'shorebird.n.01', 'name': 'shorebird'}, {'id': 3126, 'synset': 'plover.n.01', 'name': 'plover'}, {'id': 3127, 'synset': 'piping_plover.n.01', 'name': 'piping_plover'}, {'id': 3128, 'synset': 'killdeer.n.01', 'name': 'killdeer'}, {'id': 3129, 'synset': 'dotterel.n.01', 'name': 'dotterel'}, {'id': 3130, 'synset': 'golden_plover.n.01', 'name': 'golden_plover'}, {'id': 3131, 'synset': 'lapwing.n.01', 'name': 'lapwing'}, {'id': 3132, 'synset': 'turnstone.n.01', 'name': 'turnstone'}, {'id': 3133, 'synset': 'ruddy_turnstone.n.01', 'name': 'ruddy_turnstone'}, {'id': 3134, 'synset': 'black_turnstone.n.01', 'name': 'black_turnstone'}, {'id': 3135, 'synset': 'sandpiper.n.01', 'name': 'sandpiper'}, {'id': 3136, 'synset': 'surfbird.n.01', 'name': 'surfbird'}, {'id': 3137, 'synset': 'european_sandpiper.n.01', 'name': 'European_sandpiper'}, {'id': 3138, 'synset': 'spotted_sandpiper.n.01', 'name': 'spotted_sandpiper'}, {'id': 3139, 'synset': 'least_sandpiper.n.01', 'name': 'least_sandpiper'}, {'id': 3140, 'synset': 'red-backed_sandpiper.n.01', 'name': 'red-backed_sandpiper'}, {'id': 3141, 'synset': 'greenshank.n.01', 'name': 'greenshank'}, {'id': 3142, 'synset': 'redshank.n.01', 'name': 'redshank'}, {'id': 3143, 'synset': 'yellowlegs.n.01', 'name': 'yellowlegs'}, {'id': 3144, 'synset': 'greater_yellowlegs.n.01', 'name': 'greater_yellowlegs'}, {'id': 3145, 'synset': 'lesser_yellowlegs.n.01', 'name': 'lesser_yellowlegs'}, {'id': 3146, 'synset': 'pectoral_sandpiper.n.01', 'name': 'pectoral_sandpiper'}, {'id': 3147, 'synset': 'knot.n.07', 'name': 'knot'}, {'id': 3148, 'synset': 'curlew_sandpiper.n.01', 'name': 'curlew_sandpiper'}, {'id': 3149, 'synset': 'sanderling.n.01', 'name': 'sanderling'}, {'id': 3150, 'synset': 'upland_sandpiper.n.01', 'name': 'upland_sandpiper'}, {'id': 3151, 'synset': 'ruff.n.03', 'name': 'ruff'}, {'id': 3152, 'synset': 'reeve.n.01', 'name': 'reeve'}, {'id': 3153, 'synset': 'tattler.n.02', 'name': 'tattler'}, {'id': 3154, 'synset': 'polynesian_tattler.n.01', 'name': 'Polynesian_tattler'}, {'id': 3155, 'synset': 'willet.n.01', 'name': 'willet'}, {'id': 3156, 'synset': 'woodcock.n.01', 'name': 'woodcock'}, {'id': 3157, 'synset': 'eurasian_woodcock.n.01', 'name': 'Eurasian_woodcock'}, {'id': 3158, 'synset': 'american_woodcock.n.01', 'name': 'American_woodcock'}, {'id': 3159, 'synset': 'snipe.n.01', 'name': 'snipe'}, {'id': 3160, 'synset': 'whole_snipe.n.01', 'name': 'whole_snipe'}, {'id': 3161, 'synset': "wilson's_snipe.n.01", 'name': "Wilson's_snipe"}, {'id': 3162, 'synset': 'great_snipe.n.01', 'name': 'great_snipe'}, {'id': 3163, 'synset': 'jacksnipe.n.01', 'name': 'jacksnipe'}, {'id': 3164, 'synset': 'dowitcher.n.01', 'name': 'dowitcher'}, {'id': 3165, 'synset': 'greyback.n.02', 'name': 'greyback'}, {'id': 3166, 'synset': 'red-breasted_snipe.n.01', 'name': 'red-breasted_snipe'}, {'id': 3167, 'synset': 'curlew.n.01', 'name': 'curlew'}, {'id': 3168, 'synset': 'european_curlew.n.01', 'name': 'European_curlew'}, {'id': 3169, 'synset': 'eskimo_curlew.n.01', 'name': 'Eskimo_curlew'}, {'id': 3170, 'synset': 'godwit.n.01', 'name': 'godwit'}, {'id': 3171, 'synset': 'hudsonian_godwit.n.01', 'name': 'Hudsonian_godwit'}, {'id': 3172, 'synset': 'stilt.n.04', 'name': 'stilt'}, {'id': 3173, 'synset': 'black-necked_stilt.n.01', 'name': 'black-necked_stilt'}, {'id': 3174, 'synset': 'black-winged_stilt.n.01', 'name': 'black-winged_stilt'}, {'id': 3175, 'synset': 'white-headed_stilt.n.01', 'name': 'white-headed_stilt'}, {'id': 3176, 'synset': 'kaki.n.02', 'name': 'kaki'}, {'id': 3177, 'synset': 'stilt.n.03', 'name': 'stilt'}, {'id': 3178, 'synset': 'banded_stilt.n.01', 'name': 'banded_stilt'}, {'id': 3179, 'synset': 'avocet.n.01', 'name': 'avocet'}, {'id': 3180, 'synset': 'oystercatcher.n.01', 'name': 'oystercatcher'}, {'id': 3181, 'synset': 'phalarope.n.01', 'name': 'phalarope'}, {'id': 3182, 'synset': 'red_phalarope.n.01', 'name': 'red_phalarope'}, {'id': 3183, 'synset': 'northern_phalarope.n.01', 'name': 'northern_phalarope'}, {'id': 3184, 'synset': "wilson's_phalarope.n.01", 'name': "Wilson's_phalarope"}, {'id': 3185, 'synset': 'pratincole.n.01', 'name': 'pratincole'}, {'id': 3186, 'synset': 'courser.n.04', 'name': 'courser'}, {'id': 3187, 'synset': 'cream-colored_courser.n.01', 'name': 'cream-colored_courser'}, {'id': 3188, 'synset': 'crocodile_bird.n.01', 'name': 'crocodile_bird'}, {'id': 3189, 'synset': 'stone_curlew.n.01', 'name': 'stone_curlew'}, {'id': 3190, 'synset': 'coastal_diving_bird.n.01', 'name': 'coastal_diving_bird'}, {'id': 3191, 'synset': 'larid.n.01', 'name': 'larid'}, {'id': 3192, 'synset': 'mew.n.02', 'name': 'mew'}, {'id': 3193, 'synset': 'black-backed_gull.n.01', 'name': 'black-backed_gull'}, {'id': 3194, 'synset': 'herring_gull.n.01', 'name': 'herring_gull'}, {'id': 3195, 'synset': 'laughing_gull.n.01', 'name': 'laughing_gull'}, {'id': 3196, 'synset': 'ivory_gull.n.01', 'name': 'ivory_gull'}, {'id': 3197, 'synset': 'kittiwake.n.01', 'name': 'kittiwake'}, {'id': 3198, 'synset': 'tern.n.01', 'name': 'tern'}, {'id': 3199, 'synset': 'sea_swallow.n.01', 'name': 'sea_swallow'}, {'id': 3200, 'synset': 'skimmer.n.04', 'name': 'skimmer'}, {'id': 3201, 'synset': 'jaeger.n.01', 'name': 'jaeger'}, {'id': 3202, 'synset': 'parasitic_jaeger.n.01', 'name': 'parasitic_jaeger'}, {'id': 3203, 'synset': 'skua.n.01', 'name': 'skua'}, {'id': 3204, 'synset': 'great_skua.n.01', 'name': 'great_skua'}, {'id': 3205, 'synset': 'auk.n.01', 'name': 'auk'}, {'id': 3206, 'synset': 'auklet.n.01', 'name': 'auklet'}, {'id': 3207, 'synset': 'razorbill.n.01', 'name': 'razorbill'}, {'id': 3208, 'synset': 'little_auk.n.01', 'name': 'little_auk'}, {'id': 3209, 'synset': 'guillemot.n.01', 'name': 'guillemot'}, {'id': 3210, 'synset': 'black_guillemot.n.01', 'name': 'black_guillemot'}, {'id': 3211, 'synset': 'pigeon_guillemot.n.01', 'name': 'pigeon_guillemot'}, {'id': 3212, 'synset': 'murre.n.01', 'name': 'murre'}, {'id': 3213, 'synset': 'common_murre.n.01', 'name': 'common_murre'}, {'id': 3214, 'synset': 'thick-billed_murre.n.01', 'name': 'thick-billed_murre'}, {'id': 3215, 'synset': 'atlantic_puffin.n.01', 'name': 'Atlantic_puffin'}, {'id': 3216, 'synset': 'horned_puffin.n.01', 'name': 'horned_puffin'}, {'id': 3217, 'synset': 'tufted_puffin.n.01', 'name': 'tufted_puffin'}, {'id': 3218, 'synset': 'gaviiform_seabird.n.01', 'name': 'gaviiform_seabird'}, {'id': 3219, 'synset': 'loon.n.02', 'name': 'loon'}, {'id': 3220, 'synset': 'podicipitiform_seabird.n.01', 'name': 'podicipitiform_seabird'}, {'id': 3221, 'synset': 'grebe.n.01', 'name': 'grebe'}, {'id': 3222, 'synset': 'great_crested_grebe.n.01', 'name': 'great_crested_grebe'}, {'id': 3223, 'synset': 'red-necked_grebe.n.01', 'name': 'red-necked_grebe'}, {'id': 3224, 'synset': 'black-necked_grebe.n.01', 'name': 'black-necked_grebe'}, {'id': 3225, 'synset': 'dabchick.n.01', 'name': 'dabchick'}, {'id': 3226, 'synset': 'pied-billed_grebe.n.01', 'name': 'pied-billed_grebe'}, {'id': 3227, 'synset': 'pelecaniform_seabird.n.01', 'name': 'pelecaniform_seabird'}, {'id': 3228, 'synset': 'white_pelican.n.01', 'name': 'white_pelican'}, {'id': 3229, 'synset': 'old_world_white_pelican.n.01', 'name': 'Old_world_white_pelican'}, {'id': 3230, 'synset': 'frigate_bird.n.01', 'name': 'frigate_bird'}, {'id': 3231, 'synset': 'gannet.n.01', 'name': 'gannet'}, {'id': 3232, 'synset': 'solan.n.01', 'name': 'solan'}, {'id': 3233, 'synset': 'booby.n.02', 'name': 'booby'}, {'id': 3234, 'synset': 'cormorant.n.01', 'name': 'cormorant'}, {'id': 3235, 'synset': 'snakebird.n.01', 'name': 'snakebird'}, {'id': 3236, 'synset': 'water_turkey.n.01', 'name': 'water_turkey'}, {'id': 3237, 'synset': 'tropic_bird.n.01', 'name': 'tropic_bird'}, {'id': 3238, 'synset': 'sphenisciform_seabird.n.01', 'name': 'sphenisciform_seabird'}, {'id': 3239, 'synset': 'adelie.n.01', 'name': 'Adelie'}, {'id': 3240, 'synset': 'king_penguin.n.01', 'name': 'king_penguin'}, {'id': 3241, 'synset': 'emperor_penguin.n.01', 'name': 'emperor_penguin'}, {'id': 3242, 'synset': 'jackass_penguin.n.01', 'name': 'jackass_penguin'}, {'id': 3243, 'synset': 'rock_hopper.n.01', 'name': 'rock_hopper'}, {'id': 3244, 'synset': 'pelagic_bird.n.01', 'name': 'pelagic_bird'}, {'id': 3245, 'synset': 'procellariiform_seabird.n.01', 'name': 'procellariiform_seabird'}, {'id': 3246, 'synset': 'albatross.n.02', 'name': 'albatross'}, {'id': 3247, 'synset': 'wandering_albatross.n.01', 'name': 'wandering_albatross'}, {'id': 3248, 'synset': 'black-footed_albatross.n.01', 'name': 'black-footed_albatross'}, {'id': 3249, 'synset': 'petrel.n.01', 'name': 'petrel'}, {'id': 3250, 'synset': 'white-chinned_petrel.n.01', 'name': 'white-chinned_petrel'}, {'id': 3251, 'synset': 'giant_petrel.n.01', 'name': 'giant_petrel'}, {'id': 3252, 'synset': 'fulmar.n.01', 'name': 'fulmar'}, {'id': 3253, 'synset': 'shearwater.n.01', 'name': 'shearwater'}, {'id': 3254, 'synset': 'manx_shearwater.n.01', 'name': 'Manx_shearwater'}, {'id': 3255, 'synset': 'storm_petrel.n.01', 'name': 'storm_petrel'}, {'id': 3256, 'synset': 'stormy_petrel.n.01', 'name': 'stormy_petrel'}, {'id': 3257, 'synset': "mother_carey's_chicken.n.01", 'name': "Mother_Carey's_chicken"}, {'id': 3258, 'synset': 'diving_petrel.n.01', 'name': 'diving_petrel'}, {'id': 3259, 'synset': 'aquatic_mammal.n.01', 'name': 'aquatic_mammal'}, {'id': 3260, 'synset': 'cetacean.n.01', 'name': 'cetacean'}, {'id': 3261, 'synset': 'whale.n.02', 'name': 'whale'}, {'id': 3262, 'synset': 'baleen_whale.n.01', 'name': 'baleen_whale'}, {'id': 3263, 'synset': 'right_whale.n.01', 'name': 'right_whale'}, {'id': 3264, 'synset': 'bowhead.n.01', 'name': 'bowhead'}, {'id': 3265, 'synset': 'rorqual.n.01', 'name': 'rorqual'}, {'id': 3266, 'synset': 'blue_whale.n.01', 'name': 'blue_whale'}, {'id': 3267, 'synset': 'finback.n.01', 'name': 'finback'}, {'id': 3268, 'synset': 'sei_whale.n.01', 'name': 'sei_whale'}, {'id': 3269, 'synset': 'lesser_rorqual.n.01', 'name': 'lesser_rorqual'}, {'id': 3270, 'synset': 'humpback.n.03', 'name': 'humpback'}, {'id': 3271, 'synset': 'grey_whale.n.01', 'name': 'grey_whale'}, {'id': 3272, 'synset': 'toothed_whale.n.01', 'name': 'toothed_whale'}, {'id': 3273, 'synset': 'sperm_whale.n.01', 'name': 'sperm_whale'}, {'id': 3274, 'synset': 'pygmy_sperm_whale.n.01', 'name': 'pygmy_sperm_whale'}, {'id': 3275, 'synset': 'dwarf_sperm_whale.n.01', 'name': 'dwarf_sperm_whale'}, {'id': 3276, 'synset': 'beaked_whale.n.01', 'name': 'beaked_whale'}, {'id': 3277, 'synset': 'bottle-nosed_whale.n.01', 'name': 'bottle-nosed_whale'}, {'id': 3278, 'synset': 'common_dolphin.n.01', 'name': 'common_dolphin'}, {'id': 3279, 'synset': 'bottlenose_dolphin.n.01', 'name': 'bottlenose_dolphin'}, {'id': 3280, 'synset': 'atlantic_bottlenose_dolphin.n.01', 'name': 'Atlantic_bottlenose_dolphin'}, {'id': 3281, 'synset': 'pacific_bottlenose_dolphin.n.01', 'name': 'Pacific_bottlenose_dolphin'}, {'id': 3282, 'synset': 'porpoise.n.01', 'name': 'porpoise'}, {'id': 3283, 'synset': 'harbor_porpoise.n.01', 'name': 'harbor_porpoise'}, {'id': 3284, 'synset': 'vaquita.n.01', 'name': 'vaquita'}, {'id': 3285, 'synset': 'grampus.n.02', 'name': 'grampus'}, {'id': 3286, 'synset': 'killer_whale.n.01', 'name': 'killer_whale'}, {'id': 3287, 'synset': 'pilot_whale.n.01', 'name': 'pilot_whale'}, {'id': 3288, 'synset': 'river_dolphin.n.01', 'name': 'river_dolphin'}, {'id': 3289, 'synset': 'narwhal.n.01', 'name': 'narwhal'}, {'id': 3290, 'synset': 'white_whale.n.01', 'name': 'white_whale'}, {'id': 3291, 'synset': 'sea_cow.n.01', 'name': 'sea_cow'}, {'id': 3292, 'synset': 'dugong.n.01', 'name': 'dugong'}, {'id': 3293, 'synset': "steller's_sea_cow.n.01", 'name': "Steller's_sea_cow"}, {'id': 3294, 'synset': 'carnivore.n.01', 'name': 'carnivore'}, {'id': 3295, 'synset': 'omnivore.n.02', 'name': 'omnivore'}, {'id': 3296, 'synset': 'pinniped_mammal.n.01', 'name': 'pinniped_mammal'}, {'id': 3297, 'synset': 'seal.n.09', 'name': 'seal'}, {'id': 3298, 'synset': 'crabeater_seal.n.01', 'name': 'crabeater_seal'}, {'id': 3299, 'synset': 'eared_seal.n.01', 'name': 'eared_seal'}, {'id': 3300, 'synset': 'fur_seal.n.02', 'name': 'fur_seal'}, {'id': 3301, 'synset': 'guadalupe_fur_seal.n.01', 'name': 'guadalupe_fur_seal'}, {'id': 3302, 'synset': 'fur_seal.n.01', 'name': 'fur_seal'}, {'id': 3303, 'synset': 'alaska_fur_seal.n.01', 'name': 'Alaska_fur_seal'}, {'id': 3304, 'synset': 'sea_lion.n.01', 'name': 'sea_lion'}, {'id': 3305, 'synset': 'south_american_sea_lion.n.01', 'name': 'South_American_sea_lion'}, {'id': 3306, 'synset': 'california_sea_lion.n.01', 'name': 'California_sea_lion'}, {'id': 3307, 'synset': 'australian_sea_lion.n.01', 'name': 'Australian_sea_lion'}, {'id': 3308, 'synset': 'steller_sea_lion.n.01', 'name': 'Steller_sea_lion'}, {'id': 3309, 'synset': 'earless_seal.n.01', 'name': 'earless_seal'}, {'id': 3310, 'synset': 'harbor_seal.n.01', 'name': 'harbor_seal'}, {'id': 3311, 'synset': 'harp_seal.n.01', 'name': 'harp_seal'}, {'id': 3312, 'synset': 'elephant_seal.n.01', 'name': 'elephant_seal'}, {'id': 3313, 'synset': 'bearded_seal.n.01', 'name': 'bearded_seal'}, {'id': 3314, 'synset': 'hooded_seal.n.01', 'name': 'hooded_seal'}, {'id': 3315, 'synset': 'atlantic_walrus.n.01', 'name': 'Atlantic_walrus'}, {'id': 3316, 'synset': 'pacific_walrus.n.01', 'name': 'Pacific_walrus'}, {'id': 3317, 'synset': 'fissipedia.n.01', 'name': 'Fissipedia'}, {'id': 3318, 'synset': 'fissiped_mammal.n.01', 'name': 'fissiped_mammal'}, {'id': 3319, 'synset': 'aardvark.n.01', 'name': 'aardvark'}, {'id': 3320, 'synset': 'canine.n.02', 'name': 'canine'}, {'id': 3321, 'synset': 'bitch.n.04', 'name': 'bitch'}, {'id': 3322, 'synset': 'brood_bitch.n.01', 'name': 'brood_bitch'}, {'id': 3323, 'synset': 'pooch.n.01', 'name': 'pooch'}, {'id': 3324, 'synset': 'cur.n.01', 'name': 'cur'}, {'id': 3325, 'synset': 'feist.n.01', 'name': 'feist'}, {'id': 3326, 'synset': 'pariah_dog.n.01', 'name': 'pariah_dog'}, {'id': 3327, 'synset': 'lapdog.n.01', 'name': 'lapdog'}, {'id': 3328, 'synset': 'toy_dog.n.01', 'name': 'toy_dog'}, {'id': 3329, 'synset': 'chihuahua.n.03', 'name': 'Chihuahua'}, {'id': 3330, 'synset': 'japanese_spaniel.n.01', 'name': 'Japanese_spaniel'}, {'id': 3331, 'synset': 'maltese_dog.n.01', 'name': 'Maltese_dog'}, {'id': 3332, 'synset': 'pekinese.n.01', 'name': 'Pekinese'}, {'id': 3333, 'synset': 'shih-tzu.n.01', 'name': 'Shih-Tzu'}, {'id': 3334, 'synset': 'toy_spaniel.n.01', 'name': 'toy_spaniel'}, {'id': 3335, 'synset': 'english_toy_spaniel.n.01', 'name': 'English_toy_spaniel'}, {'id': 3336, 'synset': 'blenheim_spaniel.n.01', 'name': 'Blenheim_spaniel'}, {'id': 3337, 'synset': 'king_charles_spaniel.n.01', 'name': 'King_Charles_spaniel'}, {'id': 3338, 'synset': 'papillon.n.01', 'name': 'papillon'}, {'id': 3339, 'synset': 'toy_terrier.n.01', 'name': 'toy_terrier'}, {'id': 3340, 'synset': 'hunting_dog.n.01', 'name': 'hunting_dog'}, {'id': 3341, 'synset': 'courser.n.03', 'name': 'courser'}, {'id': 3342, 'synset': 'rhodesian_ridgeback.n.01', 'name': 'Rhodesian_ridgeback'}, {'id': 3343, 'synset': 'hound.n.01', 'name': 'hound'}, {'id': 3344, 'synset': 'afghan_hound.n.01', 'name': 'Afghan_hound'}, {'id': 3345, 'synset': 'basset.n.01', 'name': 'basset'}, {'id': 3346, 'synset': 'beagle.n.01', 'name': 'beagle'}, {'id': 3347, 'synset': 'bloodhound.n.01', 'name': 'bloodhound'}, {'id': 3348, 'synset': 'bluetick.n.01', 'name': 'bluetick'}, {'id': 3349, 'synset': 'boarhound.n.01', 'name': 'boarhound'}, {'id': 3350, 'synset': 'coonhound.n.01', 'name': 'coonhound'}, {'id': 3351, 'synset': 'coondog.n.01', 'name': 'coondog'}, {'id': 3352, 'synset': 'black-and-tan_coonhound.n.01', 'name': 'black-and-tan_coonhound'}, {'id': 3353, 'synset': 'dachshund.n.01', 'name': 'dachshund'}, {'id': 3354, 'synset': 'sausage_dog.n.01', 'name': 'sausage_dog'}, {'id': 3355, 'synset': 'foxhound.n.01', 'name': 'foxhound'}, {'id': 3356, 'synset': 'american_foxhound.n.01', 'name': 'American_foxhound'}, {'id': 3357, 'synset': 'walker_hound.n.01', 'name': 'Walker_hound'}, {'id': 3358, 'synset': 'english_foxhound.n.01', 'name': 'English_foxhound'}, {'id': 3359, 'synset': 'harrier.n.02', 'name': 'harrier'}, {'id': 3360, 'synset': 'plott_hound.n.01', 'name': 'Plott_hound'}, {'id': 3361, 'synset': 'redbone.n.01', 'name': 'redbone'}, {'id': 3362, 'synset': 'wolfhound.n.01', 'name': 'wolfhound'}, {'id': 3363, 'synset': 'borzoi.n.01', 'name': 'borzoi'}, {'id': 3364, 'synset': 'irish_wolfhound.n.01', 'name': 'Irish_wolfhound'}, {'id': 3365, 'synset': 'greyhound.n.01', 'name': 'greyhound'}, {'id': 3366, 'synset': 'italian_greyhound.n.01', 'name': 'Italian_greyhound'}, {'id': 3367, 'synset': 'whippet.n.01', 'name': 'whippet'}, {'id': 3368, 'synset': 'ibizan_hound.n.01', 'name': 'Ibizan_hound'}, {'id': 3369, 'synset': 'norwegian_elkhound.n.01', 'name': 'Norwegian_elkhound'}, {'id': 3370, 'synset': 'otterhound.n.01', 'name': 'otterhound'}, {'id': 3371, 'synset': 'saluki.n.01', 'name': 'Saluki'}, {'id': 3372, 'synset': 'scottish_deerhound.n.01', 'name': 'Scottish_deerhound'}, {'id': 3373, 'synset': 'staghound.n.01', 'name': 'staghound'}, {'id': 3374, 'synset': 'weimaraner.n.01', 'name': 'Weimaraner'}, {'id': 3375, 'synset': 'terrier.n.01', 'name': 'terrier'}, {'id': 3376, 'synset': 'bullterrier.n.01', 'name': 'bullterrier'}, {'id': 3377, 'synset': 'staffordshire_bullterrier.n.01', 'name': 'Staffordshire_bullterrier'}, {'id': 3378, 'synset': 'american_staffordshire_terrier.n.01', 'name': 'American_Staffordshire_terrier'}, {'id': 3379, 'synset': 'bedlington_terrier.n.01', 'name': 'Bedlington_terrier'}, {'id': 3380, 'synset': 'border_terrier.n.01', 'name': 'Border_terrier'}, {'id': 3381, 'synset': 'kerry_blue_terrier.n.01', 'name': 'Kerry_blue_terrier'}, {'id': 3382, 'synset': 'irish_terrier.n.01', 'name': 'Irish_terrier'}, {'id': 3383, 'synset': 'norfolk_terrier.n.01', 'name': 'Norfolk_terrier'}, {'id': 3384, 'synset': 'norwich_terrier.n.01', 'name': 'Norwich_terrier'}, {'id': 3385, 'synset': 'yorkshire_terrier.n.01', 'name': 'Yorkshire_terrier'}, {'id': 3386, 'synset': 'rat_terrier.n.01', 'name': 'rat_terrier'}, {'id': 3387, 'synset': 'manchester_terrier.n.01', 'name': 'Manchester_terrier'}, {'id': 3388, 'synset': 'toy_manchester.n.01', 'name': 'toy_Manchester'}, {'id': 3389, 'synset': 'fox_terrier.n.01', 'name': 'fox_terrier'}, {'id': 3390, 'synset': 'smooth-haired_fox_terrier.n.01', 'name': 'smooth-haired_fox_terrier'}, {'id': 3391, 'synset': 'wire-haired_fox_terrier.n.01', 'name': 'wire-haired_fox_terrier'}, {'id': 3392, 'synset': 'wirehair.n.01', 'name': 'wirehair'}, {'id': 3393, 'synset': 'lakeland_terrier.n.01', 'name': 'Lakeland_terrier'}, {'id': 3394, 'synset': 'welsh_terrier.n.01', 'name': 'Welsh_terrier'}, {'id': 3395, 'synset': 'sealyham_terrier.n.01', 'name': 'Sealyham_terrier'}, {'id': 3396, 'synset': 'airedale.n.01', 'name': 'Airedale'}, {'id': 3397, 'synset': 'cairn.n.02', 'name': 'cairn'}, {'id': 3398, 'synset': 'australian_terrier.n.01', 'name': 'Australian_terrier'}, {'id': 3399, 'synset': 'dandie_dinmont.n.01', 'name': 'Dandie_Dinmont'}, {'id': 3400, 'synset': 'boston_bull.n.01', 'name': 'Boston_bull'}, {'id': 3401, 'synset': 'schnauzer.n.01', 'name': 'schnauzer'}, {'id': 3402, 'synset': 'miniature_schnauzer.n.01', 'name': 'miniature_schnauzer'}, {'id': 3403, 'synset': 'giant_schnauzer.n.01', 'name': 'giant_schnauzer'}, {'id': 3404, 'synset': 'standard_schnauzer.n.01', 'name': 'standard_schnauzer'}, {'id': 3405, 'synset': 'scotch_terrier.n.01', 'name': 'Scotch_terrier'}, {'id': 3406, 'synset': 'tibetan_terrier.n.01', 'name': 'Tibetan_terrier'}, {'id': 3407, 'synset': 'silky_terrier.n.01', 'name': 'silky_terrier'}, {'id': 3408, 'synset': 'skye_terrier.n.01', 'name': 'Skye_terrier'}, {'id': 3409, 'synset': 'clydesdale_terrier.n.01', 'name': 'Clydesdale_terrier'}, {'id': 3410, 'synset': 'soft-coated_wheaten_terrier.n.01', 'name': 'soft-coated_wheaten_terrier'}, {'id': 3411, 'synset': 'west_highland_white_terrier.n.01', 'name': 'West_Highland_white_terrier'}, {'id': 3412, 'synset': 'lhasa.n.02', 'name': 'Lhasa'}, {'id': 3413, 'synset': 'sporting_dog.n.01', 'name': 'sporting_dog'}, {'id': 3414, 'synset': 'bird_dog.n.01', 'name': 'bird_dog'}, {'id': 3415, 'synset': 'water_dog.n.02', 'name': 'water_dog'}, {'id': 3416, 'synset': 'retriever.n.01', 'name': 'retriever'}, {'id': 3417, 'synset': 'flat-coated_retriever.n.01', 'name': 'flat-coated_retriever'}, {'id': 3418, 'synset': 'curly-coated_retriever.n.01', 'name': 'curly-coated_retriever'}, {'id': 3419, 'synset': 'golden_retriever.n.01', 'name': 'golden_retriever'}, {'id': 3420, 'synset': 'labrador_retriever.n.01', 'name': 'Labrador_retriever'}, {'id': 3421, 'synset': 'chesapeake_bay_retriever.n.01', 'name': 'Chesapeake_Bay_retriever'}, {'id': 3422, 'synset': 'pointer.n.04', 'name': 'pointer'}, {'id': 3423, 'synset': 'german_short-haired_pointer.n.01', 'name': 'German_short-haired_pointer'}, {'id': 3424, 'synset': 'setter.n.02', 'name': 'setter'}, {'id': 3425, 'synset': 'vizsla.n.01', 'name': 'vizsla'}, {'id': 3426, 'synset': 'english_setter.n.01', 'name': 'English_setter'}, {'id': 3427, 'synset': 'irish_setter.n.01', 'name': 'Irish_setter'}, {'id': 3428, 'synset': 'gordon_setter.n.01', 'name': 'Gordon_setter'}, {'id': 3429, 'synset': 'spaniel.n.01', 'name': 'spaniel'}, {'id': 3430, 'synset': 'brittany_spaniel.n.01', 'name': 'Brittany_spaniel'}, {'id': 3431, 'synset': 'clumber.n.01', 'name': 'clumber'}, {'id': 3432, 'synset': 'field_spaniel.n.01', 'name': 'field_spaniel'}, {'id': 3433, 'synset': 'springer_spaniel.n.01', 'name': 'springer_spaniel'}, {'id': 3434, 'synset': 'english_springer.n.01', 'name': 'English_springer'}, {'id': 3435, 'synset': 'welsh_springer_spaniel.n.01', 'name': 'Welsh_springer_spaniel'}, {'id': 3436, 'synset': 'cocker_spaniel.n.01', 'name': 'cocker_spaniel'}, {'id': 3437, 'synset': 'sussex_spaniel.n.01', 'name': 'Sussex_spaniel'}, {'id': 3438, 'synset': 'water_spaniel.n.01', 'name': 'water_spaniel'}, {'id': 3439, 'synset': 'american_water_spaniel.n.01', 'name': 'American_water_spaniel'}, {'id': 3440, 'synset': 'irish_water_spaniel.n.01', 'name': 'Irish_water_spaniel'}, {'id': 3441, 'synset': 'griffon.n.03', 'name': 'griffon'}, {'id': 3442, 'synset': 'working_dog.n.01', 'name': 'working_dog'}, {'id': 3443, 'synset': 'watchdog.n.02', 'name': 'watchdog'}, {'id': 3444, 'synset': 'kuvasz.n.01', 'name': 'kuvasz'}, {'id': 3445, 'synset': 'attack_dog.n.01', 'name': 'attack_dog'}, {'id': 3446, 'synset': 'housedog.n.01', 'name': 'housedog'}, {'id': 3447, 'synset': 'schipperke.n.01', 'name': 'schipperke'}, {'id': 3448, 'synset': 'belgian_sheepdog.n.01', 'name': 'Belgian_sheepdog'}, {'id': 3449, 'synset': 'groenendael.n.01', 'name': 'groenendael'}, {'id': 3450, 'synset': 'malinois.n.01', 'name': 'malinois'}, {'id': 3451, 'synset': 'briard.n.01', 'name': 'briard'}, {'id': 3452, 'synset': 'kelpie.n.02', 'name': 'kelpie'}, {'id': 3453, 'synset': 'komondor.n.01', 'name': 'komondor'}, {'id': 3454, 'synset': 'old_english_sheepdog.n.01', 'name': 'Old_English_sheepdog'}, {'id': 3455, 'synset': 'shetland_sheepdog.n.01', 'name': 'Shetland_sheepdog'}, {'id': 3456, 'synset': 'collie.n.01', 'name': 'collie'}, {'id': 3457, 'synset': 'border_collie.n.01', 'name': 'Border_collie'}, {'id': 3458, 'synset': 'bouvier_des_flandres.n.01', 'name': 'Bouvier_des_Flandres'}, {'id': 3459, 'synset': 'rottweiler.n.01', 'name': 'Rottweiler'}, {'id': 3460, 'synset': 'german_shepherd.n.01', 'name': 'German_shepherd'}, {'id': 3461, 'synset': 'police_dog.n.01', 'name': 'police_dog'}, {'id': 3462, 'synset': 'pinscher.n.01', 'name': 'pinscher'}, {'id': 3463, 'synset': 'doberman.n.01', 'name': 'Doberman'}, {'id': 3464, 'synset': 'miniature_pinscher.n.01', 'name': 'miniature_pinscher'}, {'id': 3465, 'synset': 'sennenhunde.n.01', 'name': 'Sennenhunde'}, {'id': 3466, 'synset': 'greater_swiss_mountain_dog.n.01', 'name': 'Greater_Swiss_Mountain_dog'}, {'id': 3467, 'synset': 'bernese_mountain_dog.n.01', 'name': 'Bernese_mountain_dog'}, {'id': 3468, 'synset': 'appenzeller.n.01', 'name': 'Appenzeller'}, {'id': 3469, 'synset': 'entlebucher.n.01', 'name': 'EntleBucher'}, {'id': 3470, 'synset': 'boxer.n.04', 'name': 'boxer'}, {'id': 3471, 'synset': 'mastiff.n.01', 'name': 'mastiff'}, {'id': 3472, 'synset': 'bull_mastiff.n.01', 'name': 'bull_mastiff'}, {'id': 3473, 'synset': 'tibetan_mastiff.n.01', 'name': 'Tibetan_mastiff'}, {'id': 3474, 'synset': 'french_bulldog.n.01', 'name': 'French_bulldog'}, {'id': 3475, 'synset': 'great_dane.n.01', 'name': 'Great_Dane'}, {'id': 3476, 'synset': 'guide_dog.n.01', 'name': 'guide_dog'}, {'id': 3477, 'synset': 'seeing_eye_dog.n.01', 'name': 'Seeing_Eye_dog'}, {'id': 3478, 'synset': 'hearing_dog.n.01', 'name': 'hearing_dog'}, {'id': 3479, 'synset': 'saint_bernard.n.01', 'name': 'Saint_Bernard'}, {'id': 3480, 'synset': 'seizure-alert_dog.n.01', 'name': 'seizure-alert_dog'}, {'id': 3481, 'synset': 'sled_dog.n.01', 'name': 'sled_dog'}, {'id': 3482, 'synset': 'eskimo_dog.n.01', 'name': 'Eskimo_dog'}, {'id': 3483, 'synset': 'malamute.n.01', 'name': 'malamute'}, {'id': 3484, 'synset': 'siberian_husky.n.01', 'name': 'Siberian_husky'}, {'id': 3485, 'synset': 'liver-spotted_dalmatian.n.01', 'name': 'liver-spotted_dalmatian'}, {'id': 3486, 'synset': 'affenpinscher.n.01', 'name': 'affenpinscher'}, {'id': 3487, 'synset': 'basenji.n.01', 'name': 'basenji'}, {'id': 3488, 'synset': 'leonberg.n.01', 'name': 'Leonberg'}, {'id': 3489, 'synset': 'newfoundland.n.01', 'name': 'Newfoundland'}, {'id': 3490, 'synset': 'great_pyrenees.n.01', 'name': 'Great_Pyrenees'}, {'id': 3491, 'synset': 'spitz.n.01', 'name': 'spitz'}, {'id': 3492, 'synset': 'samoyed.n.03', 'name': 'Samoyed'}, {'id': 3493, 'synset': 'pomeranian.n.01', 'name': 'Pomeranian'}, {'id': 3494, 'synset': 'chow.n.03', 'name': 'chow'}, {'id': 3495, 'synset': 'keeshond.n.01', 'name': 'keeshond'}, {'id': 3496, 'synset': 'griffon.n.02', 'name': 'griffon'}, {'id': 3497, 'synset': 'brabancon_griffon.n.01', 'name': 'Brabancon_griffon'}, {'id': 3498, 'synset': 'corgi.n.01', 'name': 'corgi'}, {'id': 3499, 'synset': 'pembroke.n.01', 'name': 'Pembroke'}, {'id': 3500, 'synset': 'cardigan.n.02', 'name': 'Cardigan'}, {'id': 3501, 'synset': 'poodle.n.01', 'name': 'poodle'}, {'id': 3502, 'synset': 'toy_poodle.n.01', 'name': 'toy_poodle'}, {'id': 3503, 'synset': 'miniature_poodle.n.01', 'name': 'miniature_poodle'}, {'id': 3504, 'synset': 'standard_poodle.n.01', 'name': 'standard_poodle'}, {'id': 3505, 'synset': 'large_poodle.n.01', 'name': 'large_poodle'}, {'id': 3506, 'synset': 'mexican_hairless.n.01', 'name': 'Mexican_hairless'}, {'id': 3507, 'synset': 'timber_wolf.n.01', 'name': 'timber_wolf'}, {'id': 3508, 'synset': 'white_wolf.n.01', 'name': 'white_wolf'}, {'id': 3509, 'synset': 'red_wolf.n.01', 'name': 'red_wolf'}, {'id': 3510, 'synset': 'coyote.n.01', 'name': 'coyote'}, {'id': 3511, 'synset': 'coydog.n.01', 'name': 'coydog'}, {'id': 3512, 'synset': 'jackal.n.01', 'name': 'jackal'}, {'id': 3513, 'synset': 'wild_dog.n.01', 'name': 'wild_dog'}, {'id': 3514, 'synset': 'dingo.n.01', 'name': 'dingo'}, {'id': 3515, 'synset': 'dhole.n.01', 'name': 'dhole'}, {'id': 3516, 'synset': 'crab-eating_dog.n.01', 'name': 'crab-eating_dog'}, {'id': 3517, 'synset': 'raccoon_dog.n.01', 'name': 'raccoon_dog'}, {'id': 3518, 'synset': 'african_hunting_dog.n.01', 'name': 'African_hunting_dog'}, {'id': 3519, 'synset': 'hyena.n.01', 'name': 'hyena'}, {'id': 3520, 'synset': 'striped_hyena.n.01', 'name': 'striped_hyena'}, {'id': 3521, 'synset': 'brown_hyena.n.01', 'name': 'brown_hyena'}, {'id': 3522, 'synset': 'spotted_hyena.n.01', 'name': 'spotted_hyena'}, {'id': 3523, 'synset': 'aardwolf.n.01', 'name': 'aardwolf'}, {'id': 3524, 'synset': 'fox.n.01', 'name': 'fox'}, {'id': 3525, 'synset': 'vixen.n.02', 'name': 'vixen'}, {'id': 3526, 'synset': 'reynard.n.01', 'name': 'Reynard'}, {'id': 3527, 'synset': 'red_fox.n.03', 'name': 'red_fox'}, {'id': 3528, 'synset': 'black_fox.n.01', 'name': 'black_fox'}, {'id': 3529, 'synset': 'silver_fox.n.01', 'name': 'silver_fox'}, {'id': 3530, 'synset': 'red_fox.n.02', 'name': 'red_fox'}, {'id': 3531, 'synset': 'kit_fox.n.02', 'name': 'kit_fox'}, {'id': 3532, 'synset': 'kit_fox.n.01', 'name': 'kit_fox'}, {'id': 3533, 'synset': 'arctic_fox.n.01', 'name': 'Arctic_fox'}, {'id': 3534, 'synset': 'blue_fox.n.01', 'name': 'blue_fox'}, {'id': 3535, 'synset': 'grey_fox.n.01', 'name': 'grey_fox'}, {'id': 3536, 'synset': 'feline.n.01', 'name': 'feline'}, {'id': 3537, 'synset': 'domestic_cat.n.01', 'name': 'domestic_cat'}, {'id': 3538, 'synset': 'kitty.n.04', 'name': 'kitty'}, {'id': 3539, 'synset': 'mouser.n.01', 'name': 'mouser'}, {'id': 3540, 'synset': 'alley_cat.n.01', 'name': 'alley_cat'}, {'id': 3541, 'synset': 'stray.n.01', 'name': 'stray'}, {'id': 3542, 'synset': 'tom.n.02', 'name': 'tom'}, {'id': 3543, 'synset': 'gib.n.02', 'name': 'gib'}, {'id': 3544, 'synset': 'tabby.n.02', 'name': 'tabby'}, {'id': 3545, 'synset': 'tabby.n.01', 'name': 'tabby'}, {'id': 3546, 'synset': 'tiger_cat.n.02', 'name': 'tiger_cat'}, {'id': 3547, 'synset': 'tortoiseshell.n.03', 'name': 'tortoiseshell'}, {'id': 3548, 'synset': 'persian_cat.n.01', 'name': 'Persian_cat'}, {'id': 3549, 'synset': 'angora.n.04', 'name': 'Angora'}, {'id': 3550, 'synset': 'siamese_cat.n.01', 'name': 'Siamese_cat'}, {'id': 3551, 'synset': 'blue_point_siamese.n.01', 'name': 'blue_point_Siamese'}, {'id': 3552, 'synset': 'burmese_cat.n.01', 'name': 'Burmese_cat'}, {'id': 3553, 'synset': 'egyptian_cat.n.01', 'name': 'Egyptian_cat'}, {'id': 3554, 'synset': 'maltese.n.03', 'name': 'Maltese'}, {'id': 3555, 'synset': 'abyssinian.n.01', 'name': 'Abyssinian'}, {'id': 3556, 'synset': 'manx.n.02', 'name': 'Manx'}, {'id': 3557, 'synset': 'wildcat.n.03', 'name': 'wildcat'}, {'id': 3558, 'synset': 'sand_cat.n.01', 'name': 'sand_cat'}, {'id': 3559, 'synset': 'european_wildcat.n.01', 'name': 'European_wildcat'}, {'id': 3560, 'synset': 'ocelot.n.01', 'name': 'ocelot'}, {'id': 3561, 'synset': 'jaguarundi.n.01', 'name': 'jaguarundi'}, {'id': 3562, 'synset': 'kaffir_cat.n.01', 'name': 'kaffir_cat'}, {'id': 3563, 'synset': 'jungle_cat.n.01', 'name': 'jungle_cat'}, {'id': 3564, 'synset': 'serval.n.01', 'name': 'serval'}, {'id': 3565, 'synset': 'leopard_cat.n.01', 'name': 'leopard_cat'}, {'id': 3566, 'synset': 'margay.n.01', 'name': 'margay'}, {'id': 3567, 'synset': 'manul.n.01', 'name': 'manul'}, {'id': 3568, 'synset': 'lynx.n.02', 'name': 'lynx'}, {'id': 3569, 'synset': 'common_lynx.n.01', 'name': 'common_lynx'}, {'id': 3570, 'synset': 'canada_lynx.n.01', 'name': 'Canada_lynx'}, {'id': 3571, 'synset': 'bobcat.n.01', 'name': 'bobcat'}, {'id': 3572, 'synset': 'spotted_lynx.n.01', 'name': 'spotted_lynx'}, {'id': 3573, 'synset': 'caracal.n.01', 'name': 'caracal'}, {'id': 3574, 'synset': 'big_cat.n.01', 'name': 'big_cat'}, {'id': 3575, 'synset': 'leopard.n.02', 'name': 'leopard'}, {'id': 3576, 'synset': 'leopardess.n.01', 'name': 'leopardess'}, {'id': 3577, 'synset': 'panther.n.02', 'name': 'panther'}, {'id': 3578, 'synset': 'snow_leopard.n.01', 'name': 'snow_leopard'}, {'id': 3579, 'synset': 'jaguar.n.01', 'name': 'jaguar'}, {'id': 3580, 'synset': 'lioness.n.01', 'name': 'lioness'}, {'id': 3581, 'synset': 'lionet.n.01', 'name': 'lionet'}, {'id': 3582, 'synset': 'bengal_tiger.n.01', 'name': 'Bengal_tiger'}, {'id': 3583, 'synset': 'tigress.n.01', 'name': 'tigress'}, {'id': 3584, 'synset': 'liger.n.01', 'name': 'liger'}, {'id': 3585, 'synset': 'tiglon.n.01', 'name': 'tiglon'}, {'id': 3586, 'synset': 'cheetah.n.01', 'name': 'cheetah'}, {'id': 3587, 'synset': 'saber-toothed_tiger.n.01', 'name': 'saber-toothed_tiger'}, {'id': 3588, 'synset': 'smiledon_californicus.n.01', 'name': 'Smiledon_californicus'}, {'id': 3589, 'synset': 'brown_bear.n.01', 'name': 'brown_bear'}, {'id': 3590, 'synset': 'bruin.n.01', 'name': 'bruin'}, {'id': 3591, 'synset': 'syrian_bear.n.01', 'name': 'Syrian_bear'}, {'id': 3592, 'synset': 'alaskan_brown_bear.n.01', 'name': 'Alaskan_brown_bear'}, {'id': 3593, 'synset': 'american_black_bear.n.01', 'name': 'American_black_bear'}, {'id': 3594, 'synset': 'cinnamon_bear.n.01', 'name': 'cinnamon_bear'}, {'id': 3595, 'synset': 'asiatic_black_bear.n.01', 'name': 'Asiatic_black_bear'}, {'id': 3596, 'synset': 'sloth_bear.n.01', 'name': 'sloth_bear'}, {'id': 3597, 'synset': 'viverrine.n.01', 'name': 'viverrine'}, {'id': 3598, 'synset': 'civet.n.01', 'name': 'civet'}, {'id': 3599, 'synset': 'large_civet.n.01', 'name': 'large_civet'}, {'id': 3600, 'synset': 'small_civet.n.01', 'name': 'small_civet'}, {'id': 3601, 'synset': 'binturong.n.01', 'name': 'binturong'}, {'id': 3602, 'synset': 'cryptoprocta.n.01', 'name': 'Cryptoprocta'}, {'id': 3603, 'synset': 'fossa.n.03', 'name': 'fossa'}, {'id': 3604, 'synset': 'fanaloka.n.01', 'name': 'fanaloka'}, {'id': 3605, 'synset': 'genet.n.03', 'name': 'genet'}, {'id': 3606, 'synset': 'banded_palm_civet.n.01', 'name': 'banded_palm_civet'}, {'id': 3607, 'synset': 'mongoose.n.01', 'name': 'mongoose'}, {'id': 3608, 'synset': 'indian_mongoose.n.01', 'name': 'Indian_mongoose'}, {'id': 3609, 'synset': 'ichneumon.n.01', 'name': 'ichneumon'}, {'id': 3610, 'synset': 'palm_cat.n.01', 'name': 'palm_cat'}, {'id': 3611, 'synset': 'meerkat.n.01', 'name': 'meerkat'}, {'id': 3612, 'synset': 'slender-tailed_meerkat.n.01', 'name': 'slender-tailed_meerkat'}, {'id': 3613, 'synset': 'suricate.n.01', 'name': 'suricate'}, {'id': 3614, 'synset': 'fruit_bat.n.01', 'name': 'fruit_bat'}, {'id': 3615, 'synset': 'flying_fox.n.01', 'name': 'flying_fox'}, {'id': 3616, 'synset': 'pteropus_capestratus.n.01', 'name': 'Pteropus_capestratus'}, {'id': 3617, 'synset': 'pteropus_hypomelanus.n.01', 'name': 'Pteropus_hypomelanus'}, {'id': 3618, 'synset': 'harpy.n.03', 'name': 'harpy'}, {'id': 3619, 'synset': 'cynopterus_sphinx.n.01', 'name': 'Cynopterus_sphinx'}, {'id': 3620, 'synset': 'carnivorous_bat.n.01', 'name': 'carnivorous_bat'}, {'id': 3621, 'synset': 'mouse-eared_bat.n.01', 'name': 'mouse-eared_bat'}, {'id': 3622, 'synset': 'leafnose_bat.n.01', 'name': 'leafnose_bat'}, {'id': 3623, 'synset': 'macrotus.n.01', 'name': 'macrotus'}, {'id': 3624, 'synset': 'spearnose_bat.n.01', 'name': 'spearnose_bat'}, {'id': 3625, 'synset': 'phyllostomus_hastatus.n.01', 'name': 'Phyllostomus_hastatus'}, {'id': 3626, 'synset': 'hognose_bat.n.01', 'name': 'hognose_bat'}, {'id': 3627, 'synset': 'horseshoe_bat.n.02', 'name': 'horseshoe_bat'}, {'id': 3628, 'synset': 'horseshoe_bat.n.01', 'name': 'horseshoe_bat'}, {'id': 3629, 'synset': 'orange_bat.n.01', 'name': 'orange_bat'}, {'id': 3630, 'synset': 'false_vampire.n.01', 'name': 'false_vampire'}, {'id': 3631, 'synset': 'big-eared_bat.n.01', 'name': 'big-eared_bat'}, {'id': 3632, 'synset': 'vespertilian_bat.n.01', 'name': 'vespertilian_bat'}, {'id': 3633, 'synset': 'frosted_bat.n.01', 'name': 'frosted_bat'}, {'id': 3634, 'synset': 'red_bat.n.01', 'name': 'red_bat'}, {'id': 3635, 'synset': 'brown_bat.n.01', 'name': 'brown_bat'}, {'id': 3636, 'synset': 'little_brown_bat.n.01', 'name': 'little_brown_bat'}, {'id': 3637, 'synset': 'cave_myotis.n.01', 'name': 'cave_myotis'}, {'id': 3638, 'synset': 'big_brown_bat.n.01', 'name': 'big_brown_bat'}, {'id': 3639, 'synset': 'serotine.n.01', 'name': 'serotine'}, {'id': 3640, 'synset': 'pallid_bat.n.01', 'name': 'pallid_bat'}, {'id': 3641, 'synset': 'pipistrelle.n.01', 'name': 'pipistrelle'}, {'id': 3642, 'synset': 'eastern_pipistrel.n.01', 'name': 'eastern_pipistrel'}, {'id': 3643, 'synset': 'jackass_bat.n.01', 'name': 'jackass_bat'}, {'id': 3644, 'synset': 'long-eared_bat.n.01', 'name': 'long-eared_bat'}, {'id': 3645, 'synset': 'western_big-eared_bat.n.01', 'name': 'western_big-eared_bat'}, {'id': 3646, 'synset': 'freetail.n.01', 'name': 'freetail'}, {'id': 3647, 'synset': 'guano_bat.n.01', 'name': 'guano_bat'}, {'id': 3648, 'synset': 'pocketed_bat.n.01', 'name': 'pocketed_bat'}, {'id': 3649, 'synset': 'mastiff_bat.n.01', 'name': 'mastiff_bat'}, {'id': 3650, 'synset': 'vampire_bat.n.01', 'name': 'vampire_bat'}, {'id': 3651, 'synset': 'desmodus_rotundus.n.01', 'name': 'Desmodus_rotundus'}, {'id': 3652, 'synset': 'hairy-legged_vampire_bat.n.01', 'name': 'hairy-legged_vampire_bat'}, {'id': 3653, 'synset': 'predator.n.02', 'name': 'predator'}, {'id': 3654, 'synset': 'prey.n.02', 'name': 'prey'}, {'id': 3655, 'synset': 'game.n.04', 'name': 'game'}, {'id': 3656, 'synset': 'big_game.n.01', 'name': 'big_game'}, {'id': 3657, 'synset': 'game_bird.n.01', 'name': 'game_bird'}, {'id': 3658, 'synset': 'fossorial_mammal.n.01', 'name': 'fossorial_mammal'}, {'id': 3659, 'synset': 'tetrapod.n.01', 'name': 'tetrapod'}, {'id': 3660, 'synset': 'quadruped.n.01', 'name': 'quadruped'}, {'id': 3661, 'synset': 'hexapod.n.01', 'name': 'hexapod'}, {'id': 3662, 'synset': 'biped.n.01', 'name': 'biped'}, {'id': 3663, 'synset': 'insect.n.01', 'name': 'insect'}, {'id': 3664, 'synset': 'social_insect.n.01', 'name': 'social_insect'}, {'id': 3665, 'synset': 'holometabola.n.01', 'name': 'holometabola'}, {'id': 3666, 'synset': 'defoliator.n.01', 'name': 'defoliator'}, {'id': 3667, 'synset': 'pollinator.n.01', 'name': 'pollinator'}, {'id': 3668, 'synset': 'gallfly.n.03', 'name': 'gallfly'}, {'id': 3669, 'synset': 'scorpion_fly.n.01', 'name': 'scorpion_fly'}, {'id': 3670, 'synset': 'hanging_fly.n.01', 'name': 'hanging_fly'}, {'id': 3671, 'synset': 'collembolan.n.01', 'name': 'collembolan'}, {'id': 3672, 'synset': 'tiger_beetle.n.01', 'name': 'tiger_beetle'}, {'id': 3673, 'synset': 'two-spotted_ladybug.n.01', 'name': 'two-spotted_ladybug'}, {'id': 3674, 'synset': 'mexican_bean_beetle.n.01', 'name': 'Mexican_bean_beetle'}, {'id': 3675, 'synset': 'hippodamia_convergens.n.01', 'name': 'Hippodamia_convergens'}, {'id': 3676, 'synset': 'vedalia.n.01', 'name': 'vedalia'}, {'id': 3677, 'synset': 'ground_beetle.n.01', 'name': 'ground_beetle'}, {'id': 3678, 'synset': 'bombardier_beetle.n.01', 'name': 'bombardier_beetle'}, {'id': 3679, 'synset': 'calosoma.n.01', 'name': 'calosoma'}, {'id': 3680, 'synset': 'searcher.n.03', 'name': 'searcher'}, {'id': 3681, 'synset': 'firefly.n.02', 'name': 'firefly'}, {'id': 3682, 'synset': 'glowworm.n.01', 'name': 'glowworm'}, {'id': 3683, 'synset': 'long-horned_beetle.n.01', 'name': 'long-horned_beetle'}, {'id': 3684, 'synset': 'sawyer.n.02', 'name': 'sawyer'}, {'id': 3685, 'synset': 'pine_sawyer.n.01', 'name': 'pine_sawyer'}, {'id': 3686, 'synset': 'leaf_beetle.n.01', 'name': 'leaf_beetle'}, {'id': 3687, 'synset': 'flea_beetle.n.01', 'name': 'flea_beetle'}, {'id': 3688, 'synset': 'colorado_potato_beetle.n.01', 'name': 'Colorado_potato_beetle'}, {'id': 3689, 'synset': 'carpet_beetle.n.01', 'name': 'carpet_beetle'}, {'id': 3690, 'synset': 'buffalo_carpet_beetle.n.01', 'name': 'buffalo_carpet_beetle'}, {'id': 3691, 'synset': 'black_carpet_beetle.n.01', 'name': 'black_carpet_beetle'}, {'id': 3692, 'synset': 'clerid_beetle.n.01', 'name': 'clerid_beetle'}, {'id': 3693, 'synset': 'bee_beetle.n.01', 'name': 'bee_beetle'}, {'id': 3694, 'synset': 'lamellicorn_beetle.n.01', 'name': 'lamellicorn_beetle'}, {'id': 3695, 'synset': 'scarabaeid_beetle.n.01', 'name': 'scarabaeid_beetle'}, {'id': 3696, 'synset': 'dung_beetle.n.01', 'name': 'dung_beetle'}, {'id': 3697, 'synset': 'scarab.n.01', 'name': 'scarab'}, {'id': 3698, 'synset': 'tumblebug.n.01', 'name': 'tumblebug'}, {'id': 3699, 'synset': 'dorbeetle.n.01', 'name': 'dorbeetle'}, {'id': 3700, 'synset': 'june_beetle.n.01', 'name': 'June_beetle'}, {'id': 3701, 'synset': 'green_june_beetle.n.01', 'name': 'green_June_beetle'}, {'id': 3702, 'synset': 'japanese_beetle.n.01', 'name': 'Japanese_beetle'}, {'id': 3703, 'synset': 'oriental_beetle.n.01', 'name': 'Oriental_beetle'}, {'id': 3704, 'synset': 'rhinoceros_beetle.n.01', 'name': 'rhinoceros_beetle'}, {'id': 3705, 'synset': 'melolonthid_beetle.n.01', 'name': 'melolonthid_beetle'}, {'id': 3706, 'synset': 'cockchafer.n.01', 'name': 'cockchafer'}, {'id': 3707, 'synset': 'rose_chafer.n.02', 'name': 'rose_chafer'}, {'id': 3708, 'synset': 'rose_chafer.n.01', 'name': 'rose_chafer'}, {'id': 3709, 'synset': 'stag_beetle.n.01', 'name': 'stag_beetle'}, {'id': 3710, 'synset': 'elaterid_beetle.n.01', 'name': 'elaterid_beetle'}, {'id': 3711, 'synset': 'click_beetle.n.01', 'name': 'click_beetle'}, {'id': 3712, 'synset': 'firefly.n.01', 'name': 'firefly'}, {'id': 3713, 'synset': 'wireworm.n.01', 'name': 'wireworm'}, {'id': 3714, 'synset': 'water_beetle.n.01', 'name': 'water_beetle'}, {'id': 3715, 'synset': 'whirligig_beetle.n.01', 'name': 'whirligig_beetle'}, {'id': 3716, 'synset': 'deathwatch_beetle.n.01', 'name': 'deathwatch_beetle'}, {'id': 3717, 'synset': 'weevil.n.01', 'name': 'weevil'}, {'id': 3718, 'synset': 'snout_beetle.n.01', 'name': 'snout_beetle'}, {'id': 3719, 'synset': 'boll_weevil.n.01', 'name': 'boll_weevil'}, {'id': 3720, 'synset': 'blister_beetle.n.01', 'name': 'blister_beetle'}, {'id': 3721, 'synset': 'oil_beetle.n.01', 'name': 'oil_beetle'}, {'id': 3722, 'synset': 'spanish_fly.n.01', 'name': 'Spanish_fly'}, {'id': 3723, 'synset': 'dutch-elm_beetle.n.01', 'name': 'Dutch-elm_beetle'}, {'id': 3724, 'synset': 'bark_beetle.n.01', 'name': 'bark_beetle'}, {'id': 3725, 'synset': 'spruce_bark_beetle.n.01', 'name': 'spruce_bark_beetle'}, {'id': 3726, 'synset': 'rove_beetle.n.01', 'name': 'rove_beetle'}, {'id': 3727, 'synset': 'darkling_beetle.n.01', 'name': 'darkling_beetle'}, {'id': 3728, 'synset': 'mealworm.n.01', 'name': 'mealworm'}, {'id': 3729, 'synset': 'flour_beetle.n.01', 'name': 'flour_beetle'}, {'id': 3730, 'synset': 'seed_beetle.n.01', 'name': 'seed_beetle'}, {'id': 3731, 'synset': 'pea_weevil.n.01', 'name': 'pea_weevil'}, {'id': 3732, 'synset': 'bean_weevil.n.01', 'name': 'bean_weevil'}, {'id': 3733, 'synset': 'rice_weevil.n.01', 'name': 'rice_weevil'}, {'id': 3734, 'synset': 'asian_longhorned_beetle.n.01', 'name': 'Asian_longhorned_beetle'}, {'id': 3735, 'synset': 'web_spinner.n.01', 'name': 'web_spinner'}, {'id': 3736, 'synset': 'louse.n.01', 'name': 'louse'}, {'id': 3737, 'synset': 'common_louse.n.01', 'name': 'common_louse'}, {'id': 3738, 'synset': 'head_louse.n.01', 'name': 'head_louse'}, {'id': 3739, 'synset': 'body_louse.n.01', 'name': 'body_louse'}, {'id': 3740, 'synset': 'crab_louse.n.01', 'name': 'crab_louse'}, {'id': 3741, 'synset': 'bird_louse.n.01', 'name': 'bird_louse'}, {'id': 3742, 'synset': 'flea.n.01', 'name': 'flea'}, {'id': 3743, 'synset': 'pulex_irritans.n.01', 'name': 'Pulex_irritans'}, {'id': 3744, 'synset': 'dog_flea.n.01', 'name': 'dog_flea'}, {'id': 3745, 'synset': 'cat_flea.n.01', 'name': 'cat_flea'}, {'id': 3746, 'synset': 'chigoe.n.01', 'name': 'chigoe'}, {'id': 3747, 'synset': 'sticktight.n.02', 'name': 'sticktight'}, {'id': 3748, 'synset': 'dipterous_insect.n.01', 'name': 'dipterous_insect'}, {'id': 3749, 'synset': 'gall_midge.n.01', 'name': 'gall_midge'}, {'id': 3750, 'synset': 'hessian_fly.n.01', 'name': 'Hessian_fly'}, {'id': 3751, 'synset': 'fly.n.01', 'name': 'fly'}, {'id': 3752, 'synset': 'housefly.n.01', 'name': 'housefly'}, {'id': 3753, 'synset': 'tsetse_fly.n.01', 'name': 'tsetse_fly'}, {'id': 3754, 'synset': 'blowfly.n.01', 'name': 'blowfly'}, {'id': 3755, 'synset': 'bluebottle.n.02', 'name': 'bluebottle'}, {'id': 3756, 'synset': 'greenbottle.n.01', 'name': 'greenbottle'}, {'id': 3757, 'synset': 'flesh_fly.n.01', 'name': 'flesh_fly'}, {'id': 3758, 'synset': 'tachina_fly.n.01', 'name': 'tachina_fly'}, {'id': 3759, 'synset': 'gadfly.n.02', 'name': 'gadfly'}, {'id': 3760, 'synset': 'botfly.n.01', 'name': 'botfly'}, {'id': 3761, 'synset': 'human_botfly.n.01', 'name': 'human_botfly'}, {'id': 3762, 'synset': 'sheep_botfly.n.01', 'name': 'sheep_botfly'}, {'id': 3763, 'synset': 'warble_fly.n.01', 'name': 'warble_fly'}, {'id': 3764, 'synset': 'horsefly.n.02', 'name': 'horsefly'}, {'id': 3765, 'synset': 'bee_fly.n.01', 'name': 'bee_fly'}, {'id': 3766, 'synset': 'robber_fly.n.01', 'name': 'robber_fly'}, {'id': 3767, 'synset': 'fruit_fly.n.01', 'name': 'fruit_fly'}, {'id': 3768, 'synset': 'apple_maggot.n.01', 'name': 'apple_maggot'}, {'id': 3769, 'synset': 'mediterranean_fruit_fly.n.01', 'name': 'Mediterranean_fruit_fly'}, {'id': 3770, 'synset': 'drosophila.n.01', 'name': 'drosophila'}, {'id': 3771, 'synset': 'vinegar_fly.n.01', 'name': 'vinegar_fly'}, {'id': 3772, 'synset': 'leaf_miner.n.01', 'name': 'leaf_miner'}, {'id': 3773, 'synset': 'louse_fly.n.01', 'name': 'louse_fly'}, {'id': 3774, 'synset': 'horse_tick.n.01', 'name': 'horse_tick'}, {'id': 3775, 'synset': 'sheep_ked.n.01', 'name': 'sheep_ked'}, {'id': 3776, 'synset': 'horn_fly.n.01', 'name': 'horn_fly'}, {'id': 3777, 'synset': 'mosquito.n.01', 'name': 'mosquito'}, {'id': 3778, 'synset': 'wiggler.n.02', 'name': 'wiggler'}, {'id': 3779, 'synset': 'gnat.n.02', 'name': 'gnat'}, {'id': 3780, 'synset': 'yellow-fever_mosquito.n.01', 'name': 'yellow-fever_mosquito'}, {'id': 3781, 'synset': 'asian_tiger_mosquito.n.01', 'name': 'Asian_tiger_mosquito'}, {'id': 3782, 'synset': 'anopheline.n.01', 'name': 'anopheline'}, {'id': 3783, 'synset': 'malarial_mosquito.n.01', 'name': 'malarial_mosquito'}, {'id': 3784, 'synset': 'common_mosquito.n.01', 'name': 'common_mosquito'}, {'id': 3785, 'synset': 'culex_quinquefasciatus.n.01', 'name': 'Culex_quinquefasciatus'}, {'id': 3786, 'synset': 'gnat.n.01', 'name': 'gnat'}, {'id': 3787, 'synset': 'punkie.n.01', 'name': 'punkie'}, {'id': 3788, 'synset': 'midge.n.01', 'name': 'midge'}, {'id': 3789, 'synset': 'fungus_gnat.n.02', 'name': 'fungus_gnat'}, {'id': 3790, 'synset': 'psychodid.n.01', 'name': 'psychodid'}, {'id': 3791, 'synset': 'sand_fly.n.01', 'name': 'sand_fly'}, {'id': 3792, 'synset': 'fungus_gnat.n.01', 'name': 'fungus_gnat'}, {'id': 3793, 'synset': 'armyworm.n.03', 'name': 'armyworm'}, {'id': 3794, 'synset': 'crane_fly.n.01', 'name': 'crane_fly'}, {'id': 3795, 'synset': 'blackfly.n.02', 'name': 'blackfly'}, {'id': 3796, 'synset': 'hymenopterous_insect.n.01', 'name': 'hymenopterous_insect'}, {'id': 3797, 'synset': 'bee.n.01', 'name': 'bee'}, {'id': 3798, 'synset': 'drone.n.01', 'name': 'drone'}, {'id': 3799, 'synset': 'queen_bee.n.01', 'name': 'queen_bee'}, {'id': 3800, 'synset': 'worker.n.03', 'name': 'worker'}, {'id': 3801, 'synset': 'soldier.n.02', 'name': 'soldier'}, {'id': 3802, 'synset': 'worker_bee.n.01', 'name': 'worker_bee'}, {'id': 3803, 'synset': 'honeybee.n.01', 'name': 'honeybee'}, {'id': 3804, 'synset': 'africanized_bee.n.01', 'name': 'Africanized_bee'}, {'id': 3805, 'synset': 'black_bee.n.01', 'name': 'black_bee'}, {'id': 3806, 'synset': 'carniolan_bee.n.01', 'name': 'Carniolan_bee'}, {'id': 3807, 'synset': 'italian_bee.n.01', 'name': 'Italian_bee'}, {'id': 3808, 'synset': 'carpenter_bee.n.01', 'name': 'carpenter_bee'}, {'id': 3809, 'synset': 'bumblebee.n.01', 'name': 'bumblebee'}, {'id': 3810, 'synset': 'cuckoo-bumblebee.n.01', 'name': 'cuckoo-bumblebee'}, {'id': 3811, 'synset': 'andrena.n.01', 'name': 'andrena'}, {'id': 3812, 'synset': 'nomia_melanderi.n.01', 'name': 'Nomia_melanderi'}, {'id': 3813, 'synset': 'leaf-cutting_bee.n.01', 'name': 'leaf-cutting_bee'}, {'id': 3814, 'synset': 'mason_bee.n.01', 'name': 'mason_bee'}, {'id': 3815, 'synset': 'potter_bee.n.01', 'name': 'potter_bee'}, {'id': 3816, 'synset': 'wasp.n.02', 'name': 'wasp'}, {'id': 3817, 'synset': 'vespid.n.01', 'name': 'vespid'}, {'id': 3818, 'synset': 'paper_wasp.n.01', 'name': 'paper_wasp'}, {'id': 3819, 'synset': 'giant_hornet.n.01', 'name': 'giant_hornet'}, {'id': 3820, 'synset': 'common_wasp.n.01', 'name': 'common_wasp'}, {'id': 3821, 'synset': 'bald-faced_hornet.n.01', 'name': 'bald-faced_hornet'}, {'id': 3822, 'synset': 'yellow_jacket.n.02', 'name': 'yellow_jacket'}, {'id': 3823, 'synset': 'polistes_annularis.n.01', 'name': 'Polistes_annularis'}, {'id': 3824, 'synset': 'mason_wasp.n.02', 'name': 'mason_wasp'}, {'id': 3825, 'synset': 'potter_wasp.n.01', 'name': 'potter_wasp'}, {'id': 3826, 'synset': 'mutillidae.n.01', 'name': 'Mutillidae'}, {'id': 3827, 'synset': 'velvet_ant.n.01', 'name': 'velvet_ant'}, {'id': 3828, 'synset': 'sphecoid_wasp.n.01', 'name': 'sphecoid_wasp'}, {'id': 3829, 'synset': 'mason_wasp.n.01', 'name': 'mason_wasp'}, {'id': 3830, 'synset': 'digger_wasp.n.01', 'name': 'digger_wasp'}, {'id': 3831, 'synset': 'cicada_killer.n.01', 'name': 'cicada_killer'}, {'id': 3832, 'synset': 'mud_dauber.n.01', 'name': 'mud_dauber'}, {'id': 3833, 'synset': 'gall_wasp.n.01', 'name': 'gall_wasp'}, {'id': 3834, 'synset': 'chalcid_fly.n.01', 'name': 'chalcid_fly'}, {'id': 3835, 'synset': 'strawworm.n.02', 'name': 'strawworm'}, {'id': 3836, 'synset': 'chalcis_fly.n.01', 'name': 'chalcis_fly'}, {'id': 3837, 'synset': 'ichneumon_fly.n.01', 'name': 'ichneumon_fly'}, {'id': 3838, 'synset': 'sawfly.n.01', 'name': 'sawfly'}, {'id': 3839, 'synset': 'birch_leaf_miner.n.01', 'name': 'birch_leaf_miner'}, {'id': 3840, 'synset': 'ant.n.01', 'name': 'ant'}, {'id': 3841, 'synset': 'pharaoh_ant.n.01', 'name': 'pharaoh_ant'}, {'id': 3842, 'synset': 'little_black_ant.n.01', 'name': 'little_black_ant'}, {'id': 3843, 'synset': 'army_ant.n.01', 'name': 'army_ant'}, {'id': 3844, 'synset': 'carpenter_ant.n.01', 'name': 'carpenter_ant'}, {'id': 3845, 'synset': 'fire_ant.n.01', 'name': 'fire_ant'}, {'id': 3846, 'synset': 'wood_ant.n.01', 'name': 'wood_ant'}, {'id': 3847, 'synset': 'slave_ant.n.01', 'name': 'slave_ant'}, {'id': 3848, 'synset': 'formica_fusca.n.01', 'name': 'Formica_fusca'}, {'id': 3849, 'synset': 'slave-making_ant.n.01', 'name': 'slave-making_ant'}, {'id': 3850, 'synset': 'sanguinary_ant.n.01', 'name': 'sanguinary_ant'}, {'id': 3851, 'synset': 'bulldog_ant.n.01', 'name': 'bulldog_ant'}, {'id': 3852, 'synset': 'amazon_ant.n.01', 'name': 'Amazon_ant'}, {'id': 3853, 'synset': 'termite.n.01', 'name': 'termite'}, {'id': 3854, 'synset': 'dry-wood_termite.n.01', 'name': 'dry-wood_termite'}, {'id': 3855, 'synset': 'reticulitermes_lucifugus.n.01', 'name': 'Reticulitermes_lucifugus'}, {'id': 3856, 'synset': 'mastotermes_darwiniensis.n.01', 'name': 'Mastotermes_darwiniensis'}, {'id': 3857, 'synset': 'mastotermes_electrodominicus.n.01', 'name': 'Mastotermes_electrodominicus'}, {'id': 3858, 'synset': 'powder-post_termite.n.01', 'name': 'powder-post_termite'}, {'id': 3859, 'synset': 'orthopterous_insect.n.01', 'name': 'orthopterous_insect'}, {'id': 3860, 'synset': 'grasshopper.n.01', 'name': 'grasshopper'}, {'id': 3861, 'synset': 'short-horned_grasshopper.n.01', 'name': 'short-horned_grasshopper'}, {'id': 3862, 'synset': 'locust.n.01', 'name': 'locust'}, {'id': 3863, 'synset': 'migratory_locust.n.01', 'name': 'migratory_locust'}, {'id': 3864, 'synset': 'migratory_grasshopper.n.01', 'name': 'migratory_grasshopper'}, {'id': 3865, 'synset': 'long-horned_grasshopper.n.01', 'name': 'long-horned_grasshopper'}, {'id': 3866, 'synset': 'katydid.n.01', 'name': 'katydid'}, {'id': 3867, 'synset': 'mormon_cricket.n.01', 'name': 'mormon_cricket'}, {'id': 3868, 'synset': 'sand_cricket.n.01', 'name': 'sand_cricket'}, {'id': 3869, 'synset': 'cricket.n.01', 'name': 'cricket'}, {'id': 3870, 'synset': 'mole_cricket.n.01', 'name': 'mole_cricket'}, {'id': 3871, 'synset': 'european_house_cricket.n.01', 'name': 'European_house_cricket'}, {'id': 3872, 'synset': 'field_cricket.n.01', 'name': 'field_cricket'}, {'id': 3873, 'synset': 'tree_cricket.n.01', 'name': 'tree_cricket'}, {'id': 3874, 'synset': 'snowy_tree_cricket.n.01', 'name': 'snowy_tree_cricket'}, {'id': 3875, 'synset': 'phasmid.n.01', 'name': 'phasmid'}, {'id': 3876, 'synset': 'walking_stick.n.02', 'name': 'walking_stick'}, {'id': 3877, 'synset': 'diapheromera.n.01', 'name': 'diapheromera'}, {'id': 3878, 'synset': 'walking_leaf.n.02', 'name': 'walking_leaf'}, {'id': 3879, 'synset': 'oriental_cockroach.n.01', 'name': 'oriental_cockroach'}, {'id': 3880, 'synset': 'american_cockroach.n.01', 'name': 'American_cockroach'}, {'id': 3881, 'synset': 'australian_cockroach.n.01', 'name': 'Australian_cockroach'}, {'id': 3882, 'synset': 'german_cockroach.n.01', 'name': 'German_cockroach'}, {'id': 3883, 'synset': 'giant_cockroach.n.01', 'name': 'giant_cockroach'}, {'id': 3884, 'synset': 'mantis.n.01', 'name': 'mantis'}, {'id': 3885, 'synset': 'praying_mantis.n.01', 'name': 'praying_mantis'}, {'id': 3886, 'synset': 'bug.n.01', 'name': 'bug'}, {'id': 3887, 'synset': 'hemipterous_insect.n.01', 'name': 'hemipterous_insect'}, {'id': 3888, 'synset': 'leaf_bug.n.01', 'name': 'leaf_bug'}, {'id': 3889, 'synset': 'mirid_bug.n.01', 'name': 'mirid_bug'}, {'id': 3890, 'synset': 'four-lined_plant_bug.n.01', 'name': 'four-lined_plant_bug'}, {'id': 3891, 'synset': 'lygus_bug.n.01', 'name': 'lygus_bug'}, {'id': 3892, 'synset': 'tarnished_plant_bug.n.01', 'name': 'tarnished_plant_bug'}, {'id': 3893, 'synset': 'lace_bug.n.01', 'name': 'lace_bug'}, {'id': 3894, 'synset': 'lygaeid.n.01', 'name': 'lygaeid'}, {'id': 3895, 'synset': 'chinch_bug.n.01', 'name': 'chinch_bug'}, {'id': 3896, 'synset': 'coreid_bug.n.01', 'name': 'coreid_bug'}, {'id': 3897, 'synset': 'squash_bug.n.01', 'name': 'squash_bug'}, {'id': 3898, 'synset': 'leaf-footed_bug.n.01', 'name': 'leaf-footed_bug'}, {'id': 3899, 'synset': 'bedbug.n.01', 'name': 'bedbug'}, {'id': 3900, 'synset': 'backswimmer.n.01', 'name': 'backswimmer'}, {'id': 3901, 'synset': 'true_bug.n.01', 'name': 'true_bug'}, {'id': 3902, 'synset': 'heteropterous_insect.n.01', 'name': 'heteropterous_insect'}, {'id': 3903, 'synset': 'water_bug.n.01', 'name': 'water_bug'}, {'id': 3904, 'synset': 'giant_water_bug.n.01', 'name': 'giant_water_bug'}, {'id': 3905, 'synset': 'water_scorpion.n.01', 'name': 'water_scorpion'}, {'id': 3906, 'synset': 'water_boatman.n.01', 'name': 'water_boatman'}, {'id': 3907, 'synset': 'water_strider.n.01', 'name': 'water_strider'}, {'id': 3908, 'synset': 'common_pond-skater.n.01', 'name': 'common_pond-skater'}, {'id': 3909, 'synset': 'assassin_bug.n.01', 'name': 'assassin_bug'}, {'id': 3910, 'synset': 'conenose.n.01', 'name': 'conenose'}, {'id': 3911, 'synset': 'wheel_bug.n.01', 'name': 'wheel_bug'}, {'id': 3912, 'synset': 'firebug.n.02', 'name': 'firebug'}, {'id': 3913, 'synset': 'cotton_stainer.n.01', 'name': 'cotton_stainer'}, {'id': 3914, 'synset': 'homopterous_insect.n.01', 'name': 'homopterous_insect'}, {'id': 3915, 'synset': 'whitefly.n.01', 'name': 'whitefly'}, {'id': 3916, 'synset': 'citrus_whitefly.n.01', 'name': 'citrus_whitefly'}, {'id': 3917, 'synset': 'greenhouse_whitefly.n.01', 'name': 'greenhouse_whitefly'}, {'id': 3918, 'synset': 'sweet-potato_whitefly.n.01', 'name': 'sweet-potato_whitefly'}, {'id': 3919, 'synset': 'superbug.n.02', 'name': 'superbug'}, {'id': 3920, 'synset': 'cotton_strain.n.01', 'name': 'cotton_strain'}, {'id': 3921, 'synset': 'coccid_insect.n.01', 'name': 'coccid_insect'}, {'id': 3922, 'synset': 'scale_insect.n.01', 'name': 'scale_insect'}, {'id': 3923, 'synset': 'soft_scale.n.01', 'name': 'soft_scale'}, {'id': 3924, 'synset': 'brown_soft_scale.n.01', 'name': 'brown_soft_scale'}, {'id': 3925, 'synset': 'armored_scale.n.01', 'name': 'armored_scale'}, {'id': 3926, 'synset': 'san_jose_scale.n.01', 'name': 'San_Jose_scale'}, {'id': 3927, 'synset': 'cochineal_insect.n.01', 'name': 'cochineal_insect'}, {'id': 3928, 'synset': 'mealybug.n.01', 'name': 'mealybug'}, {'id': 3929, 'synset': 'citrophilous_mealybug.n.01', 'name': 'citrophilous_mealybug'}, {'id': 3930, 'synset': 'comstock_mealybug.n.01', 'name': 'Comstock_mealybug'}, {'id': 3931, 'synset': 'citrus_mealybug.n.01', 'name': 'citrus_mealybug'}, {'id': 3932, 'synset': 'plant_louse.n.01', 'name': 'plant_louse'}, {'id': 3933, 'synset': 'aphid.n.01', 'name': 'aphid'}, {'id': 3934, 'synset': 'apple_aphid.n.01', 'name': 'apple_aphid'}, {'id': 3935, 'synset': 'blackfly.n.01', 'name': 'blackfly'}, {'id': 3936, 'synset': 'greenfly.n.01', 'name': 'greenfly'}, {'id': 3937, 'synset': 'green_peach_aphid.n.01', 'name': 'green_peach_aphid'}, {'id': 3938, 'synset': 'ant_cow.n.01', 'name': 'ant_cow'}, {'id': 3939, 'synset': 'woolly_aphid.n.01', 'name': 'woolly_aphid'}, {'id': 3940, 'synset': 'woolly_apple_aphid.n.01', 'name': 'woolly_apple_aphid'}, {'id': 3941, 'synset': 'woolly_alder_aphid.n.01', 'name': 'woolly_alder_aphid'}, {'id': 3942, 'synset': 'adelgid.n.01', 'name': 'adelgid'}, {'id': 3943, 'synset': 'balsam_woolly_aphid.n.01', 'name': 'balsam_woolly_aphid'}, {'id': 3944, 'synset': 'spruce_gall_aphid.n.01', 'name': 'spruce_gall_aphid'}, {'id': 3945, 'synset': 'woolly_adelgid.n.01', 'name': 'woolly_adelgid'}, {'id': 3946, 'synset': 'jumping_plant_louse.n.01', 'name': 'jumping_plant_louse'}, {'id': 3947, 'synset': 'cicada.n.01', 'name': 'cicada'}, {'id': 3948, 'synset': 'dog-day_cicada.n.01', 'name': 'dog-day_cicada'}, {'id': 3949, 'synset': 'seventeen-year_locust.n.01', 'name': 'seventeen-year_locust'}, {'id': 3950, 'synset': 'spittle_insect.n.01', 'name': 'spittle_insect'}, {'id': 3951, 'synset': 'froghopper.n.01', 'name': 'froghopper'}, {'id': 3952, 'synset': 'meadow_spittlebug.n.01', 'name': 'meadow_spittlebug'}, {'id': 3953, 'synset': 'pine_spittlebug.n.01', 'name': 'pine_spittlebug'}, {'id': 3954, 'synset': 'saratoga_spittlebug.n.01', 'name': 'Saratoga_spittlebug'}, {'id': 3955, 'synset': 'leafhopper.n.01', 'name': 'leafhopper'}, {'id': 3956, 'synset': 'plant_hopper.n.01', 'name': 'plant_hopper'}, {'id': 3957, 'synset': 'treehopper.n.01', 'name': 'treehopper'}, {'id': 3958, 'synset': 'lantern_fly.n.01', 'name': 'lantern_fly'}, {'id': 3959, 'synset': 'psocopterous_insect.n.01', 'name': 'psocopterous_insect'}, {'id': 3960, 'synset': 'psocid.n.01', 'name': 'psocid'}, {'id': 3961, 'synset': 'bark-louse.n.01', 'name': 'bark-louse'}, {'id': 3962, 'synset': 'booklouse.n.01', 'name': 'booklouse'}, {'id': 3963, 'synset': 'common_booklouse.n.01', 'name': 'common_booklouse'}, {'id': 3964, 'synset': 'ephemerid.n.01', 'name': 'ephemerid'}, {'id': 3965, 'synset': 'mayfly.n.01', 'name': 'mayfly'}, {'id': 3966, 'synset': 'stonefly.n.01', 'name': 'stonefly'}, {'id': 3967, 'synset': 'neuropteron.n.01', 'name': 'neuropteron'}, {'id': 3968, 'synset': 'ant_lion.n.02', 'name': 'ant_lion'}, {'id': 3969, 'synset': 'doodlebug.n.03', 'name': 'doodlebug'}, {'id': 3970, 'synset': 'lacewing.n.01', 'name': 'lacewing'}, {'id': 3971, 'synset': 'aphid_lion.n.01', 'name': 'aphid_lion'}, {'id': 3972, 'synset': 'green_lacewing.n.01', 'name': 'green_lacewing'}, {'id': 3973, 'synset': 'brown_lacewing.n.01', 'name': 'brown_lacewing'}, {'id': 3974, 'synset': 'dobson.n.02', 'name': 'dobson'}, {'id': 3975, 'synset': 'hellgrammiate.n.01', 'name': 'hellgrammiate'}, {'id': 3976, 'synset': 'fish_fly.n.01', 'name': 'fish_fly'}, {'id': 3977, 'synset': 'alderfly.n.01', 'name': 'alderfly'}, {'id': 3978, 'synset': 'snakefly.n.01', 'name': 'snakefly'}, {'id': 3979, 'synset': 'mantispid.n.01', 'name': 'mantispid'}, {'id': 3980, 'synset': 'odonate.n.01', 'name': 'odonate'}, {'id': 3981, 'synset': 'damselfly.n.01', 'name': 'damselfly'}, {'id': 3982, 'synset': 'trichopterous_insect.n.01', 'name': 'trichopterous_insect'}, {'id': 3983, 'synset': 'caddis_fly.n.01', 'name': 'caddis_fly'}, {'id': 3984, 'synset': 'caseworm.n.01', 'name': 'caseworm'}, {'id': 3985, 'synset': 'caddisworm.n.01', 'name': 'caddisworm'}, {'id': 3986, 'synset': 'thysanuran_insect.n.01', 'name': 'thysanuran_insect'}, {'id': 3987, 'synset': 'bristletail.n.01', 'name': 'bristletail'}, {'id': 3988, 'synset': 'silverfish.n.01', 'name': 'silverfish'}, {'id': 3989, 'synset': 'firebrat.n.01', 'name': 'firebrat'}, {'id': 3990, 'synset': 'jumping_bristletail.n.01', 'name': 'jumping_bristletail'}, {'id': 3991, 'synset': 'thysanopter.n.01', 'name': 'thysanopter'}, {'id': 3992, 'synset': 'thrips.n.01', 'name': 'thrips'}, {'id': 3993, 'synset': 'tobacco_thrips.n.01', 'name': 'tobacco_thrips'}, {'id': 3994, 'synset': 'onion_thrips.n.01', 'name': 'onion_thrips'}, {'id': 3995, 'synset': 'earwig.n.01', 'name': 'earwig'}, {'id': 3996, 'synset': 'common_european_earwig.n.01', 'name': 'common_European_earwig'}, {'id': 3997, 'synset': 'lepidopterous_insect.n.01', 'name': 'lepidopterous_insect'}, {'id': 3998, 'synset': 'nymphalid.n.01', 'name': 'nymphalid'}, {'id': 3999, 'synset': 'mourning_cloak.n.01', 'name': 'mourning_cloak'}, {'id': 4000, 'synset': 'tortoiseshell.n.02', 'name': 'tortoiseshell'}, {'id': 4001, 'synset': 'painted_beauty.n.01', 'name': 'painted_beauty'}, {'id': 4002, 'synset': 'admiral.n.02', 'name': 'admiral'}, {'id': 4003, 'synset': 'red_admiral.n.01', 'name': 'red_admiral'}, {'id': 4004, 'synset': 'white_admiral.n.02', 'name': 'white_admiral'}, {'id': 4005, 'synset': 'banded_purple.n.01', 'name': 'banded_purple'}, {'id': 4006, 'synset': 'red-spotted_purple.n.01', 'name': 'red-spotted_purple'}, {'id': 4007, 'synset': 'viceroy.n.02', 'name': 'viceroy'}, {'id': 4008, 'synset': 'anglewing.n.01', 'name': 'anglewing'}, {'id': 4009, 'synset': 'ringlet.n.04', 'name': 'ringlet'}, {'id': 4010, 'synset': 'comma.n.02', 'name': 'comma'}, {'id': 4011, 'synset': 'fritillary.n.02', 'name': 'fritillary'}, {'id': 4012, 'synset': 'silverspot.n.01', 'name': 'silverspot'}, {'id': 4013, 'synset': 'emperor_butterfly.n.01', 'name': 'emperor_butterfly'}, {'id': 4014, 'synset': 'purple_emperor.n.01', 'name': 'purple_emperor'}, {'id': 4015, 'synset': 'peacock.n.01', 'name': 'peacock'}, {'id': 4016, 'synset': 'danaid.n.01', 'name': 'danaid'}, {'id': 4017, 'synset': 'monarch.n.02', 'name': 'monarch'}, {'id': 4018, 'synset': 'pierid.n.01', 'name': 'pierid'}, {'id': 4019, 'synset': 'cabbage_butterfly.n.01', 'name': 'cabbage_butterfly'}, {'id': 4020, 'synset': 'small_white.n.01', 'name': 'small_white'}, {'id': 4021, 'synset': 'large_white.n.01', 'name': 'large_white'}, {'id': 4022, 'synset': 'southern_cabbage_butterfly.n.01', 'name': 'southern_cabbage_butterfly'}, {'id': 4023, 'synset': 'sulphur_butterfly.n.01', 'name': 'sulphur_butterfly'}, {'id': 4024, 'synset': 'lycaenid.n.01', 'name': 'lycaenid'}, {'id': 4025, 'synset': 'blue.n.07', 'name': 'blue'}, {'id': 4026, 'synset': 'copper.n.05', 'name': 'copper'}, {'id': 4027, 'synset': 'american_copper.n.01', 'name': 'American_copper'}, {'id': 4028, 'synset': 'hairstreak.n.01', 'name': 'hairstreak'}, {'id': 4029, 'synset': 'strymon_melinus.n.01', 'name': 'Strymon_melinus'}, {'id': 4030, 'synset': 'moth.n.01', 'name': 'moth'}, {'id': 4031, 'synset': 'moth_miller.n.01', 'name': 'moth_miller'}, {'id': 4032, 'synset': 'tortricid.n.01', 'name': 'tortricid'}, {'id': 4033, 'synset': 'leaf_roller.n.01', 'name': 'leaf_roller'}, {'id': 4034, 'synset': 'tea_tortrix.n.01', 'name': 'tea_tortrix'}, {'id': 4035, 'synset': 'orange_tortrix.n.01', 'name': 'orange_tortrix'}, {'id': 4036, 'synset': 'codling_moth.n.01', 'name': 'codling_moth'}, {'id': 4037, 'synset': 'lymantriid.n.01', 'name': 'lymantriid'}, {'id': 4038, 'synset': 'tussock_caterpillar.n.01', 'name': 'tussock_caterpillar'}, {'id': 4039, 'synset': 'gypsy_moth.n.01', 'name': 'gypsy_moth'}, {'id': 4040, 'synset': 'browntail.n.01', 'name': 'browntail'}, {'id': 4041, 'synset': 'gold-tail_moth.n.01', 'name': 'gold-tail_moth'}, {'id': 4042, 'synset': 'geometrid.n.01', 'name': 'geometrid'}, {'id': 4043, 'synset': 'paleacrita_vernata.n.01', 'name': 'Paleacrita_vernata'}, {'id': 4044, 'synset': 'alsophila_pometaria.n.01', 'name': 'Alsophila_pometaria'}, {'id': 4045, 'synset': 'cankerworm.n.01', 'name': 'cankerworm'}, {'id': 4046, 'synset': 'spring_cankerworm.n.01', 'name': 'spring_cankerworm'}, {'id': 4047, 'synset': 'fall_cankerworm.n.01', 'name': 'fall_cankerworm'}, {'id': 4048, 'synset': 'measuring_worm.n.01', 'name': 'measuring_worm'}, {'id': 4049, 'synset': 'pyralid.n.01', 'name': 'pyralid'}, {'id': 4050, 'synset': 'bee_moth.n.01', 'name': 'bee_moth'}, {'id': 4051, 'synset': 'corn_borer.n.02', 'name': 'corn_borer'}, {'id': 4052, 'synset': 'mediterranean_flour_moth.n.01', 'name': 'Mediterranean_flour_moth'}, {'id': 4053, 'synset': 'tobacco_moth.n.01', 'name': 'tobacco_moth'}, {'id': 4054, 'synset': 'almond_moth.n.01', 'name': 'almond_moth'}, {'id': 4055, 'synset': 'raisin_moth.n.01', 'name': 'raisin_moth'}, {'id': 4056, 'synset': 'tineoid.n.01', 'name': 'tineoid'}, {'id': 4057, 'synset': 'tineid.n.01', 'name': 'tineid'}, {'id': 4058, 'synset': 'clothes_moth.n.01', 'name': 'clothes_moth'}, {'id': 4059, 'synset': 'casemaking_clothes_moth.n.01', 'name': 'casemaking_clothes_moth'}, {'id': 4060, 'synset': 'webbing_clothes_moth.n.01', 'name': 'webbing_clothes_moth'}, {'id': 4061, 'synset': 'carpet_moth.n.01', 'name': 'carpet_moth'}, {'id': 4062, 'synset': 'gelechiid.n.01', 'name': 'gelechiid'}, {'id': 4063, 'synset': 'grain_moth.n.01', 'name': 'grain_moth'}, {'id': 4064, 'synset': 'angoumois_moth.n.01', 'name': 'angoumois_moth'}, {'id': 4065, 'synset': 'potato_moth.n.01', 'name': 'potato_moth'}, {'id': 4066, 'synset': 'potato_tuberworm.n.01', 'name': 'potato_tuberworm'}, {'id': 4067, 'synset': 'noctuid_moth.n.01', 'name': 'noctuid_moth'}, {'id': 4068, 'synset': 'cutworm.n.01', 'name': 'cutworm'}, {'id': 4069, 'synset': 'underwing.n.01', 'name': 'underwing'}, {'id': 4070, 'synset': 'red_underwing.n.01', 'name': 'red_underwing'}, {'id': 4071, 'synset': 'antler_moth.n.01', 'name': 'antler_moth'}, {'id': 4072, 'synset': 'heliothis_moth.n.01', 'name': 'heliothis_moth'}, {'id': 4073, 'synset': 'army_cutworm.n.01', 'name': 'army_cutworm'}, {'id': 4074, 'synset': 'armyworm.n.02', 'name': 'armyworm'}, {'id': 4075, 'synset': 'armyworm.n.01', 'name': 'armyworm'}, {'id': 4076, 'synset': 'spodoptera_exigua.n.02', 'name': 'Spodoptera_exigua'}, {'id': 4077, 'synset': 'beet_armyworm.n.01', 'name': 'beet_armyworm'}, {'id': 4078, 'synset': 'spodoptera_frugiperda.n.02', 'name': 'Spodoptera_frugiperda'}, {'id': 4079, 'synset': 'fall_armyworm.n.01', 'name': 'fall_armyworm'}, {'id': 4080, 'synset': 'hawkmoth.n.01', 'name': 'hawkmoth'}, {'id': 4081, 'synset': 'manduca_sexta.n.02', 'name': 'Manduca_sexta'}, {'id': 4082, 'synset': 'tobacco_hornworm.n.01', 'name': 'tobacco_hornworm'}, {'id': 4083, 'synset': 'manduca_quinquemaculata.n.02', 'name': 'Manduca_quinquemaculata'}, {'id': 4084, 'synset': 'tomato_hornworm.n.01', 'name': 'tomato_hornworm'}, {'id': 4085, 'synset': "death's-head_moth.n.01", 'name': "death's-head_moth"}, {'id': 4086, 'synset': 'bombycid.n.01', 'name': 'bombycid'}, {'id': 4087, 'synset': 'domestic_silkworm_moth.n.01', 'name': 'domestic_silkworm_moth'}, {'id': 4088, 'synset': 'silkworm.n.01', 'name': 'silkworm'}, {'id': 4089, 'synset': 'saturniid.n.01', 'name': 'saturniid'}, {'id': 4090, 'synset': 'emperor.n.03', 'name': 'emperor'}, {'id': 4091, 'synset': 'imperial_moth.n.01', 'name': 'imperial_moth'}, {'id': 4092, 'synset': 'giant_silkworm_moth.n.01', 'name': 'giant_silkworm_moth'}, {'id': 4093, 'synset': 'silkworm.n.02', 'name': 'silkworm'}, {'id': 4094, 'synset': 'luna_moth.n.01', 'name': 'luna_moth'}, {'id': 4095, 'synset': 'cecropia.n.02', 'name': 'cecropia'}, {'id': 4096, 'synset': 'cynthia_moth.n.01', 'name': 'cynthia_moth'}, {'id': 4097, 'synset': 'ailanthus_silkworm.n.01', 'name': 'ailanthus_silkworm'}, {'id': 4098, 'synset': 'io_moth.n.01', 'name': 'io_moth'}, {'id': 4099, 'synset': 'polyphemus_moth.n.01', 'name': 'polyphemus_moth'}, {'id': 4100, 'synset': 'pernyi_moth.n.01', 'name': 'pernyi_moth'}, {'id': 4101, 'synset': 'tussah.n.01', 'name': 'tussah'}, {'id': 4102, 'synset': 'atlas_moth.n.01', 'name': 'atlas_moth'}, {'id': 4103, 'synset': 'arctiid.n.01', 'name': 'arctiid'}, {'id': 4104, 'synset': 'tiger_moth.n.01', 'name': 'tiger_moth'}, {'id': 4105, 'synset': 'cinnabar.n.02', 'name': 'cinnabar'}, {'id': 4106, 'synset': 'lasiocampid.n.01', 'name': 'lasiocampid'}, {'id': 4107, 'synset': 'eggar.n.01', 'name': 'eggar'}, {'id': 4108, 'synset': 'tent-caterpillar_moth.n.02', 'name': 'tent-caterpillar_moth'}, {'id': 4109, 'synset': 'tent_caterpillar.n.01', 'name': 'tent_caterpillar'}, {'id': 4110, 'synset': 'tent-caterpillar_moth.n.01', 'name': 'tent-caterpillar_moth'}, {'id': 4111, 'synset': 'forest_tent_caterpillar.n.01', 'name': 'forest_tent_caterpillar'}, {'id': 4112, 'synset': 'lappet.n.03', 'name': 'lappet'}, {'id': 4113, 'synset': 'lappet_caterpillar.n.01', 'name': 'lappet_caterpillar'}, {'id': 4114, 'synset': 'webworm.n.01', 'name': 'webworm'}, {'id': 4115, 'synset': 'webworm_moth.n.01', 'name': 'webworm_moth'}, {'id': 4116, 'synset': 'hyphantria_cunea.n.02', 'name': 'Hyphantria_cunea'}, {'id': 4117, 'synset': 'fall_webworm.n.01', 'name': 'fall_webworm'}, {'id': 4118, 'synset': 'garden_webworm.n.01', 'name': 'garden_webworm'}, {'id': 4119, 'synset': 'instar.n.01', 'name': 'instar'}, {'id': 4120, 'synset': 'caterpillar.n.01', 'name': 'caterpillar'}, {'id': 4121, 'synset': 'corn_borer.n.01', 'name': 'corn_borer'}, {'id': 4122, 'synset': 'bollworm.n.01', 'name': 'bollworm'}, {'id': 4123, 'synset': 'pink_bollworm.n.01', 'name': 'pink_bollworm'}, {'id': 4124, 'synset': 'corn_earworm.n.01', 'name': 'corn_earworm'}, {'id': 4125, 'synset': 'cabbageworm.n.01', 'name': 'cabbageworm'}, {'id': 4126, 'synset': 'woolly_bear.n.01', 'name': 'woolly_bear'}, {'id': 4127, 'synset': 'woolly_bear_moth.n.01', 'name': 'woolly_bear_moth'}, {'id': 4128, 'synset': 'larva.n.01', 'name': 'larva'}, {'id': 4129, 'synset': 'nymph.n.02', 'name': 'nymph'}, {'id': 4130, 'synset': 'leptocephalus.n.01', 'name': 'leptocephalus'}, {'id': 4131, 'synset': 'grub.n.02', 'name': 'grub'}, {'id': 4132, 'synset': 'maggot.n.01', 'name': 'maggot'}, {'id': 4133, 'synset': 'leatherjacket.n.03', 'name': 'leatherjacket'}, {'id': 4134, 'synset': 'pupa.n.01', 'name': 'pupa'}, {'id': 4135, 'synset': 'chrysalis.n.01', 'name': 'chrysalis'}, {'id': 4136, 'synset': 'imago.n.02', 'name': 'imago'}, {'id': 4137, 'synset': 'queen.n.01', 'name': 'queen'}, {'id': 4138, 'synset': 'phoronid.n.01', 'name': 'phoronid'}, {'id': 4139, 'synset': 'bryozoan.n.01', 'name': 'bryozoan'}, {'id': 4140, 'synset': 'brachiopod.n.01', 'name': 'brachiopod'}, {'id': 4141, 'synset': 'peanut_worm.n.01', 'name': 'peanut_worm'}, {'id': 4142, 'synset': 'echinoderm.n.01', 'name': 'echinoderm'}, {'id': 4143, 'synset': 'brittle_star.n.01', 'name': 'brittle_star'}, {'id': 4144, 'synset': 'basket_star.n.01', 'name': 'basket_star'}, {'id': 4145, 'synset': 'astrophyton_muricatum.n.01', 'name': 'Astrophyton_muricatum'}, {'id': 4146, 'synset': 'sea_urchin.n.01', 'name': 'sea_urchin'}, {'id': 4147, 'synset': 'edible_sea_urchin.n.01', 'name': 'edible_sea_urchin'}, {'id': 4148, 'synset': 'sand_dollar.n.01', 'name': 'sand_dollar'}, {'id': 4149, 'synset': 'heart_urchin.n.01', 'name': 'heart_urchin'}, {'id': 4150, 'synset': 'crinoid.n.01', 'name': 'crinoid'}, {'id': 4151, 'synset': 'sea_lily.n.01', 'name': 'sea_lily'}, {'id': 4152, 'synset': 'feather_star.n.01', 'name': 'feather_star'}, {'id': 4153, 'synset': 'sea_cucumber.n.01', 'name': 'sea_cucumber'}, {'id': 4154, 'synset': 'trepang.n.01', 'name': 'trepang'}, {'id': 4155, 'synset': 'duplicidentata.n.01', 'name': 'Duplicidentata'}, {'id': 4156, 'synset': 'lagomorph.n.01', 'name': 'lagomorph'}, {'id': 4157, 'synset': 'leporid.n.01', 'name': 'leporid'}, {'id': 4158, 'synset': 'rabbit_ears.n.02', 'name': 'rabbit_ears'}, {'id': 4159, 'synset': 'lapin.n.02', 'name': 'lapin'}, {'id': 4160, 'synset': 'bunny.n.02', 'name': 'bunny'}, {'id': 4161, 'synset': 'european_rabbit.n.01', 'name': 'European_rabbit'}, {'id': 4162, 'synset': 'wood_rabbit.n.01', 'name': 'wood_rabbit'}, {'id': 4163, 'synset': 'eastern_cottontail.n.01', 'name': 'eastern_cottontail'}, {'id': 4164, 'synset': 'swamp_rabbit.n.02', 'name': 'swamp_rabbit'}, {'id': 4165, 'synset': 'marsh_hare.n.01', 'name': 'marsh_hare'}, {'id': 4166, 'synset': 'hare.n.01', 'name': 'hare'}, {'id': 4167, 'synset': 'leveret.n.01', 'name': 'leveret'}, {'id': 4168, 'synset': 'european_hare.n.01', 'name': 'European_hare'}, {'id': 4169, 'synset': 'jackrabbit.n.01', 'name': 'jackrabbit'}, {'id': 4170, 'synset': 'white-tailed_jackrabbit.n.01', 'name': 'white-tailed_jackrabbit'}, {'id': 4171, 'synset': 'blacktail_jackrabbit.n.01', 'name': 'blacktail_jackrabbit'}, {'id': 4172, 'synset': 'polar_hare.n.01', 'name': 'polar_hare'}, {'id': 4173, 'synset': 'snowshoe_hare.n.01', 'name': 'snowshoe_hare'}, {'id': 4174, 'synset': 'belgian_hare.n.01', 'name': 'Belgian_hare'}, {'id': 4175, 'synset': 'angora.n.03', 'name': 'Angora'}, {'id': 4176, 'synset': 'pika.n.01', 'name': 'pika'}, {'id': 4177, 'synset': 'little_chief_hare.n.01', 'name': 'little_chief_hare'}, {'id': 4178, 'synset': 'collared_pika.n.01', 'name': 'collared_pika'}, {'id': 4179, 'synset': 'mouse.n.01', 'name': 'mouse'}, {'id': 4180, 'synset': 'pocket_rat.n.01', 'name': 'pocket_rat'}, {'id': 4181, 'synset': 'murine.n.01', 'name': 'murine'}, {'id': 4182, 'synset': 'house_mouse.n.01', 'name': 'house_mouse'}, {'id': 4183, 'synset': 'harvest_mouse.n.02', 'name': 'harvest_mouse'}, {'id': 4184, 'synset': 'field_mouse.n.02', 'name': 'field_mouse'}, {'id': 4185, 'synset': 'nude_mouse.n.01', 'name': 'nude_mouse'}, {'id': 4186, 'synset': 'european_wood_mouse.n.01', 'name': 'European_wood_mouse'}, {'id': 4187, 'synset': 'brown_rat.n.01', 'name': 'brown_rat'}, {'id': 4188, 'synset': 'wharf_rat.n.02', 'name': 'wharf_rat'}, {'id': 4189, 'synset': 'sewer_rat.n.01', 'name': 'sewer_rat'}, {'id': 4190, 'synset': 'black_rat.n.01', 'name': 'black_rat'}, {'id': 4191, 'synset': 'bandicoot_rat.n.01', 'name': 'bandicoot_rat'}, {'id': 4192, 'synset': 'jerboa_rat.n.01', 'name': 'jerboa_rat'}, {'id': 4193, 'synset': 'kangaroo_mouse.n.02', 'name': 'kangaroo_mouse'}, {'id': 4194, 'synset': 'water_rat.n.03', 'name': 'water_rat'}, {'id': 4195, 'synset': 'beaver_rat.n.01', 'name': 'beaver_rat'}, {'id': 4196, 'synset': 'new_world_mouse.n.01', 'name': 'New_World_mouse'}, {'id': 4197, 'synset': 'american_harvest_mouse.n.01', 'name': 'American_harvest_mouse'}, {'id': 4198, 'synset': 'wood_mouse.n.01', 'name': 'wood_mouse'}, {'id': 4199, 'synset': 'white-footed_mouse.n.01', 'name': 'white-footed_mouse'}, {'id': 4200, 'synset': 'deer_mouse.n.01', 'name': 'deer_mouse'}, {'id': 4201, 'synset': 'cactus_mouse.n.01', 'name': 'cactus_mouse'}, {'id': 4202, 'synset': 'cotton_mouse.n.01', 'name': 'cotton_mouse'}, {'id': 4203, 'synset': 'pygmy_mouse.n.01', 'name': 'pygmy_mouse'}, {'id': 4204, 'synset': 'grasshopper_mouse.n.01', 'name': 'grasshopper_mouse'}, {'id': 4205, 'synset': 'muskrat.n.02', 'name': 'muskrat'}, {'id': 4206, 'synset': 'round-tailed_muskrat.n.01', 'name': 'round-tailed_muskrat'}, {'id': 4207, 'synset': 'cotton_rat.n.01', 'name': 'cotton_rat'}, {'id': 4208, 'synset': 'wood_rat.n.01', 'name': 'wood_rat'}, {'id': 4209, 'synset': 'dusky-footed_wood_rat.n.01', 'name': 'dusky-footed_wood_rat'}, {'id': 4210, 'synset': 'vole.n.01', 'name': 'vole'}, {'id': 4211, 'synset': 'packrat.n.02', 'name': 'packrat'}, {'id': 4212, 'synset': 'dusky-footed_woodrat.n.01', 'name': 'dusky-footed_woodrat'}, {'id': 4213, 'synset': 'eastern_woodrat.n.01', 'name': 'eastern_woodrat'}, {'id': 4214, 'synset': 'rice_rat.n.01', 'name': 'rice_rat'}, {'id': 4215, 'synset': 'pine_vole.n.01', 'name': 'pine_vole'}, {'id': 4216, 'synset': 'meadow_vole.n.01', 'name': 'meadow_vole'}, {'id': 4217, 'synset': 'water_vole.n.02', 'name': 'water_vole'}, {'id': 4218, 'synset': 'prairie_vole.n.01', 'name': 'prairie_vole'}, {'id': 4219, 'synset': 'water_vole.n.01', 'name': 'water_vole'}, {'id': 4220, 'synset': 'red-backed_mouse.n.01', 'name': 'red-backed_mouse'}, {'id': 4221, 'synset': 'phenacomys.n.01', 'name': 'phenacomys'}, {'id': 4222, 'synset': 'eurasian_hamster.n.01', 'name': 'Eurasian_hamster'}, {'id': 4223, 'synset': 'golden_hamster.n.01', 'name': 'golden_hamster'}, {'id': 4224, 'synset': 'gerbil.n.01', 'name': 'gerbil'}, {'id': 4225, 'synset': 'jird.n.01', 'name': 'jird'}, {'id': 4226, 'synset': 'tamarisk_gerbil.n.01', 'name': 'tamarisk_gerbil'}, {'id': 4227, 'synset': 'sand_rat.n.02', 'name': 'sand_rat'}, {'id': 4228, 'synset': 'lemming.n.01', 'name': 'lemming'}, {'id': 4229, 'synset': 'european_lemming.n.01', 'name': 'European_lemming'}, {'id': 4230, 'synset': 'brown_lemming.n.01', 'name': 'brown_lemming'}, {'id': 4231, 'synset': 'grey_lemming.n.01', 'name': 'grey_lemming'}, {'id': 4232, 'synset': 'pied_lemming.n.01', 'name': 'pied_lemming'}, {'id': 4233, 'synset': 'hudson_bay_collared_lemming.n.01', 'name': 'Hudson_bay_collared_lemming'}, {'id': 4234, 'synset': 'southern_bog_lemming.n.01', 'name': 'southern_bog_lemming'}, {'id': 4235, 'synset': 'northern_bog_lemming.n.01', 'name': 'northern_bog_lemming'}, {'id': 4236, 'synset': 'porcupine.n.01', 'name': 'porcupine'}, {'id': 4237, 'synset': 'old_world_porcupine.n.01', 'name': 'Old_World_porcupine'}, {'id': 4238, 'synset': 'brush-tailed_porcupine.n.01', 'name': 'brush-tailed_porcupine'}, {'id': 4239, 'synset': 'long-tailed_porcupine.n.01', 'name': 'long-tailed_porcupine'}, {'id': 4240, 'synset': 'new_world_porcupine.n.01', 'name': 'New_World_porcupine'}, {'id': 4241, 'synset': 'canada_porcupine.n.01', 'name': 'Canada_porcupine'}, {'id': 4242, 'synset': 'pocket_mouse.n.01', 'name': 'pocket_mouse'}, {'id': 4243, 'synset': 'silky_pocket_mouse.n.01', 'name': 'silky_pocket_mouse'}, {'id': 4244, 'synset': 'plains_pocket_mouse.n.01', 'name': 'plains_pocket_mouse'}, {'id': 4245, 'synset': 'hispid_pocket_mouse.n.01', 'name': 'hispid_pocket_mouse'}, {'id': 4246, 'synset': 'mexican_pocket_mouse.n.01', 'name': 'Mexican_pocket_mouse'}, {'id': 4247, 'synset': 'kangaroo_rat.n.01', 'name': 'kangaroo_rat'}, {'id': 4248, 'synset': 'ord_kangaroo_rat.n.01', 'name': 'Ord_kangaroo_rat'}, {'id': 4249, 'synset': 'kangaroo_mouse.n.01', 'name': 'kangaroo_mouse'}, {'id': 4250, 'synset': 'jumping_mouse.n.01', 'name': 'jumping_mouse'}, {'id': 4251, 'synset': 'meadow_jumping_mouse.n.01', 'name': 'meadow_jumping_mouse'}, {'id': 4252, 'synset': 'jerboa.n.01', 'name': 'jerboa'}, {'id': 4253, 'synset': 'typical_jerboa.n.01', 'name': 'typical_jerboa'}, {'id': 4254, 'synset': 'jaculus_jaculus.n.01', 'name': 'Jaculus_jaculus'}, {'id': 4255, 'synset': 'dormouse.n.01', 'name': 'dormouse'}, {'id': 4256, 'synset': 'loir.n.01', 'name': 'loir'}, {'id': 4257, 'synset': 'hazel_mouse.n.01', 'name': 'hazel_mouse'}, {'id': 4258, 'synset': 'lerot.n.01', 'name': 'lerot'}, {'id': 4259, 'synset': 'gopher.n.04', 'name': 'gopher'}, {'id': 4260, 'synset': 'plains_pocket_gopher.n.01', 'name': 'plains_pocket_gopher'}, {'id': 4261, 'synset': 'southeastern_pocket_gopher.n.01', 'name': 'southeastern_pocket_gopher'}, {'id': 4262, 'synset': 'valley_pocket_gopher.n.01', 'name': 'valley_pocket_gopher'}, {'id': 4263, 'synset': 'northern_pocket_gopher.n.01', 'name': 'northern_pocket_gopher'}, {'id': 4264, 'synset': 'tree_squirrel.n.01', 'name': 'tree_squirrel'}, {'id': 4265, 'synset': 'eastern_grey_squirrel.n.01', 'name': 'eastern_grey_squirrel'}, {'id': 4266, 'synset': 'western_grey_squirrel.n.01', 'name': 'western_grey_squirrel'}, {'id': 4267, 'synset': 'fox_squirrel.n.01', 'name': 'fox_squirrel'}, {'id': 4268, 'synset': 'black_squirrel.n.01', 'name': 'black_squirrel'}, {'id': 4269, 'synset': 'red_squirrel.n.02', 'name': 'red_squirrel'}, {'id': 4270, 'synset': 'american_red_squirrel.n.01', 'name': 'American_red_squirrel'}, {'id': 4271, 'synset': 'chickeree.n.01', 'name': 'chickeree'}, {'id': 4272, 'synset': 'antelope_squirrel.n.01', 'name': 'antelope_squirrel'}, {'id': 4273, 'synset': 'ground_squirrel.n.02', 'name': 'ground_squirrel'}, {'id': 4274, 'synset': 'mantled_ground_squirrel.n.01', 'name': 'mantled_ground_squirrel'}, {'id': 4275, 'synset': 'suslik.n.01', 'name': 'suslik'}, {'id': 4276, 'synset': 'flickertail.n.01', 'name': 'flickertail'}, {'id': 4277, 'synset': 'rock_squirrel.n.01', 'name': 'rock_squirrel'}, {'id': 4278, 'synset': 'arctic_ground_squirrel.n.01', 'name': 'Arctic_ground_squirrel'}, {'id': 4279, 'synset': 'prairie_dog.n.01', 'name': 'prairie_dog'}, {'id': 4280, 'synset': 'blacktail_prairie_dog.n.01', 'name': 'blacktail_prairie_dog'}, {'id': 4281, 'synset': 'whitetail_prairie_dog.n.01', 'name': 'whitetail_prairie_dog'}, {'id': 4282, 'synset': 'eastern_chipmunk.n.01', 'name': 'eastern_chipmunk'}, {'id': 4283, 'synset': 'chipmunk.n.01', 'name': 'chipmunk'}, {'id': 4284, 'synset': 'baronduki.n.01', 'name': 'baronduki'}, {'id': 4285, 'synset': 'american_flying_squirrel.n.01', 'name': 'American_flying_squirrel'}, {'id': 4286, 'synset': 'southern_flying_squirrel.n.01', 'name': 'southern_flying_squirrel'}, {'id': 4287, 'synset': 'northern_flying_squirrel.n.01', 'name': 'northern_flying_squirrel'}, {'id': 4288, 'synset': 'marmot.n.01', 'name': 'marmot'}, {'id': 4289, 'synset': 'groundhog.n.01', 'name': 'groundhog'}, {'id': 4290, 'synset': 'hoary_marmot.n.01', 'name': 'hoary_marmot'}, {'id': 4291, 'synset': 'yellowbelly_marmot.n.01', 'name': 'yellowbelly_marmot'}, {'id': 4292, 'synset': 'asiatic_flying_squirrel.n.01', 'name': 'Asiatic_flying_squirrel'}, {'id': 4293, 'synset': 'beaver.n.07', 'name': 'beaver'}, {'id': 4294, 'synset': 'old_world_beaver.n.01', 'name': 'Old_World_beaver'}, {'id': 4295, 'synset': 'new_world_beaver.n.01', 'name': 'New_World_beaver'}, {'id': 4296, 'synset': 'mountain_beaver.n.01', 'name': 'mountain_beaver'}, {'id': 4297, 'synset': 'cavy.n.01', 'name': 'cavy'}, {'id': 4298, 'synset': 'guinea_pig.n.02', 'name': 'guinea_pig'}, {'id': 4299, 'synset': 'aperea.n.01', 'name': 'aperea'}, {'id': 4300, 'synset': 'mara.n.02', 'name': 'mara'}, {'id': 4301, 'synset': 'capybara.n.01', 'name': 'capybara'}, {'id': 4302, 'synset': 'agouti.n.01', 'name': 'agouti'}, {'id': 4303, 'synset': 'paca.n.01', 'name': 'paca'}, {'id': 4304, 'synset': 'mountain_paca.n.01', 'name': 'mountain_paca'}, {'id': 4305, 'synset': 'coypu.n.01', 'name': 'coypu'}, {'id': 4306, 'synset': 'chinchilla.n.03', 'name': 'chinchilla'}, {'id': 4307, 'synset': 'mountain_chinchilla.n.01', 'name': 'mountain_chinchilla'}, {'id': 4308, 'synset': 'viscacha.n.01', 'name': 'viscacha'}, {'id': 4309, 'synset': 'abrocome.n.01', 'name': 'abrocome'}, {'id': 4310, 'synset': 'mole_rat.n.02', 'name': 'mole_rat'}, {'id': 4311, 'synset': 'mole_rat.n.01', 'name': 'mole_rat'}, {'id': 4312, 'synset': 'sand_rat.n.01', 'name': 'sand_rat'}, {'id': 4313, 'synset': 'naked_mole_rat.n.01', 'name': 'naked_mole_rat'}, {'id': 4314, 'synset': 'queen.n.09', 'name': 'queen'}, {'id': 4315, 'synset': 'damaraland_mole_rat.n.01', 'name': 'Damaraland_mole_rat'}, {'id': 4316, 'synset': 'ungulata.n.01', 'name': 'Ungulata'}, {'id': 4317, 'synset': 'ungulate.n.01', 'name': 'ungulate'}, {'id': 4318, 'synset': 'unguiculate.n.01', 'name': 'unguiculate'}, {'id': 4319, 'synset': 'dinoceras.n.01', 'name': 'dinoceras'}, {'id': 4320, 'synset': 'hyrax.n.01', 'name': 'hyrax'}, {'id': 4321, 'synset': 'rock_hyrax.n.01', 'name': 'rock_hyrax'}, {'id': 4322, 'synset': 'odd-toed_ungulate.n.01', 'name': 'odd-toed_ungulate'}, {'id': 4323, 'synset': 'equine.n.01', 'name': 'equine'}, {'id': 4324, 'synset': 'roan.n.02', 'name': 'roan'}, {'id': 4325, 'synset': 'stablemate.n.01', 'name': 'stablemate'}, {'id': 4326, 'synset': 'gee-gee.n.01', 'name': 'gee-gee'}, {'id': 4327, 'synset': 'eohippus.n.01', 'name': 'eohippus'}, {'id': 4328, 'synset': 'filly.n.01', 'name': 'filly'}, {'id': 4329, 'synset': 'colt.n.01', 'name': 'colt'}, {'id': 4330, 'synset': 'male_horse.n.01', 'name': 'male_horse'}, {'id': 4331, 'synset': 'ridgeling.n.01', 'name': 'ridgeling'}, {'id': 4332, 'synset': 'stallion.n.01', 'name': 'stallion'}, {'id': 4333, 'synset': 'stud.n.04', 'name': 'stud'}, {'id': 4334, 'synset': 'gelding.n.01', 'name': 'gelding'}, {'id': 4335, 'synset': 'mare.n.01', 'name': 'mare'}, {'id': 4336, 'synset': 'broodmare.n.01', 'name': 'broodmare'}, {'id': 4337, 'synset': 'saddle_horse.n.01', 'name': 'saddle_horse'}, {'id': 4338, 'synset': 'remount.n.01', 'name': 'remount'}, {'id': 4339, 'synset': 'palfrey.n.01', 'name': 'palfrey'}, {'id': 4340, 'synset': 'warhorse.n.03', 'name': 'warhorse'}, {'id': 4341, 'synset': 'cavalry_horse.n.01', 'name': 'cavalry_horse'}, {'id': 4342, 'synset': 'charger.n.01', 'name': 'charger'}, {'id': 4343, 'synset': 'steed.n.01', 'name': 'steed'}, {'id': 4344, 'synset': 'prancer.n.01', 'name': 'prancer'}, {'id': 4345, 'synset': 'hack.n.08', 'name': 'hack'}, {'id': 4346, 'synset': 'cow_pony.n.01', 'name': 'cow_pony'}, {'id': 4347, 'synset': 'quarter_horse.n.01', 'name': 'quarter_horse'}, {'id': 4348, 'synset': 'morgan.n.06', 'name': 'Morgan'}, {'id': 4349, 'synset': 'tennessee_walker.n.01', 'name': 'Tennessee_walker'}, {'id': 4350, 'synset': 'american_saddle_horse.n.01', 'name': 'American_saddle_horse'}, {'id': 4351, 'synset': 'appaloosa.n.01', 'name': 'Appaloosa'}, {'id': 4352, 'synset': 'arabian.n.02', 'name': 'Arabian'}, {'id': 4353, 'synset': 'lippizan.n.01', 'name': 'Lippizan'}, {'id': 4354, 'synset': 'pony.n.01', 'name': 'pony'}, {'id': 4355, 'synset': 'polo_pony.n.01', 'name': 'polo_pony'}, {'id': 4356, 'synset': 'mustang.n.01', 'name': 'mustang'}, {'id': 4357, 'synset': 'bronco.n.01', 'name': 'bronco'}, {'id': 4358, 'synset': 'bucking_bronco.n.01', 'name': 'bucking_bronco'}, {'id': 4359, 'synset': 'buckskin.n.01', 'name': 'buckskin'}, {'id': 4360, 'synset': 'crowbait.n.01', 'name': 'crowbait'}, {'id': 4361, 'synset': 'dun.n.01', 'name': 'dun'}, {'id': 4362, 'synset': 'grey.n.07', 'name': 'grey'}, {'id': 4363, 'synset': 'wild_horse.n.01', 'name': 'wild_horse'}, {'id': 4364, 'synset': 'tarpan.n.01', 'name': 'tarpan'}, {'id': 4365, 'synset': "przewalski's_horse.n.01", 'name': "Przewalski's_horse"}, {'id': 4366, 'synset': 'cayuse.n.01', 'name': 'cayuse'}, {'id': 4367, 'synset': 'hack.n.07', 'name': 'hack'}, {'id': 4368, 'synset': 'hack.n.06', 'name': 'hack'}, {'id': 4369, 'synset': 'plow_horse.n.01', 'name': 'plow_horse'}, {'id': 4370, 'synset': 'shetland_pony.n.01', 'name': 'Shetland_pony'}, {'id': 4371, 'synset': 'welsh_pony.n.01', 'name': 'Welsh_pony'}, {'id': 4372, 'synset': 'exmoor.n.02', 'name': 'Exmoor'}, {'id': 4373, 'synset': 'racehorse.n.01', 'name': 'racehorse'}, {'id': 4374, 'synset': 'thoroughbred.n.02', 'name': 'thoroughbred'}, {'id': 4375, 'synset': 'steeplechaser.n.01', 'name': 'steeplechaser'}, {'id': 4376, 'synset': 'racer.n.03', 'name': 'racer'}, {'id': 4377, 'synset': 'finisher.n.06', 'name': 'finisher'}, {'id': 4378, 'synset': 'pony.n.02', 'name': 'pony'}, {'id': 4379, 'synset': 'yearling.n.02', 'name': 'yearling'}, {'id': 4380, 'synset': 'dark_horse.n.02', 'name': 'dark_horse'}, {'id': 4381, 'synset': 'mudder.n.01', 'name': 'mudder'}, {'id': 4382, 'synset': 'nonstarter.n.02', 'name': 'nonstarter'}, {'id': 4383, 'synset': 'stalking-horse.n.04', 'name': 'stalking-horse'}, {'id': 4384, 'synset': 'harness_horse.n.01', 'name': 'harness_horse'}, {'id': 4385, 'synset': 'cob.n.02', 'name': 'cob'}, {'id': 4386, 'synset': 'hackney.n.02', 'name': 'hackney'}, {'id': 4387, 'synset': 'workhorse.n.02', 'name': 'workhorse'}, {'id': 4388, 'synset': 'draft_horse.n.01', 'name': 'draft_horse'}, {'id': 4389, 'synset': 'packhorse.n.01', 'name': 'packhorse'}, {'id': 4390, 'synset': 'carthorse.n.01', 'name': 'carthorse'}, {'id': 4391, 'synset': 'clydesdale.n.01', 'name': 'Clydesdale'}, {'id': 4392, 'synset': 'percheron.n.01', 'name': 'Percheron'}, {'id': 4393, 'synset': 'farm_horse.n.01', 'name': 'farm_horse'}, {'id': 4394, 'synset': 'shire.n.02', 'name': 'shire'}, {'id': 4395, 'synset': 'pole_horse.n.02', 'name': 'pole_horse'}, {'id': 4396, 'synset': 'post_horse.n.01', 'name': 'post_horse'}, {'id': 4397, 'synset': 'coach_horse.n.01', 'name': 'coach_horse'}, {'id': 4398, 'synset': 'pacer.n.02', 'name': 'pacer'}, {'id': 4399, 'synset': 'pacer.n.01', 'name': 'pacer'}, {'id': 4400, 'synset': 'trotting_horse.n.01', 'name': 'trotting_horse'}, {'id': 4401, 'synset': 'pole_horse.n.01', 'name': 'pole_horse'}, {'id': 4402, 'synset': 'stepper.n.03', 'name': 'stepper'}, {'id': 4403, 'synset': 'chestnut.n.06', 'name': 'chestnut'}, {'id': 4404, 'synset': 'liver_chestnut.n.01', 'name': 'liver_chestnut'}, {'id': 4405, 'synset': 'bay.n.07', 'name': 'bay'}, {'id': 4406, 'synset': 'sorrel.n.05', 'name': 'sorrel'}, {'id': 4407, 'synset': 'palomino.n.01', 'name': 'palomino'}, {'id': 4408, 'synset': 'pinto.n.01', 'name': 'pinto'}, {'id': 4409, 'synset': 'ass.n.03', 'name': 'ass'}, {'id': 4410, 'synset': 'burro.n.01', 'name': 'burro'}, {'id': 4411, 'synset': 'moke.n.01', 'name': 'moke'}, {'id': 4412, 'synset': 'jack.n.12', 'name': 'jack'}, {'id': 4413, 'synset': 'jennet.n.01', 'name': 'jennet'}, {'id': 4414, 'synset': 'mule.n.01', 'name': 'mule'}, {'id': 4415, 'synset': 'hinny.n.01', 'name': 'hinny'}, {'id': 4416, 'synset': 'wild_ass.n.01', 'name': 'wild_ass'}, {'id': 4417, 'synset': 'african_wild_ass.n.01', 'name': 'African_wild_ass'}, {'id': 4418, 'synset': 'kiang.n.01', 'name': 'kiang'}, {'id': 4419, 'synset': 'onager.n.02', 'name': 'onager'}, {'id': 4420, 'synset': 'chigetai.n.01', 'name': 'chigetai'}, {'id': 4421, 'synset': 'common_zebra.n.01', 'name': 'common_zebra'}, {'id': 4422, 'synset': 'mountain_zebra.n.01', 'name': 'mountain_zebra'}, {'id': 4423, 'synset': "grevy's_zebra.n.01", 'name': "grevy's_zebra"}, {'id': 4424, 'synset': 'quagga.n.01', 'name': 'quagga'}, {'id': 4425, 'synset': 'indian_rhinoceros.n.01', 'name': 'Indian_rhinoceros'}, {'id': 4426, 'synset': 'woolly_rhinoceros.n.01', 'name': 'woolly_rhinoceros'}, {'id': 4427, 'synset': 'white_rhinoceros.n.01', 'name': 'white_rhinoceros'}, {'id': 4428, 'synset': 'black_rhinoceros.n.01', 'name': 'black_rhinoceros'}, {'id': 4429, 'synset': 'tapir.n.01', 'name': 'tapir'}, {'id': 4430, 'synset': 'new_world_tapir.n.01', 'name': 'New_World_tapir'}, {'id': 4431, 'synset': 'malayan_tapir.n.01', 'name': 'Malayan_tapir'}, {'id': 4432, 'synset': 'even-toed_ungulate.n.01', 'name': 'even-toed_ungulate'}, {'id': 4433, 'synset': 'swine.n.01', 'name': 'swine'}, {'id': 4434, 'synset': 'piglet.n.01', 'name': 'piglet'}, {'id': 4435, 'synset': 'sucking_pig.n.01', 'name': 'sucking_pig'}, {'id': 4436, 'synset': 'porker.n.01', 'name': 'porker'}, {'id': 4437, 'synset': 'boar.n.02', 'name': 'boar'}, {'id': 4438, 'synset': 'sow.n.01', 'name': 'sow'}, {'id': 4439, 'synset': 'razorback.n.01', 'name': 'razorback'}, {'id': 4440, 'synset': 'wild_boar.n.01', 'name': 'wild_boar'}, {'id': 4441, 'synset': 'babirusa.n.01', 'name': 'babirusa'}, {'id': 4442, 'synset': 'warthog.n.01', 'name': 'warthog'}, {'id': 4443, 'synset': 'peccary.n.01', 'name': 'peccary'}, {'id': 4444, 'synset': 'collared_peccary.n.01', 'name': 'collared_peccary'}, {'id': 4445, 'synset': 'white-lipped_peccary.n.01', 'name': 'white-lipped_peccary'}, {'id': 4446, 'synset': 'ruminant.n.01', 'name': 'ruminant'}, {'id': 4447, 'synset': 'bovid.n.01', 'name': 'bovid'}, {'id': 4448, 'synset': 'bovine.n.01', 'name': 'bovine'}, {'id': 4449, 'synset': 'ox.n.02', 'name': 'ox'}, {'id': 4450, 'synset': 'cattle.n.01', 'name': 'cattle'}, {'id': 4451, 'synset': 'ox.n.01', 'name': 'ox'}, {'id': 4452, 'synset': 'stirk.n.01', 'name': 'stirk'}, {'id': 4453, 'synset': 'bullock.n.02', 'name': 'bullock'}, {'id': 4454, 'synset': 'bull.n.01', 'name': 'bull'}, {'id': 4455, 'synset': 'cow.n.01', 'name': 'cow'}, {'id': 4456, 'synset': 'heifer.n.01', 'name': 'heifer'}, {'id': 4457, 'synset': 'bullock.n.01', 'name': 'bullock'}, {'id': 4458, 'synset': 'dogie.n.01', 'name': 'dogie'}, {'id': 4459, 'synset': 'maverick.n.02', 'name': 'maverick'}, {'id': 4460, 'synset': 'longhorn.n.01', 'name': 'longhorn'}, {'id': 4461, 'synset': 'brahman.n.04', 'name': 'Brahman'}, {'id': 4462, 'synset': 'zebu.n.01', 'name': 'zebu'}, {'id': 4463, 'synset': 'aurochs.n.02', 'name': 'aurochs'}, {'id': 4464, 'synset': 'yak.n.02', 'name': 'yak'}, {'id': 4465, 'synset': 'banteng.n.01', 'name': 'banteng'}, {'id': 4466, 'synset': 'welsh.n.03', 'name': 'Welsh'}, {'id': 4467, 'synset': 'red_poll.n.01', 'name': 'red_poll'}, {'id': 4468, 'synset': 'santa_gertrudis.n.01', 'name': 'Santa_Gertrudis'}, {'id': 4469, 'synset': 'aberdeen_angus.n.01', 'name': 'Aberdeen_Angus'}, {'id': 4470, 'synset': 'africander.n.01', 'name': 'Africander'}, {'id': 4471, 'synset': 'dairy_cattle.n.01', 'name': 'dairy_cattle'}, {'id': 4472, 'synset': 'ayrshire.n.01', 'name': 'Ayrshire'}, {'id': 4473, 'synset': 'brown_swiss.n.01', 'name': 'Brown_Swiss'}, {'id': 4474, 'synset': 'charolais.n.01', 'name': 'Charolais'}, {'id': 4475, 'synset': 'jersey.n.05', 'name': 'Jersey'}, {'id': 4476, 'synset': 'devon.n.02', 'name': 'Devon'}, {'id': 4477, 'synset': 'grade.n.09', 'name': 'grade'}, {'id': 4478, 'synset': 'durham.n.02', 'name': 'Durham'}, {'id': 4479, 'synset': 'milking_shorthorn.n.01', 'name': 'milking_shorthorn'}, {'id': 4480, 'synset': 'galloway.n.02', 'name': 'Galloway'}, {'id': 4481, 'synset': 'friesian.n.01', 'name': 'Friesian'}, {'id': 4482, 'synset': 'guernsey.n.02', 'name': 'Guernsey'}, {'id': 4483, 'synset': 'hereford.n.01', 'name': 'Hereford'}, {'id': 4484, 'synset': 'cattalo.n.01', 'name': 'cattalo'}, {'id': 4485, 'synset': 'old_world_buffalo.n.01', 'name': 'Old_World_buffalo'}, {'id': 4486, 'synset': 'water_buffalo.n.01', 'name': 'water_buffalo'}, {'id': 4487, 'synset': 'indian_buffalo.n.01', 'name': 'Indian_buffalo'}, {'id': 4488, 'synset': 'carabao.n.01', 'name': 'carabao'}, {'id': 4489, 'synset': 'anoa.n.01', 'name': 'anoa'}, {'id': 4490, 'synset': 'tamarau.n.01', 'name': 'tamarau'}, {'id': 4491, 'synset': 'cape_buffalo.n.01', 'name': 'Cape_buffalo'}, {'id': 4492, 'synset': 'asian_wild_ox.n.01', 'name': 'Asian_wild_ox'}, {'id': 4493, 'synset': 'gaur.n.01', 'name': 'gaur'}, {'id': 4494, 'synset': 'gayal.n.01', 'name': 'gayal'}, {'id': 4495, 'synset': 'bison.n.01', 'name': 'bison'}, {'id': 4496, 'synset': 'american_bison.n.01', 'name': 'American_bison'}, {'id': 4497, 'synset': 'wisent.n.01', 'name': 'wisent'}, {'id': 4498, 'synset': 'musk_ox.n.01', 'name': 'musk_ox'}, {'id': 4499, 'synset': 'ewe.n.03', 'name': 'ewe'}, {'id': 4500, 'synset': 'wether.n.01', 'name': 'wether'}, {'id': 4501, 'synset': 'lambkin.n.01', 'name': 'lambkin'}, {'id': 4502, 'synset': 'baa-lamb.n.01', 'name': 'baa-lamb'}, {'id': 4503, 'synset': 'hog.n.02', 'name': 'hog'}, {'id': 4504, 'synset': 'teg.n.01', 'name': 'teg'}, {'id': 4505, 'synset': 'persian_lamb.n.02', 'name': 'Persian_lamb'}, {'id': 4506, 'synset': 'domestic_sheep.n.01', 'name': 'domestic_sheep'}, {'id': 4507, 'synset': 'cotswold.n.01', 'name': 'Cotswold'}, {'id': 4508, 'synset': 'hampshire.n.02', 'name': 'Hampshire'}, {'id': 4509, 'synset': 'lincoln.n.03', 'name': 'Lincoln'}, {'id': 4510, 'synset': 'exmoor.n.01', 'name': 'Exmoor'}, {'id': 4511, 'synset': 'cheviot.n.01', 'name': 'Cheviot'}, {'id': 4512, 'synset': 'broadtail.n.02', 'name': 'broadtail'}, {'id': 4513, 'synset': 'longwool.n.01', 'name': 'longwool'}, {'id': 4514, 'synset': 'merino.n.01', 'name': 'merino'}, {'id': 4515, 'synset': 'rambouillet.n.01', 'name': 'Rambouillet'}, {'id': 4516, 'synset': 'wild_sheep.n.01', 'name': 'wild_sheep'}, {'id': 4517, 'synset': 'argali.n.01', 'name': 'argali'}, {'id': 4518, 'synset': 'marco_polo_sheep.n.01', 'name': 'Marco_Polo_sheep'}, {'id': 4519, 'synset': 'urial.n.01', 'name': 'urial'}, {'id': 4520, 'synset': 'dall_sheep.n.01', 'name': 'Dall_sheep'}, {'id': 4521, 'synset': 'mountain_sheep.n.01', 'name': 'mountain_sheep'}, {'id': 4522, 'synset': 'bighorn.n.02', 'name': 'bighorn'}, {'id': 4523, 'synset': 'mouflon.n.01', 'name': 'mouflon'}, {'id': 4524, 'synset': 'aoudad.n.01', 'name': 'aoudad'}, {'id': 4525, 'synset': 'kid.n.05', 'name': 'kid'}, {'id': 4526, 'synset': 'billy.n.02', 'name': 'billy'}, {'id': 4527, 'synset': 'nanny.n.02', 'name': 'nanny'}, {'id': 4528, 'synset': 'domestic_goat.n.01', 'name': 'domestic_goat'}, {'id': 4529, 'synset': 'cashmere_goat.n.01', 'name': 'Cashmere_goat'}, {'id': 4530, 'synset': 'angora.n.02', 'name': 'Angora'}, {'id': 4531, 'synset': 'wild_goat.n.01', 'name': 'wild_goat'}, {'id': 4532, 'synset': 'bezoar_goat.n.01', 'name': 'bezoar_goat'}, {'id': 4533, 'synset': 'markhor.n.01', 'name': 'markhor'}, {'id': 4534, 'synset': 'ibex.n.01', 'name': 'ibex'}, {'id': 4535, 'synset': 'goat_antelope.n.01', 'name': 'goat_antelope'}, {'id': 4536, 'synset': 'mountain_goat.n.01', 'name': 'mountain_goat'}, {'id': 4537, 'synset': 'goral.n.01', 'name': 'goral'}, {'id': 4538, 'synset': 'serow.n.01', 'name': 'serow'}, {'id': 4539, 'synset': 'chamois.n.02', 'name': 'chamois'}, {'id': 4540, 'synset': 'takin.n.01', 'name': 'takin'}, {'id': 4541, 'synset': 'antelope.n.01', 'name': 'antelope'}, {'id': 4542, 'synset': 'blackbuck.n.01', 'name': 'blackbuck'}, {'id': 4543, 'synset': 'gerenuk.n.01', 'name': 'gerenuk'}, {'id': 4544, 'synset': 'addax.n.01', 'name': 'addax'}, {'id': 4545, 'synset': 'gnu.n.01', 'name': 'gnu'}, {'id': 4546, 'synset': 'dik-dik.n.01', 'name': 'dik-dik'}, {'id': 4547, 'synset': 'hartebeest.n.01', 'name': 'hartebeest'}, {'id': 4548, 'synset': 'sassaby.n.01', 'name': 'sassaby'}, {'id': 4549, 'synset': 'impala.n.01', 'name': 'impala'}, {'id': 4550, 'synset': "thomson's_gazelle.n.01", 'name': "Thomson's_gazelle"}, {'id': 4551, 'synset': 'gazella_subgutturosa.n.01', 'name': 'Gazella_subgutturosa'}, {'id': 4552, 'synset': 'springbok.n.01', 'name': 'springbok'}, {'id': 4553, 'synset': 'bongo.n.02', 'name': 'bongo'}, {'id': 4554, 'synset': 'kudu.n.01', 'name': 'kudu'}, {'id': 4555, 'synset': 'greater_kudu.n.01', 'name': 'greater_kudu'}, {'id': 4556, 'synset': 'lesser_kudu.n.01', 'name': 'lesser_kudu'}, {'id': 4557, 'synset': 'harnessed_antelope.n.01', 'name': 'harnessed_antelope'}, {'id': 4558, 'synset': 'nyala.n.02', 'name': 'nyala'}, {'id': 4559, 'synset': 'mountain_nyala.n.01', 'name': 'mountain_nyala'}, {'id': 4560, 'synset': 'bushbuck.n.01', 'name': 'bushbuck'}, {'id': 4561, 'synset': 'nilgai.n.01', 'name': 'nilgai'}, {'id': 4562, 'synset': 'sable_antelope.n.01', 'name': 'sable_antelope'}, {'id': 4563, 'synset': 'saiga.n.01', 'name': 'saiga'}, {'id': 4564, 'synset': 'steenbok.n.01', 'name': 'steenbok'}, {'id': 4565, 'synset': 'eland.n.01', 'name': 'eland'}, {'id': 4566, 'synset': 'common_eland.n.01', 'name': 'common_eland'}, {'id': 4567, 'synset': 'giant_eland.n.01', 'name': 'giant_eland'}, {'id': 4568, 'synset': 'kob.n.01', 'name': 'kob'}, {'id': 4569, 'synset': 'lechwe.n.01', 'name': 'lechwe'}, {'id': 4570, 'synset': 'waterbuck.n.01', 'name': 'waterbuck'}, {'id': 4571, 'synset': 'puku.n.01', 'name': 'puku'}, {'id': 4572, 'synset': 'oryx.n.01', 'name': 'oryx'}, {'id': 4573, 'synset': 'gemsbok.n.01', 'name': 'gemsbok'}, {'id': 4574, 'synset': 'forest_goat.n.01', 'name': 'forest_goat'}, {'id': 4575, 'synset': 'pronghorn.n.01', 'name': 'pronghorn'}, {'id': 4576, 'synset': 'stag.n.02', 'name': 'stag'}, {'id': 4577, 'synset': 'royal.n.02', 'name': 'royal'}, {'id': 4578, 'synset': 'pricket.n.02', 'name': 'pricket'}, {'id': 4579, 'synset': 'fawn.n.02', 'name': 'fawn'}, {'id': 4580, 'synset': 'red_deer.n.01', 'name': 'red_deer'}, {'id': 4581, 'synset': 'hart.n.03', 'name': 'hart'}, {'id': 4582, 'synset': 'hind.n.02', 'name': 'hind'}, {'id': 4583, 'synset': 'brocket.n.02', 'name': 'brocket'}, {'id': 4584, 'synset': 'sambar.n.01', 'name': 'sambar'}, {'id': 4585, 'synset': 'wapiti.n.01', 'name': 'wapiti'}, {'id': 4586, 'synset': 'japanese_deer.n.01', 'name': 'Japanese_deer'}, {'id': 4587, 'synset': 'virginia_deer.n.01', 'name': 'Virginia_deer'}, {'id': 4588, 'synset': 'mule_deer.n.01', 'name': 'mule_deer'}, {'id': 4589, 'synset': 'black-tailed_deer.n.01', 'name': 'black-tailed_deer'}, {'id': 4590, 'synset': 'fallow_deer.n.01', 'name': 'fallow_deer'}, {'id': 4591, 'synset': 'roe_deer.n.01', 'name': 'roe_deer'}, {'id': 4592, 'synset': 'roebuck.n.01', 'name': 'roebuck'}, {'id': 4593, 'synset': 'caribou.n.01', 'name': 'caribou'}, {'id': 4594, 'synset': 'woodland_caribou.n.01', 'name': 'woodland_caribou'}, {'id': 4595, 'synset': 'barren_ground_caribou.n.01', 'name': 'barren_ground_caribou'}, {'id': 4596, 'synset': 'brocket.n.01', 'name': 'brocket'}, {'id': 4597, 'synset': 'muntjac.n.01', 'name': 'muntjac'}, {'id': 4598, 'synset': 'musk_deer.n.01', 'name': 'musk_deer'}, {'id': 4599, 'synset': "pere_david's_deer.n.01", 'name': "pere_david's_deer"}, {'id': 4600, 'synset': 'chevrotain.n.01', 'name': 'chevrotain'}, {'id': 4601, 'synset': 'kanchil.n.01', 'name': 'kanchil'}, {'id': 4602, 'synset': 'napu.n.01', 'name': 'napu'}, {'id': 4603, 'synset': 'water_chevrotain.n.01', 'name': 'water_chevrotain'}, {'id': 4604, 'synset': 'arabian_camel.n.01', 'name': 'Arabian_camel'}, {'id': 4605, 'synset': 'bactrian_camel.n.01', 'name': 'Bactrian_camel'}, {'id': 4606, 'synset': 'llama.n.01', 'name': 'llama'}, {'id': 4607, 'synset': 'domestic_llama.n.01', 'name': 'domestic_llama'}, {'id': 4608, 'synset': 'guanaco.n.01', 'name': 'guanaco'}, {'id': 4609, 'synset': 'alpaca.n.03', 'name': 'alpaca'}, {'id': 4610, 'synset': 'vicuna.n.03', 'name': 'vicuna'}, {'id': 4611, 'synset': 'okapi.n.01', 'name': 'okapi'}, {'id': 4612, 'synset': 'musteline_mammal.n.01', 'name': 'musteline_mammal'}, {'id': 4613, 'synset': 'weasel.n.02', 'name': 'weasel'}, {'id': 4614, 'synset': 'ermine.n.02', 'name': 'ermine'}, {'id': 4615, 'synset': 'stoat.n.01', 'name': 'stoat'}, {'id': 4616, 'synset': 'new_world_least_weasel.n.01', 'name': 'New_World_least_weasel'}, {'id': 4617, 'synset': 'old_world_least_weasel.n.01', 'name': 'Old_World_least_weasel'}, {'id': 4618, 'synset': 'longtail_weasel.n.01', 'name': 'longtail_weasel'}, {'id': 4619, 'synset': 'mink.n.03', 'name': 'mink'}, {'id': 4620, 'synset': 'american_mink.n.01', 'name': 'American_mink'}, {'id': 4621, 'synset': 'polecat.n.02', 'name': 'polecat'}, {'id': 4622, 'synset': 'black-footed_ferret.n.01', 'name': 'black-footed_ferret'}, {'id': 4623, 'synset': 'muishond.n.01', 'name': 'muishond'}, {'id': 4624, 'synset': 'snake_muishond.n.01', 'name': 'snake_muishond'}, {'id': 4625, 'synset': 'striped_muishond.n.01', 'name': 'striped_muishond'}, {'id': 4626, 'synset': 'otter.n.02', 'name': 'otter'}, {'id': 4627, 'synset': 'river_otter.n.01', 'name': 'river_otter'}, {'id': 4628, 'synset': 'eurasian_otter.n.01', 'name': 'Eurasian_otter'}, {'id': 4629, 'synset': 'sea_otter.n.01', 'name': 'sea_otter'}, {'id': 4630, 'synset': 'skunk.n.04', 'name': 'skunk'}, {'id': 4631, 'synset': 'striped_skunk.n.01', 'name': 'striped_skunk'}, {'id': 4632, 'synset': 'hooded_skunk.n.01', 'name': 'hooded_skunk'}, {'id': 4633, 'synset': 'hog-nosed_skunk.n.01', 'name': 'hog-nosed_skunk'}, {'id': 4634, 'synset': 'spotted_skunk.n.01', 'name': 'spotted_skunk'}, {'id': 4635, 'synset': 'badger.n.02', 'name': 'badger'}, {'id': 4636, 'synset': 'american_badger.n.01', 'name': 'American_badger'}, {'id': 4637, 'synset': 'eurasian_badger.n.01', 'name': 'Eurasian_badger'}, {'id': 4638, 'synset': 'ratel.n.01', 'name': 'ratel'}, {'id': 4639, 'synset': 'ferret_badger.n.01', 'name': 'ferret_badger'}, {'id': 4640, 'synset': 'hog_badger.n.01', 'name': 'hog_badger'}, {'id': 4641, 'synset': 'wolverine.n.03', 'name': 'wolverine'}, {'id': 4642, 'synset': 'glutton.n.02', 'name': 'glutton'}, {'id': 4643, 'synset': 'grison.n.01', 'name': 'grison'}, {'id': 4644, 'synset': 'marten.n.01', 'name': 'marten'}, {'id': 4645, 'synset': 'pine_marten.n.01', 'name': 'pine_marten'}, {'id': 4646, 'synset': 'sable.n.05', 'name': 'sable'}, {'id': 4647, 'synset': 'american_marten.n.01', 'name': 'American_marten'}, {'id': 4648, 'synset': 'stone_marten.n.01', 'name': 'stone_marten'}, {'id': 4649, 'synset': 'fisher.n.02', 'name': 'fisher'}, {'id': 4650, 'synset': 'yellow-throated_marten.n.01', 'name': 'yellow-throated_marten'}, {'id': 4651, 'synset': 'tayra.n.01', 'name': 'tayra'}, {'id': 4652, 'synset': 'fictional_animal.n.01', 'name': 'fictional_animal'}, {'id': 4653, 'synset': 'pachyderm.n.01', 'name': 'pachyderm'}, {'id': 4654, 'synset': 'edentate.n.01', 'name': 'edentate'}, {'id': 4655, 'synset': 'armadillo.n.01', 'name': 'armadillo'}, {'id': 4656, 'synset': 'peba.n.01', 'name': 'peba'}, {'id': 4657, 'synset': 'apar.n.01', 'name': 'apar'}, {'id': 4658, 'synset': 'tatouay.n.01', 'name': 'tatouay'}, {'id': 4659, 'synset': 'peludo.n.01', 'name': 'peludo'}, {'id': 4660, 'synset': 'giant_armadillo.n.01', 'name': 'giant_armadillo'}, {'id': 4661, 'synset': 'pichiciago.n.01', 'name': 'pichiciago'}, {'id': 4662, 'synset': 'sloth.n.02', 'name': 'sloth'}, {'id': 4663, 'synset': 'three-toed_sloth.n.01', 'name': 'three-toed_sloth'}, {'id': 4664, 'synset': 'two-toed_sloth.n.02', 'name': 'two-toed_sloth'}, {'id': 4665, 'synset': 'two-toed_sloth.n.01', 'name': 'two-toed_sloth'}, {'id': 4666, 'synset': 'megatherian.n.01', 'name': 'megatherian'}, {'id': 4667, 'synset': 'mylodontid.n.01', 'name': 'mylodontid'}, {'id': 4668, 'synset': 'anteater.n.02', 'name': 'anteater'}, {'id': 4669, 'synset': 'ant_bear.n.01', 'name': 'ant_bear'}, {'id': 4670, 'synset': 'silky_anteater.n.01', 'name': 'silky_anteater'}, {'id': 4671, 'synset': 'tamandua.n.01', 'name': 'tamandua'}, {'id': 4672, 'synset': 'pangolin.n.01', 'name': 'pangolin'}, {'id': 4673, 'synset': 'coronet.n.02', 'name': 'coronet'}, {'id': 4674, 'synset': 'scapular.n.01', 'name': 'scapular'}, {'id': 4675, 'synset': 'tadpole.n.01', 'name': 'tadpole'}, {'id': 4676, 'synset': 'primate.n.02', 'name': 'primate'}, {'id': 4677, 'synset': 'simian.n.01', 'name': 'simian'}, {'id': 4678, 'synset': 'ape.n.01', 'name': 'ape'}, {'id': 4679, 'synset': 'anthropoid.n.02', 'name': 'anthropoid'}, {'id': 4680, 'synset': 'anthropoid_ape.n.01', 'name': 'anthropoid_ape'}, {'id': 4681, 'synset': 'hominoid.n.01', 'name': 'hominoid'}, {'id': 4682, 'synset': 'hominid.n.01', 'name': 'hominid'}, {'id': 4683, 'synset': 'homo.n.02', 'name': 'homo'}, {'id': 4684, 'synset': 'world.n.08', 'name': 'world'}, {'id': 4685, 'synset': 'homo_erectus.n.01', 'name': 'Homo_erectus'}, {'id': 4686, 'synset': 'pithecanthropus.n.01', 'name': 'Pithecanthropus'}, {'id': 4687, 'synset': 'java_man.n.01', 'name': 'Java_man'}, {'id': 4688, 'synset': 'peking_man.n.01', 'name': 'Peking_man'}, {'id': 4689, 'synset': 'sinanthropus.n.01', 'name': 'Sinanthropus'}, {'id': 4690, 'synset': 'homo_soloensis.n.01', 'name': 'Homo_soloensis'}, {'id': 4691, 'synset': 'javanthropus.n.01', 'name': 'Javanthropus'}, {'id': 4692, 'synset': 'homo_habilis.n.01', 'name': 'Homo_habilis'}, {'id': 4693, 'synset': 'homo_sapiens.n.01', 'name': 'Homo_sapiens'}, {'id': 4694, 'synset': 'neandertal_man.n.01', 'name': 'Neandertal_man'}, {'id': 4695, 'synset': 'cro-magnon.n.01', 'name': 'Cro-magnon'}, {'id': 4696, 'synset': 'homo_sapiens_sapiens.n.01', 'name': 'Homo_sapiens_sapiens'}, {'id': 4697, 'synset': 'australopithecine.n.01', 'name': 'australopithecine'}, {'id': 4698, 'synset': 'australopithecus_afarensis.n.01', 'name': 'Australopithecus_afarensis'}, {'id': 4699, 'synset': 'australopithecus_africanus.n.01', 'name': 'Australopithecus_africanus'}, {'id': 4700, 'synset': 'australopithecus_boisei.n.01', 'name': 'Australopithecus_boisei'}, {'id': 4701, 'synset': 'zinjanthropus.n.01', 'name': 'Zinjanthropus'}, {'id': 4702, 'synset': 'australopithecus_robustus.n.01', 'name': 'Australopithecus_robustus'}, {'id': 4703, 'synset': 'paranthropus.n.01', 'name': 'Paranthropus'}, {'id': 4704, 'synset': 'sivapithecus.n.01', 'name': 'Sivapithecus'}, {'id': 4705, 'synset': 'rudapithecus.n.01', 'name': 'rudapithecus'}, {'id': 4706, 'synset': 'proconsul.n.03', 'name': 'proconsul'}, {'id': 4707, 'synset': 'aegyptopithecus.n.01', 'name': 'Aegyptopithecus'}, {'id': 4708, 'synset': 'great_ape.n.01', 'name': 'great_ape'}, {'id': 4709, 'synset': 'orangutan.n.01', 'name': 'orangutan'}, {'id': 4710, 'synset': 'western_lowland_gorilla.n.01', 'name': 'western_lowland_gorilla'}, {'id': 4711, 'synset': 'eastern_lowland_gorilla.n.01', 'name': 'eastern_lowland_gorilla'}, {'id': 4712, 'synset': 'mountain_gorilla.n.01', 'name': 'mountain_gorilla'}, {'id': 4713, 'synset': 'silverback.n.01', 'name': 'silverback'}, {'id': 4714, 'synset': 'chimpanzee.n.01', 'name': 'chimpanzee'}, {'id': 4715, 'synset': 'western_chimpanzee.n.01', 'name': 'western_chimpanzee'}, {'id': 4716, 'synset': 'eastern_chimpanzee.n.01', 'name': 'eastern_chimpanzee'}, {'id': 4717, 'synset': 'central_chimpanzee.n.01', 'name': 'central_chimpanzee'}, {'id': 4718, 'synset': 'pygmy_chimpanzee.n.01', 'name': 'pygmy_chimpanzee'}, {'id': 4719, 'synset': 'lesser_ape.n.01', 'name': 'lesser_ape'}, {'id': 4720, 'synset': 'gibbon.n.02', 'name': 'gibbon'}, {'id': 4721, 'synset': 'siamang.n.01', 'name': 'siamang'}, {'id': 4722, 'synset': 'old_world_monkey.n.01', 'name': 'Old_World_monkey'}, {'id': 4723, 'synset': 'guenon.n.01', 'name': 'guenon'}, {'id': 4724, 'synset': 'talapoin.n.01', 'name': 'talapoin'}, {'id': 4725, 'synset': 'grivet.n.01', 'name': 'grivet'}, {'id': 4726, 'synset': 'vervet.n.01', 'name': 'vervet'}, {'id': 4727, 'synset': 'green_monkey.n.01', 'name': 'green_monkey'}, {'id': 4728, 'synset': 'mangabey.n.01', 'name': 'mangabey'}, {'id': 4729, 'synset': 'patas.n.01', 'name': 'patas'}, {'id': 4730, 'synset': 'chacma.n.01', 'name': 'chacma'}, {'id': 4731, 'synset': 'mandrill.n.01', 'name': 'mandrill'}, {'id': 4732, 'synset': 'drill.n.02', 'name': 'drill'}, {'id': 4733, 'synset': 'macaque.n.01', 'name': 'macaque'}, {'id': 4734, 'synset': 'rhesus.n.01', 'name': 'rhesus'}, {'id': 4735, 'synset': 'bonnet_macaque.n.01', 'name': 'bonnet_macaque'}, {'id': 4736, 'synset': 'barbary_ape.n.01', 'name': 'Barbary_ape'}, {'id': 4737, 'synset': 'crab-eating_macaque.n.01', 'name': 'crab-eating_macaque'}, {'id': 4738, 'synset': 'langur.n.01', 'name': 'langur'}, {'id': 4739, 'synset': 'entellus.n.01', 'name': 'entellus'}, {'id': 4740, 'synset': 'colobus.n.01', 'name': 'colobus'}, {'id': 4741, 'synset': 'guereza.n.01', 'name': 'guereza'}, {'id': 4742, 'synset': 'proboscis_monkey.n.01', 'name': 'proboscis_monkey'}, {'id': 4743, 'synset': 'new_world_monkey.n.01', 'name': 'New_World_monkey'}, {'id': 4744, 'synset': 'marmoset.n.01', 'name': 'marmoset'}, {'id': 4745, 'synset': 'true_marmoset.n.01', 'name': 'true_marmoset'}, {'id': 4746, 'synset': 'pygmy_marmoset.n.01', 'name': 'pygmy_marmoset'}, {'id': 4747, 'synset': 'tamarin.n.01', 'name': 'tamarin'}, {'id': 4748, 'synset': 'silky_tamarin.n.01', 'name': 'silky_tamarin'}, {'id': 4749, 'synset': 'pinche.n.01', 'name': 'pinche'}, {'id': 4750, 'synset': 'capuchin.n.02', 'name': 'capuchin'}, {'id': 4751, 'synset': 'douroucouli.n.01', 'name': 'douroucouli'}, {'id': 4752, 'synset': 'howler_monkey.n.01', 'name': 'howler_monkey'}, {'id': 4753, 'synset': 'saki.n.03', 'name': 'saki'}, {'id': 4754, 'synset': 'uakari.n.01', 'name': 'uakari'}, {'id': 4755, 'synset': 'titi.n.03', 'name': 'titi'}, {'id': 4756, 'synset': 'spider_monkey.n.01', 'name': 'spider_monkey'}, {'id': 4757, 'synset': 'squirrel_monkey.n.01', 'name': 'squirrel_monkey'}, {'id': 4758, 'synset': 'woolly_monkey.n.01', 'name': 'woolly_monkey'}, {'id': 4759, 'synset': 'tree_shrew.n.01', 'name': 'tree_shrew'}, {'id': 4760, 'synset': 'prosimian.n.01', 'name': 'prosimian'}, {'id': 4761, 'synset': 'lemur.n.01', 'name': 'lemur'}, {'id': 4762, 'synset': 'madagascar_cat.n.01', 'name': 'Madagascar_cat'}, {'id': 4763, 'synset': 'aye-aye.n.01', 'name': 'aye-aye'}, {'id': 4764, 'synset': 'slender_loris.n.01', 'name': 'slender_loris'}, {'id': 4765, 'synset': 'slow_loris.n.01', 'name': 'slow_loris'}, {'id': 4766, 'synset': 'potto.n.02', 'name': 'potto'}, {'id': 4767, 'synset': 'angwantibo.n.01', 'name': 'angwantibo'}, {'id': 4768, 'synset': 'galago.n.01', 'name': 'galago'}, {'id': 4769, 'synset': 'indri.n.01', 'name': 'indri'}, {'id': 4770, 'synset': 'woolly_indris.n.01', 'name': 'woolly_indris'}, {'id': 4771, 'synset': 'tarsier.n.01', 'name': 'tarsier'}, {'id': 4772, 'synset': 'tarsius_syrichta.n.01', 'name': 'Tarsius_syrichta'}, {'id': 4773, 'synset': 'tarsius_glis.n.01', 'name': 'Tarsius_glis'}, {'id': 4774, 'synset': 'flying_lemur.n.01', 'name': 'flying_lemur'}, {'id': 4775, 'synset': 'cynocephalus_variegatus.n.01', 'name': 'Cynocephalus_variegatus'}, {'id': 4776, 'synset': 'proboscidean.n.01', 'name': 'proboscidean'}, {'id': 4777, 'synset': 'rogue_elephant.n.01', 'name': 'rogue_elephant'}, {'id': 4778, 'synset': 'indian_elephant.n.01', 'name': 'Indian_elephant'}, {'id': 4779, 'synset': 'african_elephant.n.01', 'name': 'African_elephant'}, {'id': 4780, 'synset': 'woolly_mammoth.n.01', 'name': 'woolly_mammoth'}, {'id': 4781, 'synset': 'columbian_mammoth.n.01', 'name': 'columbian_mammoth'}, {'id': 4782, 'synset': 'imperial_mammoth.n.01', 'name': 'imperial_mammoth'}, {'id': 4783, 'synset': 'mastodon.n.01', 'name': 'mastodon'}, {'id': 4784, 'synset': 'plantigrade_mammal.n.01', 'name': 'plantigrade_mammal'}, {'id': 4785, 'synset': 'digitigrade_mammal.n.01', 'name': 'digitigrade_mammal'}, {'id': 4786, 'synset': 'procyonid.n.01', 'name': 'procyonid'}, {'id': 4787, 'synset': 'raccoon.n.02', 'name': 'raccoon'}, {'id': 4788, 'synset': 'common_raccoon.n.01', 'name': 'common_raccoon'}, {'id': 4789, 'synset': 'crab-eating_raccoon.n.01', 'name': 'crab-eating_raccoon'}, {'id': 4790, 'synset': 'bassarisk.n.01', 'name': 'bassarisk'}, {'id': 4791, 'synset': 'kinkajou.n.01', 'name': 'kinkajou'}, {'id': 4792, 'synset': 'coati.n.01', 'name': 'coati'}, {'id': 4793, 'synset': 'lesser_panda.n.01', 'name': 'lesser_panda'}, {'id': 4794, 'synset': 'twitterer.n.01', 'name': 'twitterer'}, {'id': 4795, 'synset': 'fingerling.n.01', 'name': 'fingerling'}, {'id': 4796, 'synset': 'game_fish.n.01', 'name': 'game_fish'}, {'id': 4797, 'synset': 'food_fish.n.01', 'name': 'food_fish'}, {'id': 4798, 'synset': 'rough_fish.n.01', 'name': 'rough_fish'}, {'id': 4799, 'synset': 'groundfish.n.01', 'name': 'groundfish'}, {'id': 4800, 'synset': 'young_fish.n.01', 'name': 'young_fish'}, {'id': 4801, 'synset': 'parr.n.03', 'name': 'parr'}, {'id': 4802, 'synset': 'mouthbreeder.n.01', 'name': 'mouthbreeder'}, {'id': 4803, 'synset': 'spawner.n.01', 'name': 'spawner'}, {'id': 4804, 'synset': 'barracouta.n.01', 'name': 'barracouta'}, {'id': 4805, 'synset': 'crossopterygian.n.01', 'name': 'crossopterygian'}, {'id': 4806, 'synset': 'coelacanth.n.01', 'name': 'coelacanth'}, {'id': 4807, 'synset': 'lungfish.n.01', 'name': 'lungfish'}, {'id': 4808, 'synset': 'ceratodus.n.01', 'name': 'ceratodus'}, {'id': 4809, 'synset': 'catfish.n.03', 'name': 'catfish'}, {'id': 4810, 'synset': 'silurid.n.01', 'name': 'silurid'}, {'id': 4811, 'synset': 'european_catfish.n.01', 'name': 'European_catfish'}, {'id': 4812, 'synset': 'electric_catfish.n.01', 'name': 'electric_catfish'}, {'id': 4813, 'synset': 'bullhead.n.02', 'name': 'bullhead'}, {'id': 4814, 'synset': 'horned_pout.n.01', 'name': 'horned_pout'}, {'id': 4815, 'synset': 'brown_bullhead.n.01', 'name': 'brown_bullhead'}, {'id': 4816, 'synset': 'channel_catfish.n.01', 'name': 'channel_catfish'}, {'id': 4817, 'synset': 'blue_catfish.n.01', 'name': 'blue_catfish'}, {'id': 4818, 'synset': 'flathead_catfish.n.01', 'name': 'flathead_catfish'}, {'id': 4819, 'synset': 'armored_catfish.n.01', 'name': 'armored_catfish'}, {'id': 4820, 'synset': 'sea_catfish.n.01', 'name': 'sea_catfish'}, {'id': 4821, 'synset': 'gadoid.n.01', 'name': 'gadoid'}, {'id': 4822, 'synset': 'cod.n.03', 'name': 'cod'}, {'id': 4823, 'synset': 'codling.n.01', 'name': 'codling'}, {'id': 4824, 'synset': 'atlantic_cod.n.01', 'name': 'Atlantic_cod'}, {'id': 4825, 'synset': 'pacific_cod.n.01', 'name': 'Pacific_cod'}, {'id': 4826, 'synset': 'whiting.n.06', 'name': 'whiting'}, {'id': 4827, 'synset': 'burbot.n.01', 'name': 'burbot'}, {'id': 4828, 'synset': 'haddock.n.02', 'name': 'haddock'}, {'id': 4829, 'synset': 'pollack.n.03', 'name': 'pollack'}, {'id': 4830, 'synset': 'hake.n.02', 'name': 'hake'}, {'id': 4831, 'synset': 'silver_hake.n.01', 'name': 'silver_hake'}, {'id': 4832, 'synset': 'ling.n.04', 'name': 'ling'}, {'id': 4833, 'synset': 'cusk.n.02', 'name': 'cusk'}, {'id': 4834, 'synset': 'grenadier.n.02', 'name': 'grenadier'}, {'id': 4835, 'synset': 'eel.n.02', 'name': 'eel'}, {'id': 4836, 'synset': 'elver.n.02', 'name': 'elver'}, {'id': 4837, 'synset': 'common_eel.n.01', 'name': 'common_eel'}, {'id': 4838, 'synset': 'tuna.n.04', 'name': 'tuna'}, {'id': 4839, 'synset': 'moray.n.01', 'name': 'moray'}, {'id': 4840, 'synset': 'conger.n.01', 'name': 'conger'}, {'id': 4841, 'synset': 'teleost_fish.n.01', 'name': 'teleost_fish'}, {'id': 4842, 'synset': 'beaked_salmon.n.01', 'name': 'beaked_salmon'}, {'id': 4843, 'synset': 'clupeid_fish.n.01', 'name': 'clupeid_fish'}, {'id': 4844, 'synset': 'whitebait.n.02', 'name': 'whitebait'}, {'id': 4845, 'synset': 'brit.n.02', 'name': 'brit'}, {'id': 4846, 'synset': 'shad.n.02', 'name': 'shad'}, {'id': 4847, 'synset': 'common_american_shad.n.01', 'name': 'common_American_shad'}, {'id': 4848, 'synset': 'river_shad.n.01', 'name': 'river_shad'}, {'id': 4849, 'synset': 'allice_shad.n.01', 'name': 'allice_shad'}, {'id': 4850, 'synset': 'alewife.n.02', 'name': 'alewife'}, {'id': 4851, 'synset': 'menhaden.n.01', 'name': 'menhaden'}, {'id': 4852, 'synset': 'herring.n.02', 'name': 'herring'}, {'id': 4853, 'synset': 'atlantic_herring.n.01', 'name': 'Atlantic_herring'}, {'id': 4854, 'synset': 'pacific_herring.n.01', 'name': 'Pacific_herring'}, {'id': 4855, 'synset': 'sardine.n.02', 'name': 'sardine'}, {'id': 4856, 'synset': 'sild.n.01', 'name': 'sild'}, {'id': 4857, 'synset': 'brisling.n.02', 'name': 'brisling'}, {'id': 4858, 'synset': 'pilchard.n.02', 'name': 'pilchard'}, {'id': 4859, 'synset': 'pacific_sardine.n.01', 'name': 'Pacific_sardine'}, {'id': 4860, 'synset': 'anchovy.n.02', 'name': 'anchovy'}, {'id': 4861, 'synset': 'mediterranean_anchovy.n.01', 'name': 'mediterranean_anchovy'}, {'id': 4862, 'synset': 'salmonid.n.01', 'name': 'salmonid'}, {'id': 4863, 'synset': 'parr.n.02', 'name': 'parr'}, {'id': 4864, 'synset': 'blackfish.n.02', 'name': 'blackfish'}, {'id': 4865, 'synset': 'redfish.n.03', 'name': 'redfish'}, {'id': 4866, 'synset': 'atlantic_salmon.n.02', 'name': 'Atlantic_salmon'}, {'id': 4867, 'synset': 'landlocked_salmon.n.01', 'name': 'landlocked_salmon'}, {'id': 4868, 'synset': 'sockeye.n.02', 'name': 'sockeye'}, {'id': 4869, 'synset': 'chinook.n.05', 'name': 'chinook'}, {'id': 4870, 'synset': 'coho.n.02', 'name': 'coho'}, {'id': 4871, 'synset': 'trout.n.02', 'name': 'trout'}, {'id': 4872, 'synset': 'brown_trout.n.01', 'name': 'brown_trout'}, {'id': 4873, 'synset': 'rainbow_trout.n.02', 'name': 'rainbow_trout'}, {'id': 4874, 'synset': 'sea_trout.n.03', 'name': 'sea_trout'}, {'id': 4875, 'synset': 'lake_trout.n.02', 'name': 'lake_trout'}, {'id': 4876, 'synset': 'brook_trout.n.02', 'name': 'brook_trout'}, {'id': 4877, 'synset': 'char.n.03', 'name': 'char'}, {'id': 4878, 'synset': 'arctic_char.n.01', 'name': 'Arctic_char'}, {'id': 4879, 'synset': 'whitefish.n.03', 'name': 'whitefish'}, {'id': 4880, 'synset': 'lake_whitefish.n.01', 'name': 'lake_whitefish'}, {'id': 4881, 'synset': 'cisco.n.02', 'name': 'cisco'}, {'id': 4882, 'synset': 'round_whitefish.n.01', 'name': 'round_whitefish'}, {'id': 4883, 'synset': 'smelt.n.02', 'name': 'smelt'}, {'id': 4884, 'synset': 'sparling.n.02', 'name': 'sparling'}, {'id': 4885, 'synset': 'capelin.n.01', 'name': 'capelin'}, {'id': 4886, 'synset': 'tarpon.n.01', 'name': 'tarpon'}, {'id': 4887, 'synset': 'ladyfish.n.01', 'name': 'ladyfish'}, {'id': 4888, 'synset': 'bonefish.n.01', 'name': 'bonefish'}, {'id': 4889, 'synset': 'argentine.n.01', 'name': 'argentine'}, {'id': 4890, 'synset': 'lanternfish.n.01', 'name': 'lanternfish'}, {'id': 4891, 'synset': 'lizardfish.n.01', 'name': 'lizardfish'}, {'id': 4892, 'synset': 'lancetfish.n.01', 'name': 'lancetfish'}, {'id': 4893, 'synset': 'opah.n.01', 'name': 'opah'}, {'id': 4894, 'synset': 'new_world_opah.n.01', 'name': 'New_World_opah'}, {'id': 4895, 'synset': 'ribbonfish.n.02', 'name': 'ribbonfish'}, {'id': 4896, 'synset': 'dealfish.n.01', 'name': 'dealfish'}, {'id': 4897, 'synset': 'oarfish.n.01', 'name': 'oarfish'}, {'id': 4898, 'synset': 'batfish.n.01', 'name': 'batfish'}, {'id': 4899, 'synset': 'goosefish.n.01', 'name': 'goosefish'}, {'id': 4900, 'synset': 'toadfish.n.01', 'name': 'toadfish'}, {'id': 4901, 'synset': 'oyster_fish.n.01', 'name': 'oyster_fish'}, {'id': 4902, 'synset': 'frogfish.n.01', 'name': 'frogfish'}, {'id': 4903, 'synset': 'sargassum_fish.n.01', 'name': 'sargassum_fish'}, {'id': 4904, 'synset': 'needlefish.n.01', 'name': 'needlefish'}, {'id': 4905, 'synset': 'timucu.n.01', 'name': 'timucu'}, {'id': 4906, 'synset': 'flying_fish.n.01', 'name': 'flying_fish'}, {'id': 4907, 'synset': 'monoplane_flying_fish.n.01', 'name': 'monoplane_flying_fish'}, {'id': 4908, 'synset': 'halfbeak.n.01', 'name': 'halfbeak'}, {'id': 4909, 'synset': 'saury.n.01', 'name': 'saury'}, {'id': 4910, 'synset': 'spiny-finned_fish.n.01', 'name': 'spiny-finned_fish'}, {'id': 4911, 'synset': 'lingcod.n.02', 'name': 'lingcod'}, {'id': 4912, 'synset': 'percoid_fish.n.01', 'name': 'percoid_fish'}, {'id': 4913, 'synset': 'perch.n.07', 'name': 'perch'}, {'id': 4914, 'synset': 'climbing_perch.n.01', 'name': 'climbing_perch'}, {'id': 4915, 'synset': 'perch.n.06', 'name': 'perch'}, {'id': 4916, 'synset': 'yellow_perch.n.01', 'name': 'yellow_perch'}, {'id': 4917, 'synset': 'european_perch.n.01', 'name': 'European_perch'}, {'id': 4918, 'synset': 'pike-perch.n.01', 'name': 'pike-perch'}, {'id': 4919, 'synset': 'walleye.n.02', 'name': 'walleye'}, {'id': 4920, 'synset': 'blue_pike.n.01', 'name': 'blue_pike'}, {'id': 4921, 'synset': 'snail_darter.n.01', 'name': 'snail_darter'}, {'id': 4922, 'synset': 'cusk-eel.n.01', 'name': 'cusk-eel'}, {'id': 4923, 'synset': 'brotula.n.01', 'name': 'brotula'}, {'id': 4924, 'synset': 'pearlfish.n.01', 'name': 'pearlfish'}, {'id': 4925, 'synset': 'robalo.n.01', 'name': 'robalo'}, {'id': 4926, 'synset': 'snook.n.01', 'name': 'snook'}, {'id': 4927, 'synset': 'pike.n.05', 'name': 'pike'}, {'id': 4928, 'synset': 'northern_pike.n.01', 'name': 'northern_pike'}, {'id': 4929, 'synset': 'muskellunge.n.02', 'name': 'muskellunge'}, {'id': 4930, 'synset': 'pickerel.n.02', 'name': 'pickerel'}, {'id': 4931, 'synset': 'chain_pickerel.n.01', 'name': 'chain_pickerel'}, {'id': 4932, 'synset': 'redfin_pickerel.n.01', 'name': 'redfin_pickerel'}, {'id': 4933, 'synset': 'sunfish.n.03', 'name': 'sunfish'}, {'id': 4934, 'synset': 'crappie.n.02', 'name': 'crappie'}, {'id': 4935, 'synset': 'black_crappie.n.01', 'name': 'black_crappie'}, {'id': 4936, 'synset': 'white_crappie.n.01', 'name': 'white_crappie'}, {'id': 4937, 'synset': 'freshwater_bream.n.02', 'name': 'freshwater_bream'}, {'id': 4938, 'synset': 'pumpkinseed.n.01', 'name': 'pumpkinseed'}, {'id': 4939, 'synset': 'bluegill.n.01', 'name': 'bluegill'}, {'id': 4940, 'synset': 'spotted_sunfish.n.01', 'name': 'spotted_sunfish'}, {'id': 4941, 'synset': 'freshwater_bass.n.02', 'name': 'freshwater_bass'}, {'id': 4942, 'synset': 'rock_bass.n.02', 'name': 'rock_bass'}, {'id': 4943, 'synset': 'black_bass.n.02', 'name': 'black_bass'}, {'id': 4944, 'synset': 'kentucky_black_bass.n.01', 'name': 'Kentucky_black_bass'}, {'id': 4945, 'synset': 'smallmouth.n.01', 'name': 'smallmouth'}, {'id': 4946, 'synset': 'largemouth.n.01', 'name': 'largemouth'}, {'id': 4947, 'synset': 'bass.n.08', 'name': 'bass'}, {'id': 4948, 'synset': 'serranid_fish.n.01', 'name': 'serranid_fish'}, {'id': 4949, 'synset': 'white_perch.n.01', 'name': 'white_perch'}, {'id': 4950, 'synset': 'yellow_bass.n.01', 'name': 'yellow_bass'}, {'id': 4951, 'synset': 'blackmouth_bass.n.01', 'name': 'blackmouth_bass'}, {'id': 4952, 'synset': 'rock_sea_bass.n.01', 'name': 'rock_sea_bass'}, {'id': 4953, 'synset': 'striped_bass.n.02', 'name': 'striped_bass'}, {'id': 4954, 'synset': 'stone_bass.n.01', 'name': 'stone_bass'}, {'id': 4955, 'synset': 'grouper.n.02', 'name': 'grouper'}, {'id': 4956, 'synset': 'hind.n.01', 'name': 'hind'}, {'id': 4957, 'synset': 'rock_hind.n.01', 'name': 'rock_hind'}, {'id': 4958, 'synset': 'creole-fish.n.01', 'name': 'creole-fish'}, {'id': 4959, 'synset': 'jewfish.n.02', 'name': 'jewfish'}, {'id': 4960, 'synset': 'soapfish.n.01', 'name': 'soapfish'}, {'id': 4961, 'synset': 'surfperch.n.01', 'name': 'surfperch'}, {'id': 4962, 'synset': 'rainbow_seaperch.n.01', 'name': 'rainbow_seaperch'}, {'id': 4963, 'synset': 'bigeye.n.01', 'name': 'bigeye'}, {'id': 4964, 'synset': 'catalufa.n.01', 'name': 'catalufa'}, {'id': 4965, 'synset': 'cardinalfish.n.01', 'name': 'cardinalfish'}, {'id': 4966, 'synset': 'flame_fish.n.01', 'name': 'flame_fish'}, {'id': 4967, 'synset': 'tilefish.n.02', 'name': 'tilefish'}, {'id': 4968, 'synset': 'bluefish.n.01', 'name': 'bluefish'}, {'id': 4969, 'synset': 'cobia.n.01', 'name': 'cobia'}, {'id': 4970, 'synset': 'remora.n.01', 'name': 'remora'}, {'id': 4971, 'synset': 'sharksucker.n.01', 'name': 'sharksucker'}, {'id': 4972, 'synset': 'whale_sucker.n.01', 'name': 'whale_sucker'}, {'id': 4973, 'synset': 'carangid_fish.n.01', 'name': 'carangid_fish'}, {'id': 4974, 'synset': 'jack.n.11', 'name': 'jack'}, {'id': 4975, 'synset': 'crevalle_jack.n.01', 'name': 'crevalle_jack'}, {'id': 4976, 'synset': 'yellow_jack.n.03', 'name': 'yellow_jack'}, {'id': 4977, 'synset': 'runner.n.10', 'name': 'runner'}, {'id': 4978, 'synset': 'rainbow_runner.n.01', 'name': 'rainbow_runner'}, {'id': 4979, 'synset': 'leatherjacket.n.02', 'name': 'leatherjacket'}, {'id': 4980, 'synset': 'threadfish.n.01', 'name': 'threadfish'}, {'id': 4981, 'synset': 'moonfish.n.01', 'name': 'moonfish'}, {'id': 4982, 'synset': 'lookdown.n.01', 'name': 'lookdown'}, {'id': 4983, 'synset': 'amberjack.n.01', 'name': 'amberjack'}, {'id': 4984, 'synset': 'yellowtail.n.02', 'name': 'yellowtail'}, {'id': 4985, 'synset': 'kingfish.n.05', 'name': 'kingfish'}, {'id': 4986, 'synset': 'pompano.n.02', 'name': 'pompano'}, {'id': 4987, 'synset': 'florida_pompano.n.01', 'name': 'Florida_pompano'}, {'id': 4988, 'synset': 'permit.n.03', 'name': 'permit'}, {'id': 4989, 'synset': 'scad.n.01', 'name': 'scad'}, {'id': 4990, 'synset': 'horse_mackerel.n.03', 'name': 'horse_mackerel'}, {'id': 4991, 'synset': 'horse_mackerel.n.02', 'name': 'horse_mackerel'}, {'id': 4992, 'synset': 'bigeye_scad.n.01', 'name': 'bigeye_scad'}, {'id': 4993, 'synset': 'mackerel_scad.n.01', 'name': 'mackerel_scad'}, {'id': 4994, 'synset': 'round_scad.n.01', 'name': 'round_scad'}, {'id': 4995, 'synset': 'dolphinfish.n.02', 'name': 'dolphinfish'}, {'id': 4996, 'synset': 'coryphaena_hippurus.n.01', 'name': 'Coryphaena_hippurus'}, {'id': 4997, 'synset': 'coryphaena_equisetis.n.01', 'name': 'Coryphaena_equisetis'}, {'id': 4998, 'synset': 'pomfret.n.01', 'name': 'pomfret'}, {'id': 4999, 'synset': 'characin.n.01', 'name': 'characin'}, {'id': 5000, 'synset': 'tetra.n.01', 'name': 'tetra'}, {'id': 5001, 'synset': 'cardinal_tetra.n.01', 'name': 'cardinal_tetra'}, {'id': 5002, 'synset': 'piranha.n.02', 'name': 'piranha'}, {'id': 5003, 'synset': 'cichlid.n.01', 'name': 'cichlid'}, {'id': 5004, 'synset': 'bolti.n.01', 'name': 'bolti'}, {'id': 5005, 'synset': 'snapper.n.05', 'name': 'snapper'}, {'id': 5006, 'synset': 'red_snapper.n.02', 'name': 'red_snapper'}, {'id': 5007, 'synset': 'grey_snapper.n.01', 'name': 'grey_snapper'}, {'id': 5008, 'synset': 'mutton_snapper.n.01', 'name': 'mutton_snapper'}, {'id': 5009, 'synset': 'schoolmaster.n.03', 'name': 'schoolmaster'}, {'id': 5010, 'synset': 'yellowtail.n.01', 'name': 'yellowtail'}, {'id': 5011, 'synset': 'grunt.n.03', 'name': 'grunt'}, {'id': 5012, 'synset': 'margate.n.01', 'name': 'margate'}, {'id': 5013, 'synset': 'spanish_grunt.n.01', 'name': 'Spanish_grunt'}, {'id': 5014, 'synset': 'tomtate.n.01', 'name': 'tomtate'}, {'id': 5015, 'synset': 'cottonwick.n.01', 'name': 'cottonwick'}, {'id': 5016, 'synset': "sailor's-choice.n.02", 'name': "sailor's-choice"}, {'id': 5017, 'synset': 'porkfish.n.01', 'name': 'porkfish'}, {'id': 5018, 'synset': 'pompon.n.02', 'name': 'pompon'}, {'id': 5019, 'synset': 'pigfish.n.02', 'name': 'pigfish'}, {'id': 5020, 'synset': 'sparid.n.01', 'name': 'sparid'}, {'id': 5021, 'synset': 'sea_bream.n.02', 'name': 'sea_bream'}, {'id': 5022, 'synset': 'porgy.n.02', 'name': 'porgy'}, {'id': 5023, 'synset': 'red_porgy.n.01', 'name': 'red_porgy'}, {'id': 5024, 'synset': 'european_sea_bream.n.01', 'name': 'European_sea_bream'}, {'id': 5025, 'synset': 'atlantic_sea_bream.n.01', 'name': 'Atlantic_sea_bream'}, {'id': 5026, 'synset': 'sheepshead.n.01', 'name': 'sheepshead'}, {'id': 5027, 'synset': 'pinfish.n.01', 'name': 'pinfish'}, {'id': 5028, 'synset': 'sheepshead_porgy.n.01', 'name': 'sheepshead_porgy'}, {'id': 5029, 'synset': 'snapper.n.04', 'name': 'snapper'}, {'id': 5030, 'synset': 'black_bream.n.01', 'name': 'black_bream'}, {'id': 5031, 'synset': 'scup.n.04', 'name': 'scup'}, {'id': 5032, 'synset': 'scup.n.03', 'name': 'scup'}, {'id': 5033, 'synset': 'sciaenid_fish.n.01', 'name': 'sciaenid_fish'}, {'id': 5034, 'synset': 'striped_drum.n.01', 'name': 'striped_drum'}, {'id': 5035, 'synset': 'jackknife-fish.n.01', 'name': 'jackknife-fish'}, {'id': 5036, 'synset': 'silver_perch.n.01', 'name': 'silver_perch'}, {'id': 5037, 'synset': 'red_drum.n.01', 'name': 'red_drum'}, {'id': 5038, 'synset': 'mulloway.n.01', 'name': 'mulloway'}, {'id': 5039, 'synset': 'maigre.n.01', 'name': 'maigre'}, {'id': 5040, 'synset': 'croaker.n.02', 'name': 'croaker'}, {'id': 5041, 'synset': 'atlantic_croaker.n.01', 'name': 'Atlantic_croaker'}, {'id': 5042, 'synset': 'yellowfin_croaker.n.01', 'name': 'yellowfin_croaker'}, {'id': 5043, 'synset': 'whiting.n.04', 'name': 'whiting'}, {'id': 5044, 'synset': 'kingfish.n.04', 'name': 'kingfish'}, {'id': 5045, 'synset': 'king_whiting.n.01', 'name': 'king_whiting'}, {'id': 5046, 'synset': 'northern_whiting.n.01', 'name': 'northern_whiting'}, {'id': 5047, 'synset': 'corbina.n.01', 'name': 'corbina'}, {'id': 5048, 'synset': 'white_croaker.n.02', 'name': 'white_croaker'}, {'id': 5049, 'synset': 'white_croaker.n.01', 'name': 'white_croaker'}, {'id': 5050, 'synset': 'sea_trout.n.02', 'name': 'sea_trout'}, {'id': 5051, 'synset': 'weakfish.n.02', 'name': 'weakfish'}, {'id': 5052, 'synset': 'spotted_weakfish.n.01', 'name': 'spotted_weakfish'}, {'id': 5053, 'synset': 'mullet.n.03', 'name': 'mullet'}, {'id': 5054, 'synset': 'goatfish.n.01', 'name': 'goatfish'}, {'id': 5055, 'synset': 'red_goatfish.n.01', 'name': 'red_goatfish'}, {'id': 5056, 'synset': 'yellow_goatfish.n.01', 'name': 'yellow_goatfish'}, {'id': 5057, 'synset': 'mullet.n.02', 'name': 'mullet'}, {'id': 5058, 'synset': 'striped_mullet.n.01', 'name': 'striped_mullet'}, {'id': 5059, 'synset': 'white_mullet.n.01', 'name': 'white_mullet'}, {'id': 5060, 'synset': 'liza.n.01', 'name': 'liza'}, {'id': 5061, 'synset': 'silversides.n.01', 'name': 'silversides'}, {'id': 5062, 'synset': 'jacksmelt.n.01', 'name': 'jacksmelt'}, {'id': 5063, 'synset': 'barracuda.n.01', 'name': 'barracuda'}, {'id': 5064, 'synset': 'great_barracuda.n.01', 'name': 'great_barracuda'}, {'id': 5065, 'synset': 'sweeper.n.03', 'name': 'sweeper'}, {'id': 5066, 'synset': 'sea_chub.n.01', 'name': 'sea_chub'}, {'id': 5067, 'synset': 'bermuda_chub.n.01', 'name': 'Bermuda_chub'}, {'id': 5068, 'synset': 'spadefish.n.01', 'name': 'spadefish'}, {'id': 5069, 'synset': 'butterfly_fish.n.01', 'name': 'butterfly_fish'}, {'id': 5070, 'synset': 'chaetodon.n.01', 'name': 'chaetodon'}, {'id': 5071, 'synset': 'angelfish.n.01', 'name': 'angelfish'}, {'id': 5072, 'synset': 'rock_beauty.n.01', 'name': 'rock_beauty'}, {'id': 5073, 'synset': 'damselfish.n.01', 'name': 'damselfish'}, {'id': 5074, 'synset': 'beaugregory.n.01', 'name': 'beaugregory'}, {'id': 5075, 'synset': 'anemone_fish.n.01', 'name': 'anemone_fish'}, {'id': 5076, 'synset': 'clown_anemone_fish.n.01', 'name': 'clown_anemone_fish'}, {'id': 5077, 'synset': 'sergeant_major.n.02', 'name': 'sergeant_major'}, {'id': 5078, 'synset': 'wrasse.n.01', 'name': 'wrasse'}, {'id': 5079, 'synset': 'pigfish.n.01', 'name': 'pigfish'}, {'id': 5080, 'synset': 'hogfish.n.01', 'name': 'hogfish'}, {'id': 5081, 'synset': 'slippery_dick.n.01', 'name': 'slippery_dick'}, {'id': 5082, 'synset': 'puddingwife.n.01', 'name': 'puddingwife'}, {'id': 5083, 'synset': 'bluehead.n.01', 'name': 'bluehead'}, {'id': 5084, 'synset': 'pearly_razorfish.n.01', 'name': 'pearly_razorfish'}, {'id': 5085, 'synset': 'tautog.n.01', 'name': 'tautog'}, {'id': 5086, 'synset': 'cunner.n.01', 'name': 'cunner'}, {'id': 5087, 'synset': 'parrotfish.n.01', 'name': 'parrotfish'}, {'id': 5088, 'synset': 'threadfin.n.01', 'name': 'threadfin'}, {'id': 5089, 'synset': 'jawfish.n.01', 'name': 'jawfish'}, {'id': 5090, 'synset': 'stargazer.n.03', 'name': 'stargazer'}, {'id': 5091, 'synset': 'sand_stargazer.n.01', 'name': 'sand_stargazer'}, {'id': 5092, 'synset': 'blenny.n.01', 'name': 'blenny'}, {'id': 5093, 'synset': 'shanny.n.01', 'name': 'shanny'}, {'id': 5094, 'synset': 'molly_miller.n.01', 'name': 'Molly_Miller'}, {'id': 5095, 'synset': 'clinid.n.01', 'name': 'clinid'}, {'id': 5096, 'synset': 'pikeblenny.n.01', 'name': 'pikeblenny'}, {'id': 5097, 'synset': 'bluethroat_pikeblenny.n.01', 'name': 'bluethroat_pikeblenny'}, {'id': 5098, 'synset': 'gunnel.n.02', 'name': 'gunnel'}, {'id': 5099, 'synset': 'rock_gunnel.n.01', 'name': 'rock_gunnel'}, {'id': 5100, 'synset': 'eelblenny.n.01', 'name': 'eelblenny'}, {'id': 5101, 'synset': 'wrymouth.n.01', 'name': 'wrymouth'}, {'id': 5102, 'synset': 'wolffish.n.01', 'name': 'wolffish'}, {'id': 5103, 'synset': 'viviparous_eelpout.n.01', 'name': 'viviparous_eelpout'}, {'id': 5104, 'synset': 'ocean_pout.n.01', 'name': 'ocean_pout'}, {'id': 5105, 'synset': 'sand_lance.n.01', 'name': 'sand_lance'}, {'id': 5106, 'synset': 'dragonet.n.01', 'name': 'dragonet'}, {'id': 5107, 'synset': 'goby.n.01', 'name': 'goby'}, {'id': 5108, 'synset': 'mudskipper.n.01', 'name': 'mudskipper'}, {'id': 5109, 'synset': 'sleeper.n.08', 'name': 'sleeper'}, {'id': 5110, 'synset': 'flathead.n.02', 'name': 'flathead'}, {'id': 5111, 'synset': 'archerfish.n.01', 'name': 'archerfish'}, {'id': 5112, 'synset': 'surgeonfish.n.01', 'name': 'surgeonfish'}, {'id': 5113, 'synset': 'gempylid.n.01', 'name': 'gempylid'}, {'id': 5114, 'synset': 'snake_mackerel.n.01', 'name': 'snake_mackerel'}, {'id': 5115, 'synset': 'escolar.n.01', 'name': 'escolar'}, {'id': 5116, 'synset': 'oilfish.n.01', 'name': 'oilfish'}, {'id': 5117, 'synset': 'cutlassfish.n.01', 'name': 'cutlassfish'}, {'id': 5118, 'synset': 'scombroid.n.01', 'name': 'scombroid'}, {'id': 5119, 'synset': 'mackerel.n.02', 'name': 'mackerel'}, {'id': 5120, 'synset': 'common_mackerel.n.01', 'name': 'common_mackerel'}, {'id': 5121, 'synset': 'spanish_mackerel.n.03', 'name': 'Spanish_mackerel'}, {'id': 5122, 'synset': 'chub_mackerel.n.01', 'name': 'chub_mackerel'}, {'id': 5123, 'synset': 'wahoo.n.03', 'name': 'wahoo'}, {'id': 5124, 'synset': 'spanish_mackerel.n.02', 'name': 'Spanish_mackerel'}, {'id': 5125, 'synset': 'king_mackerel.n.01', 'name': 'king_mackerel'}, {'id': 5126, 'synset': 'scomberomorus_maculatus.n.01', 'name': 'Scomberomorus_maculatus'}, {'id': 5127, 'synset': 'cero.n.01', 'name': 'cero'}, {'id': 5128, 'synset': 'sierra.n.02', 'name': 'sierra'}, {'id': 5129, 'synset': 'tuna.n.03', 'name': 'tuna'}, {'id': 5130, 'synset': 'albacore.n.02', 'name': 'albacore'}, {'id': 5131, 'synset': 'bluefin.n.02', 'name': 'bluefin'}, {'id': 5132, 'synset': 'yellowfin.n.01', 'name': 'yellowfin'}, {'id': 5133, 'synset': 'bonito.n.03', 'name': 'bonito'}, {'id': 5134, 'synset': 'skipjack.n.02', 'name': 'skipjack'}, {'id': 5135, 'synset': 'chile_bonito.n.01', 'name': 'Chile_bonito'}, {'id': 5136, 'synset': 'skipjack.n.01', 'name': 'skipjack'}, {'id': 5137, 'synset': 'bonito.n.02', 'name': 'bonito'}, {'id': 5138, 'synset': 'swordfish.n.02', 'name': 'swordfish'}, {'id': 5139, 'synset': 'sailfish.n.02', 'name': 'sailfish'}, {'id': 5140, 'synset': 'atlantic_sailfish.n.01', 'name': 'Atlantic_sailfish'}, {'id': 5141, 'synset': 'billfish.n.02', 'name': 'billfish'}, {'id': 5142, 'synset': 'marlin.n.01', 'name': 'marlin'}, {'id': 5143, 'synset': 'blue_marlin.n.01', 'name': 'blue_marlin'}, {'id': 5144, 'synset': 'black_marlin.n.01', 'name': 'black_marlin'}, {'id': 5145, 'synset': 'striped_marlin.n.01', 'name': 'striped_marlin'}, {'id': 5146, 'synset': 'white_marlin.n.01', 'name': 'white_marlin'}, {'id': 5147, 'synset': 'spearfish.n.01', 'name': 'spearfish'}, {'id': 5148, 'synset': 'louvar.n.01', 'name': 'louvar'}, {'id': 5149, 'synset': 'dollarfish.n.01', 'name': 'dollarfish'}, {'id': 5150, 'synset': 'palometa.n.01', 'name': 'palometa'}, {'id': 5151, 'synset': 'harvestfish.n.01', 'name': 'harvestfish'}, {'id': 5152, 'synset': 'driftfish.n.01', 'name': 'driftfish'}, {'id': 5153, 'synset': 'barrelfish.n.01', 'name': 'barrelfish'}, {'id': 5154, 'synset': 'clingfish.n.01', 'name': 'clingfish'}, {'id': 5155, 'synset': 'tripletail.n.01', 'name': 'tripletail'}, {'id': 5156, 'synset': 'atlantic_tripletail.n.01', 'name': 'Atlantic_tripletail'}, {'id': 5157, 'synset': 'pacific_tripletail.n.01', 'name': 'Pacific_tripletail'}, {'id': 5158, 'synset': 'mojarra.n.01', 'name': 'mojarra'}, {'id': 5159, 'synset': 'yellowfin_mojarra.n.01', 'name': 'yellowfin_mojarra'}, {'id': 5160, 'synset': 'silver_jenny.n.01', 'name': 'silver_jenny'}, {'id': 5161, 'synset': 'whiting.n.03', 'name': 'whiting'}, {'id': 5162, 'synset': 'ganoid.n.01', 'name': 'ganoid'}, {'id': 5163, 'synset': 'bowfin.n.01', 'name': 'bowfin'}, {'id': 5164, 'synset': 'paddlefish.n.01', 'name': 'paddlefish'}, {'id': 5165, 'synset': 'chinese_paddlefish.n.01', 'name': 'Chinese_paddlefish'}, {'id': 5166, 'synset': 'sturgeon.n.01', 'name': 'sturgeon'}, {'id': 5167, 'synset': 'pacific_sturgeon.n.01', 'name': 'Pacific_sturgeon'}, {'id': 5168, 'synset': 'beluga.n.01', 'name': 'beluga'}, {'id': 5169, 'synset': 'gar.n.01', 'name': 'gar'}, {'id': 5170, 'synset': 'scorpaenoid.n.01', 'name': 'scorpaenoid'}, {'id': 5171, 'synset': 'scorpaenid.n.01', 'name': 'scorpaenid'}, {'id': 5172, 'synset': 'scorpionfish.n.01', 'name': 'scorpionfish'}, {'id': 5173, 'synset': 'plumed_scorpionfish.n.01', 'name': 'plumed_scorpionfish'}, {'id': 5174, 'synset': 'lionfish.n.01', 'name': 'lionfish'}, {'id': 5175, 'synset': 'stonefish.n.01', 'name': 'stonefish'}, {'id': 5176, 'synset': 'rockfish.n.02', 'name': 'rockfish'}, {'id': 5177, 'synset': 'copper_rockfish.n.01', 'name': 'copper_rockfish'}, {'id': 5178, 'synset': 'vermillion_rockfish.n.01', 'name': 'vermillion_rockfish'}, {'id': 5179, 'synset': 'red_rockfish.n.02', 'name': 'red_rockfish'}, {'id': 5180, 'synset': 'rosefish.n.02', 'name': 'rosefish'}, {'id': 5181, 'synset': 'bullhead.n.01', 'name': 'bullhead'}, {'id': 5182, 'synset': "miller's-thumb.n.01", 'name': "miller's-thumb"}, {'id': 5183, 'synset': 'sea_raven.n.01', 'name': 'sea_raven'}, {'id': 5184, 'synset': 'lumpfish.n.01', 'name': 'lumpfish'}, {'id': 5185, 'synset': 'lumpsucker.n.01', 'name': 'lumpsucker'}, {'id': 5186, 'synset': 'pogge.n.01', 'name': 'pogge'}, {'id': 5187, 'synset': 'greenling.n.01', 'name': 'greenling'}, {'id': 5188, 'synset': 'kelp_greenling.n.01', 'name': 'kelp_greenling'}, {'id': 5189, 'synset': 'painted_greenling.n.01', 'name': 'painted_greenling'}, {'id': 5190, 'synset': 'flathead.n.01', 'name': 'flathead'}, {'id': 5191, 'synset': 'gurnard.n.01', 'name': 'gurnard'}, {'id': 5192, 'synset': 'tub_gurnard.n.01', 'name': 'tub_gurnard'}, {'id': 5193, 'synset': 'sea_robin.n.01', 'name': 'sea_robin'}, {'id': 5194, 'synset': 'northern_sea_robin.n.01', 'name': 'northern_sea_robin'}, {'id': 5195, 'synset': 'flying_gurnard.n.01', 'name': 'flying_gurnard'}, {'id': 5196, 'synset': 'plectognath.n.01', 'name': 'plectognath'}, {'id': 5197, 'synset': 'triggerfish.n.01', 'name': 'triggerfish'}, {'id': 5198, 'synset': 'queen_triggerfish.n.01', 'name': 'queen_triggerfish'}, {'id': 5199, 'synset': 'filefish.n.01', 'name': 'filefish'}, {'id': 5200, 'synset': 'leatherjacket.n.01', 'name': 'leatherjacket'}, {'id': 5201, 'synset': 'boxfish.n.01', 'name': 'boxfish'}, {'id': 5202, 'synset': 'cowfish.n.01', 'name': 'cowfish'}, {'id': 5203, 'synset': 'spiny_puffer.n.01', 'name': 'spiny_puffer'}, {'id': 5204, 'synset': 'porcupinefish.n.01', 'name': 'porcupinefish'}, {'id': 5205, 'synset': 'balloonfish.n.01', 'name': 'balloonfish'}, {'id': 5206, 'synset': 'burrfish.n.01', 'name': 'burrfish'}, {'id': 5207, 'synset': 'ocean_sunfish.n.01', 'name': 'ocean_sunfish'}, {'id': 5208, 'synset': 'sharptail_mola.n.01', 'name': 'sharptail_mola'}, {'id': 5209, 'synset': 'flatfish.n.02', 'name': 'flatfish'}, {'id': 5210, 'synset': 'flounder.n.02', 'name': 'flounder'}, {'id': 5211, 'synset': 'righteye_flounder.n.01', 'name': 'righteye_flounder'}, {'id': 5212, 'synset': 'plaice.n.02', 'name': 'plaice'}, {'id': 5213, 'synset': 'european_flatfish.n.01', 'name': 'European_flatfish'}, {'id': 5214, 'synset': 'yellowtail_flounder.n.02', 'name': 'yellowtail_flounder'}, {'id': 5215, 'synset': 'winter_flounder.n.02', 'name': 'winter_flounder'}, {'id': 5216, 'synset': 'lemon_sole.n.05', 'name': 'lemon_sole'}, {'id': 5217, 'synset': 'american_plaice.n.01', 'name': 'American_plaice'}, {'id': 5218, 'synset': 'halibut.n.02', 'name': 'halibut'}, {'id': 5219, 'synset': 'atlantic_halibut.n.01', 'name': 'Atlantic_halibut'}, {'id': 5220, 'synset': 'pacific_halibut.n.01', 'name': 'Pacific_halibut'}, {'id': 5221, 'synset': 'lefteye_flounder.n.01', 'name': 'lefteye_flounder'}, {'id': 5222, 'synset': 'southern_flounder.n.01', 'name': 'southern_flounder'}, {'id': 5223, 'synset': 'summer_flounder.n.01', 'name': 'summer_flounder'}, {'id': 5224, 'synset': 'whiff.n.02', 'name': 'whiff'}, {'id': 5225, 'synset': 'horned_whiff.n.01', 'name': 'horned_whiff'}, {'id': 5226, 'synset': 'sand_dab.n.02', 'name': 'sand_dab'}, {'id': 5227, 'synset': 'windowpane.n.02', 'name': 'windowpane'}, {'id': 5228, 'synset': 'brill.n.01', 'name': 'brill'}, {'id': 5229, 'synset': 'turbot.n.02', 'name': 'turbot'}, {'id': 5230, 'synset': 'tonguefish.n.01', 'name': 'tonguefish'}, {'id': 5231, 'synset': 'sole.n.04', 'name': 'sole'}, {'id': 5232, 'synset': 'european_sole.n.01', 'name': 'European_sole'}, {'id': 5233, 'synset': 'english_sole.n.02', 'name': 'English_sole'}, {'id': 5234, 'synset': 'hogchoker.n.01', 'name': 'hogchoker'}, {'id': 5235, 'synset': 'aba.n.02', 'name': 'aba'}, {'id': 5236, 'synset': 'abacus.n.02', 'name': 'abacus'}, {'id': 5237, 'synset': 'abandoned_ship.n.01', 'name': 'abandoned_ship'}, {'id': 5238, 'synset': 'a_battery.n.01', 'name': 'A_battery'}, {'id': 5239, 'synset': 'abattoir.n.01', 'name': 'abattoir'}, {'id': 5240, 'synset': 'abaya.n.01', 'name': 'abaya'}, {'id': 5241, 'synset': 'abbe_condenser.n.01', 'name': 'Abbe_condenser'}, {'id': 5242, 'synset': 'abbey.n.03', 'name': 'abbey'}, {'id': 5243, 'synset': 'abbey.n.02', 'name': 'abbey'}, {'id': 5244, 'synset': 'abbey.n.01', 'name': 'abbey'}, {'id': 5245, 'synset': 'abney_level.n.01', 'name': 'Abney_level'}, {'id': 5246, 'synset': 'abrader.n.01', 'name': 'abrader'}, {'id': 5247, 'synset': 'abrading_stone.n.01', 'name': 'abrading_stone'}, {'id': 5248, 'synset': 'abutment.n.02', 'name': 'abutment'}, {'id': 5249, 'synset': 'abutment_arch.n.01', 'name': 'abutment_arch'}, {'id': 5250, 'synset': 'academic_costume.n.01', 'name': 'academic_costume'}, {'id': 5251, 'synset': 'academic_gown.n.01', 'name': 'academic_gown'}, {'id': 5252, 'synset': 'accelerator.n.02', 'name': 'accelerator'}, {'id': 5253, 'synset': 'accelerator.n.04', 'name': 'accelerator'}, {'id': 5254, 'synset': 'accelerator.n.01', 'name': 'accelerator'}, {'id': 5255, 'synset': 'accelerometer.n.01', 'name': 'accelerometer'}, {'id': 5256, 'synset': 'accessory.n.01', 'name': 'accessory'}, {'id': 5257, 'synset': 'accommodating_lens_implant.n.01', 'name': 'accommodating_lens_implant'}, {'id': 5258, 'synset': 'accommodation.n.04', 'name': 'accommodation'}, {'id': 5259, 'synset': 'accordion.n.01', 'name': 'accordion'}, {'id': 5260, 'synset': 'acetate_disk.n.01', 'name': 'acetate_disk'}, {'id': 5261, 'synset': 'acetate_rayon.n.01', 'name': 'acetate_rayon'}, {'id': 5262, 'synset': 'achromatic_lens.n.01', 'name': 'achromatic_lens'}, {'id': 5263, 'synset': 'acoustic_delay_line.n.01', 'name': 'acoustic_delay_line'}, {'id': 5264, 'synset': 'acoustic_device.n.01', 'name': 'acoustic_device'}, {'id': 5265, 'synset': 'acoustic_guitar.n.01', 'name': 'acoustic_guitar'}, {'id': 5266, 'synset': 'acoustic_modem.n.01', 'name': 'acoustic_modem'}, {'id': 5267, 'synset': 'acropolis.n.01', 'name': 'acropolis'}, {'id': 5268, 'synset': 'acrylic.n.04', 'name': 'acrylic'}, {'id': 5269, 'synset': 'acrylic.n.03', 'name': 'acrylic'}, {'id': 5270, 'synset': 'actinometer.n.01', 'name': 'actinometer'}, {'id': 5271, 'synset': 'action.n.07', 'name': 'action'}, {'id': 5272, 'synset': 'active_matrix_screen.n.01', 'name': 'active_matrix_screen'}, {'id': 5273, 'synset': 'actuator.n.01', 'name': 'actuator'}, {'id': 5274, 'synset': 'adapter.n.02', 'name': 'adapter'}, {'id': 5275, 'synset': 'adder.n.02', 'name': 'adder'}, {'id': 5276, 'synset': 'adding_machine.n.01', 'name': 'adding_machine'}, {'id': 5277, 'synset': 'addressing_machine.n.01', 'name': 'addressing_machine'}, {'id': 5278, 'synset': 'adhesive_bandage.n.01', 'name': 'adhesive_bandage'}, {'id': 5279, 'synset': 'adit.n.01', 'name': 'adit'}, {'id': 5280, 'synset': 'adjoining_room.n.01', 'name': 'adjoining_room'}, {'id': 5281, 'synset': 'adjustable_wrench.n.01', 'name': 'adjustable_wrench'}, {'id': 5282, 'synset': 'adobe.n.02', 'name': 'adobe'}, {'id': 5283, 'synset': 'adz.n.01', 'name': 'adz'}, {'id': 5284, 'synset': 'aeolian_harp.n.01', 'name': 'aeolian_harp'}, {'id': 5285, 'synset': 'aerator.n.01', 'name': 'aerator'}, {'id': 5286, 'synset': 'aerial_torpedo.n.01', 'name': 'aerial_torpedo'}, {'id': 5287, 'synset': 'aertex.n.01', 'name': 'Aertex'}, {'id': 5288, 'synset': 'afghan.n.01', 'name': 'afghan'}, {'id': 5289, 'synset': 'afro-wig.n.01', 'name': 'Afro-wig'}, {'id': 5290, 'synset': 'afterburner.n.01', 'name': 'afterburner'}, {'id': 5291, 'synset': 'after-shave.n.01', 'name': 'after-shave'}, {'id': 5292, 'synset': 'agateware.n.01', 'name': 'agateware'}, {'id': 5293, 'synset': 'agglomerator.n.01', 'name': 'agglomerator'}, {'id': 5294, 'synset': 'aglet.n.02', 'name': 'aglet'}, {'id': 5295, 'synset': 'aglet.n.01', 'name': 'aglet'}, {'id': 5296, 'synset': 'agora.n.03', 'name': 'agora'}, {'id': 5297, 'synset': 'aigrette.n.01', 'name': 'aigrette'}, {'id': 5298, 'synset': 'aileron.n.01', 'name': 'aileron'}, {'id': 5299, 'synset': 'air_bag.n.01', 'name': 'air_bag'}, {'id': 5300, 'synset': 'airbrake.n.02', 'name': 'airbrake'}, {'id': 5301, 'synset': 'airbrush.n.01', 'name': 'airbrush'}, {'id': 5302, 'synset': 'airbus.n.01', 'name': 'airbus'}, {'id': 5303, 'synset': 'air_compressor.n.01', 'name': 'air_compressor'}, {'id': 5304, 'synset': 'aircraft.n.01', 'name': 'aircraft'}, {'id': 5305, 'synset': 'aircraft_carrier.n.01', 'name': 'aircraft_carrier'}, {'id': 5306, 'synset': 'aircraft_engine.n.01', 'name': 'aircraft_engine'}, {'id': 5307, 'synset': 'air_cushion.n.02', 'name': 'air_cushion'}, {'id': 5308, 'synset': 'airdock.n.01', 'name': 'airdock'}, {'id': 5309, 'synset': 'airfield.n.01', 'name': 'airfield'}, {'id': 5310, 'synset': 'air_filter.n.01', 'name': 'air_filter'}, {'id': 5311, 'synset': 'airfoil.n.01', 'name': 'airfoil'}, {'id': 5312, 'synset': 'airframe.n.01', 'name': 'airframe'}, {'id': 5313, 'synset': 'air_gun.n.01', 'name': 'air_gun'}, {'id': 5314, 'synset': 'air_hammer.n.01', 'name': 'air_hammer'}, {'id': 5315, 'synset': 'air_horn.n.01', 'name': 'air_horn'}, {'id': 5316, 'synset': 'airing_cupboard.n.01', 'name': 'airing_cupboard'}, {'id': 5317, 'synset': 'airliner.n.01', 'name': 'airliner'}, {'id': 5318, 'synset': 'airmailer.n.01', 'name': 'airmailer'}, {'id': 5319, 'synset': 'airplane_propeller.n.01', 'name': 'airplane_propeller'}, {'id': 5320, 'synset': 'airport.n.01', 'name': 'airport'}, {'id': 5321, 'synset': 'air_pump.n.01', 'name': 'air_pump'}, {'id': 5322, 'synset': 'air_search_radar.n.01', 'name': 'air_search_radar'}, {'id': 5323, 'synset': 'airship.n.01', 'name': 'airship'}, {'id': 5324, 'synset': 'air_terminal.n.01', 'name': 'air_terminal'}, {'id': 5325, 'synset': 'air-to-air_missile.n.01', 'name': 'air-to-air_missile'}, {'id': 5326, 'synset': 'air-to-ground_missile.n.01', 'name': 'air-to-ground_missile'}, {'id': 5327, 'synset': 'aisle.n.03', 'name': 'aisle'}, {'id': 5328, 'synset': "aladdin's_lamp.n.01", 'name': "Aladdin's_lamp"}, {'id': 5329, 'synset': 'alarm.n.02', 'name': 'alarm'}, {'id': 5330, 'synset': 'alb.n.01', 'name': 'alb'}, {'id': 5331, 'synset': 'alcazar.n.01', 'name': 'alcazar'}, {'id': 5332, 'synset': 'alcohol_thermometer.n.01', 'name': 'alcohol_thermometer'}, {'id': 5333, 'synset': 'alehouse.n.01', 'name': 'alehouse'}, {'id': 5334, 'synset': 'alembic.n.01', 'name': 'alembic'}, {'id': 5335, 'synset': 'algometer.n.01', 'name': 'algometer'}, {'id': 5336, 'synset': 'alidade.n.02', 'name': 'alidade'}, {'id': 5337, 'synset': 'alidade.n.01', 'name': 'alidade'}, {'id': 5338, 'synset': 'a-line.n.01', 'name': 'A-line'}, {'id': 5339, 'synset': 'allen_screw.n.01', 'name': 'Allen_screw'}, {'id': 5340, 'synset': 'allen_wrench.n.01', 'name': 'Allen_wrench'}, {'id': 5341, 'synset': 'alligator_wrench.n.01', 'name': 'alligator_wrench'}, {'id': 5342, 'synset': 'alms_dish.n.01', 'name': 'alms_dish'}, {'id': 5343, 'synset': 'alpaca.n.02', 'name': 'alpaca'}, {'id': 5344, 'synset': 'alpenstock.n.01', 'name': 'alpenstock'}, {'id': 5345, 'synset': 'altar.n.02', 'name': 'altar'}, {'id': 5346, 'synset': 'altar.n.01', 'name': 'altar'}, {'id': 5347, 'synset': 'altarpiece.n.01', 'name': 'altarpiece'}, {'id': 5348, 'synset': 'altazimuth.n.01', 'name': 'altazimuth'}, {'id': 5349, 'synset': 'alternator.n.01', 'name': 'alternator'}, {'id': 5350, 'synset': 'altimeter.n.01', 'name': 'altimeter'}, {'id': 5351, 'synset': 'amati.n.02', 'name': 'Amati'}, {'id': 5352, 'synset': 'amen_corner.n.01', 'name': 'amen_corner'}, {'id': 5353, 'synset': 'american_organ.n.01', 'name': 'American_organ'}, {'id': 5354, 'synset': 'ammeter.n.01', 'name': 'ammeter'}, {'id': 5355, 'synset': 'ammonia_clock.n.01', 'name': 'ammonia_clock'}, {'id': 5356, 'synset': 'ammunition.n.01', 'name': 'ammunition'}, {'id': 5357, 'synset': 'amphibian.n.02', 'name': 'amphibian'}, {'id': 5358, 'synset': 'amphibian.n.01', 'name': 'amphibian'}, {'id': 5359, 'synset': 'amphitheater.n.02', 'name': 'amphitheater'}, {'id': 5360, 'synset': 'amphitheater.n.01', 'name': 'amphitheater'}, {'id': 5361, 'synset': 'amphora.n.01', 'name': 'amphora'}, {'id': 5362, 'synset': 'ampulla.n.02', 'name': 'ampulla'}, {'id': 5363, 'synset': 'amusement_arcade.n.01', 'name': 'amusement_arcade'}, {'id': 5364, 'synset': 'analog_clock.n.01', 'name': 'analog_clock'}, {'id': 5365, 'synset': 'analog_computer.n.01', 'name': 'analog_computer'}, {'id': 5366, 'synset': 'analog_watch.n.01', 'name': 'analog_watch'}, {'id': 5367, 'synset': 'analytical_balance.n.01', 'name': 'analytical_balance'}, {'id': 5368, 'synset': 'analyzer.n.01', 'name': 'analyzer'}, {'id': 5369, 'synset': 'anamorphosis.n.02', 'name': 'anamorphosis'}, {'id': 5370, 'synset': 'anastigmat.n.01', 'name': 'anastigmat'}, {'id': 5371, 'synset': 'anchor.n.01', 'name': 'anchor'}, {'id': 5372, 'synset': 'anchor_chain.n.01', 'name': 'anchor_chain'}, {'id': 5373, 'synset': 'anchor_light.n.01', 'name': 'anchor_light'}, {'id': 5374, 'synset': 'and_circuit.n.01', 'name': 'AND_circuit'}, {'id': 5375, 'synset': 'andiron.n.01', 'name': 'andiron'}, {'id': 5376, 'synset': 'android.n.01', 'name': 'android'}, {'id': 5377, 'synset': 'anechoic_chamber.n.01', 'name': 'anechoic_chamber'}, {'id': 5378, 'synset': 'anemometer.n.01', 'name': 'anemometer'}, {'id': 5379, 'synset': 'aneroid_barometer.n.01', 'name': 'aneroid_barometer'}, {'id': 5380, 'synset': 'angiocardiogram.n.01', 'name': 'angiocardiogram'}, {'id': 5381, 'synset': 'angioscope.n.01', 'name': 'angioscope'}, {'id': 5382, 'synset': 'angle_bracket.n.02', 'name': 'angle_bracket'}, {'id': 5383, 'synset': 'angledozer.n.01', 'name': 'angledozer'}, {'id': 5384, 'synset': 'ankle_brace.n.01', 'name': 'ankle_brace'}, {'id': 5385, 'synset': 'anklet.n.02', 'name': 'anklet'}, {'id': 5386, 'synset': 'anklet.n.01', 'name': 'anklet'}, {'id': 5387, 'synset': 'ankus.n.01', 'name': 'ankus'}, {'id': 5388, 'synset': 'anode.n.01', 'name': 'anode'}, {'id': 5389, 'synset': 'anode.n.02', 'name': 'anode'}, {'id': 5390, 'synset': 'answering_machine.n.01', 'name': 'answering_machine'}, {'id': 5391, 'synset': 'anteroom.n.01', 'name': 'anteroom'}, {'id': 5392, 'synset': 'antiaircraft.n.01', 'name': 'antiaircraft'}, {'id': 5393, 'synset': 'antiballistic_missile.n.01', 'name': 'antiballistic_missile'}, {'id': 5394, 'synset': 'antifouling_paint.n.01', 'name': 'antifouling_paint'}, {'id': 5395, 'synset': 'anti-g_suit.n.01', 'name': 'anti-G_suit'}, {'id': 5396, 'synset': 'antimacassar.n.01', 'name': 'antimacassar'}, {'id': 5397, 'synset': 'antiperspirant.n.01', 'name': 'antiperspirant'}, {'id': 5398, 'synset': 'anti-submarine_rocket.n.01', 'name': 'anti-submarine_rocket'}, {'id': 5399, 'synset': 'anvil.n.01', 'name': 'anvil'}, {'id': 5400, 'synset': 'ao_dai.n.01', 'name': 'ao_dai'}, {'id': 5401, 'synset': 'apadana.n.01', 'name': 'apadana'}, {'id': 5402, 'synset': 'apartment.n.01', 'name': 'apartment'}, {'id': 5403, 'synset': 'apartment_building.n.01', 'name': 'apartment_building'}, {'id': 5404, 'synset': 'aperture.n.03', 'name': 'aperture'}, {'id': 5405, 'synset': 'aperture.n.01', 'name': 'aperture'}, {'id': 5406, 'synset': 'apiary.n.01', 'name': 'apiary'}, {'id': 5407, 'synset': 'apparatus.n.01', 'name': 'apparatus'}, {'id': 5408, 'synset': 'apparel.n.01', 'name': 'apparel'}, {'id': 5409, 'synset': 'applecart.n.02', 'name': 'applecart'}, {'id': 5410, 'synset': 'appliance.n.02', 'name': 'appliance'}, {'id': 5411, 'synset': 'appliance.n.01', 'name': 'appliance'}, {'id': 5412, 'synset': 'applicator.n.01', 'name': 'applicator'}, {'id': 5413, 'synset': 'appointment.n.03', 'name': 'appointment'}, {'id': 5414, 'synset': 'apron_string.n.01', 'name': 'apron_string'}, {'id': 5415, 'synset': 'apse.n.01', 'name': 'apse'}, {'id': 5416, 'synset': 'aqualung.n.01', 'name': 'aqualung'}, {'id': 5417, 'synset': 'aquaplane.n.01', 'name': 'aquaplane'}, {'id': 5418, 'synset': 'arabesque.n.02', 'name': 'arabesque'}, {'id': 5419, 'synset': 'arbor.n.03', 'name': 'arbor'}, {'id': 5420, 'synset': 'arcade.n.02', 'name': 'arcade'}, {'id': 5421, 'synset': 'arch.n.04', 'name': 'arch'}, {'id': 5422, 'synset': 'architecture.n.01', 'name': 'architecture'}, {'id': 5423, 'synset': 'architrave.n.02', 'name': 'architrave'}, {'id': 5424, 'synset': 'arch_support.n.01', 'name': 'arch_support'}, {'id': 5425, 'synset': 'arc_lamp.n.01', 'name': 'arc_lamp'}, {'id': 5426, 'synset': 'area.n.05', 'name': 'area'}, {'id': 5427, 'synset': 'areaway.n.01', 'name': 'areaway'}, {'id': 5428, 'synset': 'argyle.n.03', 'name': 'argyle'}, {'id': 5429, 'synset': 'ark.n.02', 'name': 'ark'}, {'id': 5430, 'synset': 'arm.n.04', 'name': 'arm'}, {'id': 5431, 'synset': 'armament.n.01', 'name': 'armament'}, {'id': 5432, 'synset': 'armature.n.01', 'name': 'armature'}, {'id': 5433, 'synset': 'armet.n.01', 'name': 'armet'}, {'id': 5434, 'synset': 'arm_guard.n.01', 'name': 'arm_guard'}, {'id': 5435, 'synset': 'armhole.n.01', 'name': 'armhole'}, {'id': 5436, 'synset': 'armilla.n.02', 'name': 'armilla'}, {'id': 5437, 'synset': 'armlet.n.01', 'name': 'armlet'}, {'id': 5438, 'synset': 'armored_car.n.02', 'name': 'armored_car'}, {'id': 5439, 'synset': 'armored_car.n.01', 'name': 'armored_car'}, {'id': 5440, 'synset': 'armored_personnel_carrier.n.01', 'name': 'armored_personnel_carrier'}, {'id': 5441, 'synset': 'armored_vehicle.n.01', 'name': 'armored_vehicle'}, {'id': 5442, 'synset': 'armor_plate.n.01', 'name': 'armor_plate'}, {'id': 5443, 'synset': 'armory.n.04', 'name': 'armory'}, {'id': 5444, 'synset': 'armrest.n.01', 'name': 'armrest'}, {'id': 5445, 'synset': 'arquebus.n.01', 'name': 'arquebus'}, {'id': 5446, 'synset': 'array.n.04', 'name': 'array'}, {'id': 5447, 'synset': 'array.n.03', 'name': 'array'}, {'id': 5448, 'synset': 'arrester.n.01', 'name': 'arrester'}, {'id': 5449, 'synset': 'arrow.n.02', 'name': 'arrow'}, {'id': 5450, 'synset': 'arsenal.n.01', 'name': 'arsenal'}, {'id': 5451, 'synset': 'arterial_road.n.01', 'name': 'arterial_road'}, {'id': 5452, 'synset': 'arthrogram.n.01', 'name': 'arthrogram'}, {'id': 5453, 'synset': 'arthroscope.n.01', 'name': 'arthroscope'}, {'id': 5454, 'synset': 'artificial_heart.n.01', 'name': 'artificial_heart'}, {'id': 5455, 'synset': 'artificial_horizon.n.01', 'name': 'artificial_horizon'}, {'id': 5456, 'synset': 'artificial_joint.n.01', 'name': 'artificial_joint'}, {'id': 5457, 'synset': 'artificial_kidney.n.01', 'name': 'artificial_kidney'}, {'id': 5458, 'synset': 'artificial_skin.n.01', 'name': 'artificial_skin'}, {'id': 5459, 'synset': 'artillery.n.01', 'name': 'artillery'}, {'id': 5460, 'synset': 'artillery_shell.n.01', 'name': 'artillery_shell'}, {'id': 5461, 'synset': "artist's_loft.n.01", 'name': "artist's_loft"}, {'id': 5462, 'synset': 'art_school.n.01', 'name': 'art_school'}, {'id': 5463, 'synset': 'ascot.n.01', 'name': 'ascot'}, {'id': 5464, 'synset': 'ash-pan.n.01', 'name': 'ash-pan'}, {'id': 5465, 'synset': 'aspergill.n.01', 'name': 'aspergill'}, {'id': 5466, 'synset': 'aspersorium.n.01', 'name': 'aspersorium'}, {'id': 5467, 'synset': 'aspirator.n.01', 'name': 'aspirator'}, {'id': 5468, 'synset': 'aspirin_powder.n.01', 'name': 'aspirin_powder'}, {'id': 5469, 'synset': 'assault_gun.n.02', 'name': 'assault_gun'}, {'id': 5470, 'synset': 'assault_rifle.n.01', 'name': 'assault_rifle'}, {'id': 5471, 'synset': 'assegai.n.01', 'name': 'assegai'}, {'id': 5472, 'synset': 'assembly.n.01', 'name': 'assembly'}, {'id': 5473, 'synset': 'assembly.n.05', 'name': 'assembly'}, {'id': 5474, 'synset': 'assembly_hall.n.01', 'name': 'assembly_hall'}, {'id': 5475, 'synset': 'assembly_plant.n.01', 'name': 'assembly_plant'}, {'id': 5476, 'synset': 'astatic_coils.n.01', 'name': 'astatic_coils'}, {'id': 5477, 'synset': 'astatic_galvanometer.n.01', 'name': 'astatic_galvanometer'}, {'id': 5478, 'synset': 'astrodome.n.01', 'name': 'astrodome'}, {'id': 5479, 'synset': 'astrolabe.n.01', 'name': 'astrolabe'}, {'id': 5480, 'synset': 'astronomical_telescope.n.01', 'name': 'astronomical_telescope'}, {'id': 5481, 'synset': 'astronomy_satellite.n.01', 'name': 'astronomy_satellite'}, {'id': 5482, 'synset': 'athenaeum.n.02', 'name': 'athenaeum'}, {'id': 5483, 'synset': 'athletic_sock.n.01', 'name': 'athletic_sock'}, {'id': 5484, 'synset': 'athletic_supporter.n.01', 'name': 'athletic_supporter'}, {'id': 5485, 'synset': 'atlas.n.04', 'name': 'atlas'}, {'id': 5486, 'synset': 'atmometer.n.01', 'name': 'atmometer'}, {'id': 5487, 'synset': 'atom_bomb.n.01', 'name': 'atom_bomb'}, {'id': 5488, 'synset': 'atomic_clock.n.01', 'name': 'atomic_clock'}, {'id': 5489, 'synset': 'atomic_pile.n.01', 'name': 'atomic_pile'}, {'id': 5490, 'synset': 'atrium.n.02', 'name': 'atrium'}, {'id': 5491, 'synset': 'attache_case.n.01', 'name': 'attache_case'}, {'id': 5492, 'synset': 'attachment.n.04', 'name': 'attachment'}, {'id': 5493, 'synset': 'attack_submarine.n.01', 'name': 'attack_submarine'}, {'id': 5494, 'synset': 'attenuator.n.01', 'name': 'attenuator'}, {'id': 5495, 'synset': 'attic.n.04', 'name': 'attic'}, {'id': 5496, 'synset': 'attic_fan.n.01', 'name': 'attic_fan'}, {'id': 5497, 'synset': 'attire.n.01', 'name': 'attire'}, {'id': 5498, 'synset': 'audio_amplifier.n.01', 'name': 'audio_amplifier'}, {'id': 5499, 'synset': 'audiocassette.n.01', 'name': 'audiocassette'}, {'id': 5500, 'synset': 'audio_cd.n.01', 'name': 'audio_CD'}, {'id': 5501, 'synset': 'audiometer.n.01', 'name': 'audiometer'}, {'id': 5502, 'synset': 'audio_system.n.01', 'name': 'audio_system'}, {'id': 5503, 'synset': 'audiotape.n.02', 'name': 'audiotape'}, {'id': 5504, 'synset': 'audiotape.n.01', 'name': 'audiotape'}, {'id': 5505, 'synset': 'audiovisual.n.01', 'name': 'audiovisual'}, {'id': 5506, 'synset': 'auditorium.n.01', 'name': 'auditorium'}, {'id': 5507, 'synset': 'auger.n.02', 'name': 'auger'}, {'id': 5508, 'synset': 'autobahn.n.01', 'name': 'autobahn'}, {'id': 5509, 'synset': 'autoclave.n.01', 'name': 'autoclave'}, {'id': 5510, 'synset': 'autofocus.n.01', 'name': 'autofocus'}, {'id': 5511, 'synset': 'autogiro.n.01', 'name': 'autogiro'}, {'id': 5512, 'synset': 'autoinjector.n.01', 'name': 'autoinjector'}, {'id': 5513, 'synset': 'autoloader.n.01', 'name': 'autoloader'}, {'id': 5514, 'synset': 'automat.n.02', 'name': 'automat'}, {'id': 5515, 'synset': 'automat.n.01', 'name': 'automat'}, {'id': 5516, 'synset': 'automatic_choke.n.01', 'name': 'automatic_choke'}, {'id': 5517, 'synset': 'automatic_firearm.n.01', 'name': 'automatic_firearm'}, {'id': 5518, 'synset': 'automatic_pistol.n.01', 'name': 'automatic_pistol'}, {'id': 5519, 'synset': 'automatic_rifle.n.01', 'name': 'automatic_rifle'}, {'id': 5520, 'synset': 'automatic_transmission.n.01', 'name': 'automatic_transmission'}, {'id': 5521, 'synset': 'automation.n.03', 'name': 'automation'}, {'id': 5522, 'synset': 'automaton.n.02', 'name': 'automaton'}, {'id': 5523, 'synset': 'automobile_engine.n.01', 'name': 'automobile_engine'}, {'id': 5524, 'synset': 'automobile_factory.n.01', 'name': 'automobile_factory'}, {'id': 5525, 'synset': 'automobile_horn.n.01', 'name': 'automobile_horn'}, {'id': 5526, 'synset': 'autopilot.n.02', 'name': 'autopilot'}, {'id': 5527, 'synset': 'autoradiograph.n.01', 'name': 'autoradiograph'}, {'id': 5528, 'synset': 'autostrada.n.01', 'name': 'autostrada'}, {'id': 5529, 'synset': 'auxiliary_boiler.n.01', 'name': 'auxiliary_boiler'}, {'id': 5530, 'synset': 'auxiliary_engine.n.01', 'name': 'auxiliary_engine'}, {'id': 5531, 'synset': 'auxiliary_pump.n.01', 'name': 'auxiliary_pump'}, {'id': 5532, 'synset': 'auxiliary_research_submarine.n.01', 'name': 'auxiliary_research_submarine'}, {'id': 5533, 'synset': 'auxiliary_storage.n.01', 'name': 'auxiliary_storage'}, {'id': 5534, 'synset': 'aviary.n.01', 'name': 'aviary'}, {'id': 5535, 'synset': 'awl.n.01', 'name': 'awl'}, {'id': 5536, 'synset': 'ax_handle.n.01', 'name': 'ax_handle'}, {'id': 5537, 'synset': 'ax_head.n.01', 'name': 'ax_head'}, {'id': 5538, 'synset': 'axis.n.06', 'name': 'axis'}, {'id': 5539, 'synset': 'axle.n.01', 'name': 'axle'}, {'id': 5540, 'synset': 'axle_bar.n.01', 'name': 'axle_bar'}, {'id': 5541, 'synset': 'axletree.n.01', 'name': 'axletree'}, {'id': 5542, 'synset': 'babushka.n.01', 'name': 'babushka'}, {'id': 5543, 'synset': 'baby_bed.n.01', 'name': 'baby_bed'}, {'id': 5544, 'synset': 'baby_grand.n.01', 'name': 'baby_grand'}, {'id': 5545, 'synset': 'baby_powder.n.01', 'name': 'baby_powder'}, {'id': 5546, 'synset': 'baby_shoe.n.01', 'name': 'baby_shoe'}, {'id': 5547, 'synset': 'back.n.08', 'name': 'back'}, {'id': 5548, 'synset': 'back.n.07', 'name': 'back'}, {'id': 5549, 'synset': 'backbench.n.01', 'name': 'backbench'}, {'id': 5550, 'synset': 'backboard.n.02', 'name': 'backboard'}, {'id': 5551, 'synset': 'backbone.n.05', 'name': 'backbone'}, {'id': 5552, 'synset': 'back_brace.n.01', 'name': 'back_brace'}, {'id': 5553, 'synset': 'backgammon_board.n.01', 'name': 'backgammon_board'}, {'id': 5554, 'synset': 'background.n.07', 'name': 'background'}, {'id': 5555, 'synset': 'backhoe.n.01', 'name': 'backhoe'}, {'id': 5556, 'synset': 'backlighting.n.01', 'name': 'backlighting'}, {'id': 5557, 'synset': 'backpacking_tent.n.01', 'name': 'backpacking_tent'}, {'id': 5558, 'synset': 'backplate.n.01', 'name': 'backplate'}, {'id': 5559, 'synset': 'back_porch.n.01', 'name': 'back_porch'}, {'id': 5560, 'synset': 'backsaw.n.01', 'name': 'backsaw'}, {'id': 5561, 'synset': 'backscratcher.n.02', 'name': 'backscratcher'}, {'id': 5562, 'synset': 'backseat.n.02', 'name': 'backseat'}, {'id': 5563, 'synset': 'backspace_key.n.01', 'name': 'backspace_key'}, {'id': 5564, 'synset': 'backstairs.n.01', 'name': 'backstairs'}, {'id': 5565, 'synset': 'backstay.n.01', 'name': 'backstay'}, {'id': 5566, 'synset': 'backstop.n.02', 'name': 'backstop'}, {'id': 5567, 'synset': 'backsword.n.02', 'name': 'backsword'}, {'id': 5568, 'synset': 'backup_system.n.01', 'name': 'backup_system'}, {'id': 5569, 'synset': 'badminton_court.n.01', 'name': 'badminton_court'}, {'id': 5570, 'synset': 'badminton_equipment.n.01', 'name': 'badminton_equipment'}, {'id': 5571, 'synset': 'badminton_racket.n.01', 'name': 'badminton_racket'}, {'id': 5572, 'synset': 'bag.n.01', 'name': 'bag'}, {'id': 5573, 'synset': 'baggage.n.01', 'name': 'baggage'}, {'id': 5574, 'synset': 'baggage.n.03', 'name': 'baggage'}, {'id': 5575, 'synset': 'baggage_car.n.01', 'name': 'baggage_car'}, {'id': 5576, 'synset': 'baggage_claim.n.01', 'name': 'baggage_claim'}, {'id': 5577, 'synset': 'bailey.n.04', 'name': 'bailey'}, {'id': 5578, 'synset': 'bailey.n.03', 'name': 'bailey'}, {'id': 5579, 'synset': 'bailey_bridge.n.01', 'name': 'Bailey_bridge'}, {'id': 5580, 'synset': 'bain-marie.n.01', 'name': 'bain-marie'}, {'id': 5581, 'synset': 'baize.n.01', 'name': 'baize'}, {'id': 5582, 'synset': 'bakery.n.01', 'name': 'bakery'}, {'id': 5583, 'synset': 'balaclava.n.01', 'name': 'balaclava'}, {'id': 5584, 'synset': 'balalaika.n.01', 'name': 'balalaika'}, {'id': 5585, 'synset': 'balance.n.12', 'name': 'balance'}, {'id': 5586, 'synset': 'balance_beam.n.01', 'name': 'balance_beam'}, {'id': 5587, 'synset': 'balance_wheel.n.01', 'name': 'balance_wheel'}, {'id': 5588, 'synset': 'balbriggan.n.01', 'name': 'balbriggan'}, {'id': 5589, 'synset': 'balcony.n.02', 'name': 'balcony'}, {'id': 5590, 'synset': 'balcony.n.01', 'name': 'balcony'}, {'id': 5591, 'synset': 'baldachin.n.01', 'name': 'baldachin'}, {'id': 5592, 'synset': 'baldric.n.01', 'name': 'baldric'}, {'id': 5593, 'synset': 'bale.n.01', 'name': 'bale'}, {'id': 5594, 'synset': 'baling_wire.n.01', 'name': 'baling_wire'}, {'id': 5595, 'synset': 'ball.n.01', 'name': 'ball'}, {'id': 5596, 'synset': 'ball_and_chain.n.01', 'name': 'ball_and_chain'}, {'id': 5597, 'synset': 'ball-and-socket_joint.n.02', 'name': 'ball-and-socket_joint'}, {'id': 5598, 'synset': 'ballast.n.05', 'name': 'ballast'}, {'id': 5599, 'synset': 'ball_bearing.n.01', 'name': 'ball_bearing'}, {'id': 5600, 'synset': 'ball_cartridge.n.01', 'name': 'ball_cartridge'}, {'id': 5601, 'synset': 'ballcock.n.01', 'name': 'ballcock'}, {'id': 5602, 'synset': 'balldress.n.01', 'name': 'balldress'}, {'id': 5603, 'synset': 'ball_gown.n.01', 'name': 'ball_gown'}, {'id': 5604, 'synset': 'ballistic_galvanometer.n.01', 'name': 'ballistic_galvanometer'}, {'id': 5605, 'synset': 'ballistic_missile.n.01', 'name': 'ballistic_missile'}, {'id': 5606, 'synset': 'ballistic_pendulum.n.01', 'name': 'ballistic_pendulum'}, {'id': 5607, 'synset': 'ballistocardiograph.n.01', 'name': 'ballistocardiograph'}, {'id': 5608, 'synset': 'balloon_bomb.n.01', 'name': 'balloon_bomb'}, {'id': 5609, 'synset': 'balloon_sail.n.01', 'name': 'balloon_sail'}, {'id': 5610, 'synset': 'ballot_box.n.01', 'name': 'ballot_box'}, {'id': 5611, 'synset': 'ballpark.n.01', 'name': 'ballpark'}, {'id': 5612, 'synset': 'ball-peen_hammer.n.01', 'name': 'ball-peen_hammer'}, {'id': 5613, 'synset': 'ballpoint.n.01', 'name': 'ballpoint'}, {'id': 5614, 'synset': 'ballroom.n.01', 'name': 'ballroom'}, {'id': 5615, 'synset': 'ball_valve.n.01', 'name': 'ball_valve'}, {'id': 5616, 'synset': 'balsa_raft.n.01', 'name': 'balsa_raft'}, {'id': 5617, 'synset': 'baluster.n.01', 'name': 'baluster'}, {'id': 5618, 'synset': 'banana_boat.n.01', 'name': 'banana_boat'}, {'id': 5619, 'synset': 'band.n.13', 'name': 'band'}, {'id': 5620, 'synset': 'bandbox.n.01', 'name': 'bandbox'}, {'id': 5621, 'synset': 'banderilla.n.01', 'name': 'banderilla'}, {'id': 5622, 'synset': 'bandoleer.n.01', 'name': 'bandoleer'}, {'id': 5623, 'synset': 'bandoneon.n.01', 'name': 'bandoneon'}, {'id': 5624, 'synset': 'bandsaw.n.01', 'name': 'bandsaw'}, {'id': 5625, 'synset': 'bandwagon.n.02', 'name': 'bandwagon'}, {'id': 5626, 'synset': 'bangalore_torpedo.n.01', 'name': 'bangalore_torpedo'}, {'id': 5627, 'synset': 'bangle.n.02', 'name': 'bangle'}, {'id': 5628, 'synset': 'bannister.n.02', 'name': 'bannister'}, {'id': 5629, 'synset': 'banquette.n.01', 'name': 'banquette'}, {'id': 5630, 'synset': 'banyan.n.02', 'name': 'banyan'}, {'id': 5631, 'synset': 'baptismal_font.n.01', 'name': 'baptismal_font'}, {'id': 5632, 'synset': 'bar.n.03', 'name': 'bar'}, {'id': 5633, 'synset': 'bar.n.02', 'name': 'bar'}, {'id': 5634, 'synset': 'barbecue.n.03', 'name': 'barbecue'}, {'id': 5635, 'synset': 'barbed_wire.n.01', 'name': 'barbed_wire'}, {'id': 5636, 'synset': 'barber_chair.n.01', 'name': 'barber_chair'}, {'id': 5637, 'synset': 'barbershop.n.01', 'name': 'barbershop'}, {'id': 5638, 'synset': 'barbette_carriage.n.01', 'name': 'barbette_carriage'}, {'id': 5639, 'synset': 'barbican.n.01', 'name': 'barbican'}, {'id': 5640, 'synset': 'bar_bit.n.01', 'name': 'bar_bit'}, {'id': 5641, 'synset': 'bareboat.n.01', 'name': 'bareboat'}, {'id': 5642, 'synset': 'barge_pole.n.01', 'name': 'barge_pole'}, {'id': 5643, 'synset': 'baritone.n.03', 'name': 'baritone'}, {'id': 5644, 'synset': 'bark.n.03', 'name': 'bark'}, {'id': 5645, 'synset': 'bar_magnet.n.01', 'name': 'bar_magnet'}, {'id': 5646, 'synset': 'bar_mask.n.01', 'name': 'bar_mask'}, {'id': 5647, 'synset': 'barn.n.01', 'name': 'barn'}, {'id': 5648, 'synset': 'barndoor.n.01', 'name': 'barndoor'}, {'id': 5649, 'synset': 'barn_door.n.01', 'name': 'barn_door'}, {'id': 5650, 'synset': 'barnyard.n.01', 'name': 'barnyard'}, {'id': 5651, 'synset': 'barograph.n.01', 'name': 'barograph'}, {'id': 5652, 'synset': 'barometer.n.01', 'name': 'barometer'}, {'id': 5653, 'synset': 'barong.n.01', 'name': 'barong'}, {'id': 5654, 'synset': 'barouche.n.01', 'name': 'barouche'}, {'id': 5655, 'synset': 'bar_printer.n.01', 'name': 'bar_printer'}, {'id': 5656, 'synset': 'barrack.n.01', 'name': 'barrack'}, {'id': 5657, 'synset': 'barrage_balloon.n.01', 'name': 'barrage_balloon'}, {'id': 5658, 'synset': 'barrel.n.01', 'name': 'barrel'}, {'id': 5659, 'synset': 'barrelhouse.n.01', 'name': 'barrelhouse'}, {'id': 5660, 'synset': 'barrel_knot.n.01', 'name': 'barrel_knot'}, {'id': 5661, 'synset': 'barrel_organ.n.01', 'name': 'barrel_organ'}, {'id': 5662, 'synset': 'barrel_vault.n.01', 'name': 'barrel_vault'}, {'id': 5663, 'synset': 'barricade.n.02', 'name': 'barricade'}, {'id': 5664, 'synset': 'barrier.n.01', 'name': 'barrier'}, {'id': 5665, 'synset': 'barroom.n.01', 'name': 'barroom'}, {'id': 5666, 'synset': 'bascule.n.01', 'name': 'bascule'}, {'id': 5667, 'synset': 'base.n.08', 'name': 'base'}, {'id': 5668, 'synset': 'baseball_equipment.n.01', 'name': 'baseball_equipment'}, {'id': 5669, 'synset': 'basement.n.01', 'name': 'basement'}, {'id': 5670, 'synset': 'basement.n.02', 'name': 'basement'}, {'id': 5671, 'synset': 'basic_point_defense_missile_system.n.01', 'name': 'basic_point_defense_missile_system'}, {'id': 5672, 'synset': 'basilica.n.02', 'name': 'basilica'}, {'id': 5673, 'synset': 'basilica.n.01', 'name': 'basilica'}, {'id': 5674, 'synset': 'basilisk.n.02', 'name': 'basilisk'}, {'id': 5675, 'synset': 'basin.n.01', 'name': 'basin'}, {'id': 5676, 'synset': 'basinet.n.01', 'name': 'basinet'}, {'id': 5677, 'synset': 'basket.n.03', 'name': 'basket'}, {'id': 5678, 'synset': 'basketball_court.n.01', 'name': 'basketball_court'}, {'id': 5679, 'synset': 'basketball_equipment.n.01', 'name': 'basketball_equipment'}, {'id': 5680, 'synset': 'basket_weave.n.01', 'name': 'basket_weave'}, {'id': 5681, 'synset': 'bass.n.07', 'name': 'bass'}, {'id': 5682, 'synset': 'bass_clarinet.n.01', 'name': 'bass_clarinet'}, {'id': 5683, 'synset': 'bass_drum.n.01', 'name': 'bass_drum'}, {'id': 5684, 'synset': 'basset_horn.n.01', 'name': 'basset_horn'}, {'id': 5685, 'synset': 'bass_fiddle.n.01', 'name': 'bass_fiddle'}, {'id': 5686, 'synset': 'bass_guitar.n.01', 'name': 'bass_guitar'}, {'id': 5687, 'synset': 'bassinet.n.01', 'name': 'bassinet'}, {'id': 5688, 'synset': 'bassinet.n.02', 'name': 'bassinet'}, {'id': 5689, 'synset': 'bassoon.n.01', 'name': 'bassoon'}, {'id': 5690, 'synset': 'baster.n.03', 'name': 'baster'}, {'id': 5691, 'synset': 'bastinado.n.01', 'name': 'bastinado'}, {'id': 5692, 'synset': 'bastion.n.03', 'name': 'bastion'}, {'id': 5693, 'synset': 'bastion.n.02', 'name': 'bastion'}, {'id': 5694, 'synset': 'bat.n.05', 'name': 'bat'}, {'id': 5695, 'synset': 'bath.n.01', 'name': 'bath'}, {'id': 5696, 'synset': 'bath_chair.n.01', 'name': 'bath_chair'}, {'id': 5697, 'synset': 'bathhouse.n.02', 'name': 'bathhouse'}, {'id': 5698, 'synset': 'bathhouse.n.01', 'name': 'bathhouse'}, {'id': 5699, 'synset': 'bathing_cap.n.01', 'name': 'bathing_cap'}, {'id': 5700, 'synset': 'bath_oil.n.01', 'name': 'bath_oil'}, {'id': 5701, 'synset': 'bathroom.n.01', 'name': 'bathroom'}, {'id': 5702, 'synset': 'bath_salts.n.01', 'name': 'bath_salts'}, {'id': 5703, 'synset': 'bathyscaphe.n.01', 'name': 'bathyscaphe'}, {'id': 5704, 'synset': 'bathysphere.n.01', 'name': 'bathysphere'}, {'id': 5705, 'synset': 'batik.n.01', 'name': 'batik'}, {'id': 5706, 'synset': 'batiste.n.01', 'name': 'batiste'}, {'id': 5707, 'synset': 'baton.n.01', 'name': 'baton'}, {'id': 5708, 'synset': 'baton.n.05', 'name': 'baton'}, {'id': 5709, 'synset': 'baton.n.04', 'name': 'baton'}, {'id': 5710, 'synset': 'baton.n.03', 'name': 'baton'}, {'id': 5711, 'synset': 'battering_ram.n.01', 'name': 'battering_ram'}, {'id': 5712, 'synset': "batter's_box.n.01", 'name': "batter's_box"}, {'id': 5713, 'synset': 'battery.n.05', 'name': 'battery'}, {'id': 5714, 'synset': 'batting_cage.n.01', 'name': 'batting_cage'}, {'id': 5715, 'synset': 'batting_glove.n.01', 'name': 'batting_glove'}, {'id': 5716, 'synset': 'batting_helmet.n.01', 'name': 'batting_helmet'}, {'id': 5717, 'synset': 'battle-ax.n.01', 'name': 'battle-ax'}, {'id': 5718, 'synset': 'battle_cruiser.n.01', 'name': 'battle_cruiser'}, {'id': 5719, 'synset': 'battle_dress.n.01', 'name': 'battle_dress'}, {'id': 5720, 'synset': 'battlement.n.01', 'name': 'battlement'}, {'id': 5721, 'synset': 'battleship.n.01', 'name': 'battleship'}, {'id': 5722, 'synset': 'battle_sight.n.01', 'name': 'battle_sight'}, {'id': 5723, 'synset': 'bay.n.05', 'name': 'bay'}, {'id': 5724, 'synset': 'bay.n.04', 'name': 'bay'}, {'id': 5725, 'synset': 'bayonet.n.01', 'name': 'bayonet'}, {'id': 5726, 'synset': 'bay_rum.n.01', 'name': 'bay_rum'}, {'id': 5727, 'synset': 'bay_window.n.02', 'name': 'bay_window'}, {'id': 5728, 'synset': 'bazaar.n.01', 'name': 'bazaar'}, {'id': 5729, 'synset': 'bazaar.n.02', 'name': 'bazaar'}, {'id': 5730, 'synset': 'bazooka.n.01', 'name': 'bazooka'}, {'id': 5731, 'synset': 'b_battery.n.01', 'name': 'B_battery'}, {'id': 5732, 'synset': 'bb_gun.n.01', 'name': 'BB_gun'}, {'id': 5733, 'synset': 'beach_house.n.01', 'name': 'beach_house'}, {'id': 5734, 'synset': 'beach_towel.n.01', 'name': 'beach_towel'}, {'id': 5735, 'synset': 'beach_wagon.n.01', 'name': 'beach_wagon'}, {'id': 5736, 'synset': 'beachwear.n.01', 'name': 'beachwear'}, {'id': 5737, 'synset': 'beacon.n.03', 'name': 'beacon'}, {'id': 5738, 'synset': 'beading_plane.n.01', 'name': 'beading_plane'}, {'id': 5739, 'synset': 'beaker.n.02', 'name': 'beaker'}, {'id': 5740, 'synset': 'beaker.n.01', 'name': 'beaker'}, {'id': 5741, 'synset': 'beam.n.02', 'name': 'beam'}, {'id': 5742, 'synset': 'beam_balance.n.01', 'name': 'beam_balance'}, {'id': 5743, 'synset': 'bearing.n.06', 'name': 'bearing'}, {'id': 5744, 'synset': 'bearing_rein.n.01', 'name': 'bearing_rein'}, {'id': 5745, 'synset': 'bearing_wall.n.01', 'name': 'bearing_wall'}, {'id': 5746, 'synset': 'bearskin.n.02', 'name': 'bearskin'}, {'id': 5747, 'synset': 'beater.n.02', 'name': 'beater'}, {'id': 5748, 'synset': 'beating-reed_instrument.n.01', 'name': 'beating-reed_instrument'}, {'id': 5749, 'synset': 'beaver.n.06', 'name': 'beaver'}, {'id': 5750, 'synset': 'beaver.n.05', 'name': 'beaver'}, {'id': 5751, 'synset': 'beckman_thermometer.n.01', 'name': 'Beckman_thermometer'}, {'id': 5752, 'synset': 'bed.n.08', 'name': 'bed'}, {'id': 5753, 'synset': 'bed_and_breakfast.n.01', 'name': 'bed_and_breakfast'}, {'id': 5754, 'synset': 'bedclothes.n.01', 'name': 'bedclothes'}, {'id': 5755, 'synset': 'bedford_cord.n.01', 'name': 'Bedford_cord'}, {'id': 5756, 'synset': 'bed_jacket.n.01', 'name': 'bed_jacket'}, {'id': 5757, 'synset': 'bedpost.n.01', 'name': 'bedpost'}, {'id': 5758, 'synset': 'bedroll.n.01', 'name': 'bedroll'}, {'id': 5759, 'synset': 'bedroom.n.01', 'name': 'bedroom'}, {'id': 5760, 'synset': 'bedroom_furniture.n.01', 'name': 'bedroom_furniture'}, {'id': 5761, 'synset': 'bedsitting_room.n.01', 'name': 'bedsitting_room'}, {'id': 5762, 'synset': 'bedspring.n.01', 'name': 'bedspring'}, {'id': 5763, 'synset': 'bedstead.n.01', 'name': 'bedstead'}, {'id': 5764, 'synset': 'beefcake.n.01', 'name': 'beefcake'}, {'id': 5765, 'synset': 'beehive.n.04', 'name': 'beehive'}, {'id': 5766, 'synset': 'beer_barrel.n.01', 'name': 'beer_barrel'}, {'id': 5767, 'synset': 'beer_garden.n.01', 'name': 'beer_garden'}, {'id': 5768, 'synset': 'beer_glass.n.01', 'name': 'beer_glass'}, {'id': 5769, 'synset': 'beer_hall.n.01', 'name': 'beer_hall'}, {'id': 5770, 'synset': 'beer_mat.n.01', 'name': 'beer_mat'}, {'id': 5771, 'synset': 'beer_mug.n.01', 'name': 'beer_mug'}, {'id': 5772, 'synset': 'belaying_pin.n.01', 'name': 'belaying_pin'}, {'id': 5773, 'synset': 'belfry.n.02', 'name': 'belfry'}, {'id': 5774, 'synset': 'bell_arch.n.01', 'name': 'bell_arch'}, {'id': 5775, 'synset': 'bellarmine.n.02', 'name': 'bellarmine'}, {'id': 5776, 'synset': 'bellbottom_trousers.n.01', 'name': 'bellbottom_trousers'}, {'id': 5777, 'synset': 'bell_cote.n.01', 'name': 'bell_cote'}, {'id': 5778, 'synset': 'bell_foundry.n.01', 'name': 'bell_foundry'}, {'id': 5779, 'synset': 'bell_gable.n.01', 'name': 'bell_gable'}, {'id': 5780, 'synset': 'bell_jar.n.01', 'name': 'bell_jar'}, {'id': 5781, 'synset': 'bellows.n.01', 'name': 'bellows'}, {'id': 5782, 'synset': 'bellpull.n.01', 'name': 'bellpull'}, {'id': 5783, 'synset': 'bell_push.n.01', 'name': 'bell_push'}, {'id': 5784, 'synset': 'bell_seat.n.01', 'name': 'bell_seat'}, {'id': 5785, 'synset': 'bell_tent.n.01', 'name': 'bell_tent'}, {'id': 5786, 'synset': 'bell_tower.n.01', 'name': 'bell_tower'}, {'id': 5787, 'synset': 'bellyband.n.01', 'name': 'bellyband'}, {'id': 5788, 'synset': 'belt.n.06', 'name': 'belt'}, {'id': 5789, 'synset': 'belting.n.01', 'name': 'belting'}, {'id': 5790, 'synset': 'bench_clamp.n.01', 'name': 'bench_clamp'}, {'id': 5791, 'synset': 'bench_hook.n.01', 'name': 'bench_hook'}, {'id': 5792, 'synset': 'bench_lathe.n.01', 'name': 'bench_lathe'}, {'id': 5793, 'synset': 'bench_press.n.02', 'name': 'bench_press'}, {'id': 5794, 'synset': 'bender.n.01', 'name': 'bender'}, {'id': 5795, 'synset': 'berlin.n.03', 'name': 'berlin'}, {'id': 5796, 'synset': 'bermuda_shorts.n.01', 'name': 'Bermuda_shorts'}, {'id': 5797, 'synset': 'berth.n.03', 'name': 'berth'}, {'id': 5798, 'synset': 'besom.n.01', 'name': 'besom'}, {'id': 5799, 'synset': 'bessemer_converter.n.01', 'name': 'Bessemer_converter'}, {'id': 5800, 'synset': 'bethel.n.01', 'name': 'bethel'}, {'id': 5801, 'synset': 'betting_shop.n.01', 'name': 'betting_shop'}, {'id': 5802, 'synset': 'bevatron.n.01', 'name': 'bevatron'}, {'id': 5803, 'synset': 'bevel.n.02', 'name': 'bevel'}, {'id': 5804, 'synset': 'bevel_gear.n.01', 'name': 'bevel_gear'}, {'id': 5805, 'synset': 'b-flat_clarinet.n.01', 'name': 'B-flat_clarinet'}, {'id': 5806, 'synset': 'bib.n.01', 'name': 'bib'}, {'id': 5807, 'synset': 'bib-and-tucker.n.01', 'name': 'bib-and-tucker'}, {'id': 5808, 'synset': 'bicorn.n.01', 'name': 'bicorn'}, {'id': 5809, 'synset': 'bicycle-built-for-two.n.01', 'name': 'bicycle-built-for-two'}, {'id': 5810, 'synset': 'bicycle_chain.n.01', 'name': 'bicycle_chain'}, {'id': 5811, 'synset': 'bicycle_clip.n.01', 'name': 'bicycle_clip'}, {'id': 5812, 'synset': 'bicycle_pump.n.01', 'name': 'bicycle_pump'}, {'id': 5813, 'synset': 'bicycle_rack.n.01', 'name': 'bicycle_rack'}, {'id': 5814, 'synset': 'bicycle_seat.n.01', 'name': 'bicycle_seat'}, {'id': 5815, 'synset': 'bicycle_wheel.n.01', 'name': 'bicycle_wheel'}, {'id': 5816, 'synset': 'bidet.n.01', 'name': 'bidet'}, {'id': 5817, 'synset': 'bier.n.02', 'name': 'bier'}, {'id': 5818, 'synset': 'bier.n.01', 'name': 'bier'}, {'id': 5819, 'synset': 'bi-fold_door.n.01', 'name': 'bi-fold_door'}, {'id': 5820, 'synset': 'bifocals.n.01', 'name': 'bifocals'}, {'id': 5821, 'synset': 'big_blue.n.01', 'name': 'Big_Blue'}, {'id': 5822, 'synset': 'big_board.n.02', 'name': 'big_board'}, {'id': 5823, 'synset': 'bight.n.04', 'name': 'bight'}, {'id': 5824, 'synset': 'bikini.n.02', 'name': 'bikini'}, {'id': 5825, 'synset': 'bikini_pants.n.01', 'name': 'bikini_pants'}, {'id': 5826, 'synset': 'bilge.n.02', 'name': 'bilge'}, {'id': 5827, 'synset': 'bilge_keel.n.01', 'name': 'bilge_keel'}, {'id': 5828, 'synset': 'bilge_pump.n.01', 'name': 'bilge_pump'}, {'id': 5829, 'synset': 'bilge_well.n.01', 'name': 'bilge_well'}, {'id': 5830, 'synset': 'bill.n.08', 'name': 'bill'}, {'id': 5831, 'synset': 'billiard_ball.n.01', 'name': 'billiard_ball'}, {'id': 5832, 'synset': 'billiard_room.n.01', 'name': 'billiard_room'}, {'id': 5833, 'synset': 'bin.n.01', 'name': 'bin'}, {'id': 5834, 'synset': 'binder.n.04', 'name': 'binder'}, {'id': 5835, 'synset': 'bindery.n.01', 'name': 'bindery'}, {'id': 5836, 'synset': 'binding.n.05', 'name': 'binding'}, {'id': 5837, 'synset': 'bin_liner.n.01', 'name': 'bin_liner'}, {'id': 5838, 'synset': 'binnacle.n.01', 'name': 'binnacle'}, {'id': 5839, 'synset': 'binocular_microscope.n.01', 'name': 'binocular_microscope'}, {'id': 5840, 'synset': 'biochip.n.01', 'name': 'biochip'}, {'id': 5841, 'synset': 'biohazard_suit.n.01', 'name': 'biohazard_suit'}, {'id': 5842, 'synset': 'bioscope.n.02', 'name': 'bioscope'}, {'id': 5843, 'synset': 'biplane.n.01', 'name': 'biplane'}, {'id': 5844, 'synset': 'birch.n.03', 'name': 'birch'}, {'id': 5845, 'synset': 'birchbark_canoe.n.01', 'name': 'birchbark_canoe'}, {'id': 5846, 'synset': 'birdcall.n.02', 'name': 'birdcall'}, {'id': 5847, 'synset': 'bird_shot.n.01', 'name': 'bird_shot'}, {'id': 5848, 'synset': 'biretta.n.01', 'name': 'biretta'}, {'id': 5849, 'synset': 'bishop.n.03', 'name': 'bishop'}, {'id': 5850, 'synset': 'bistro.n.01', 'name': 'bistro'}, {'id': 5851, 'synset': 'bit.n.11', 'name': 'bit'}, {'id': 5852, 'synset': 'bit.n.05', 'name': 'bit'}, {'id': 5853, 'synset': 'bite_plate.n.01', 'name': 'bite_plate'}, {'id': 5854, 'synset': 'bitewing.n.01', 'name': 'bitewing'}, {'id': 5855, 'synset': 'bitumastic.n.01', 'name': 'bitumastic'}, {'id': 5856, 'synset': 'black.n.07', 'name': 'black'}, {'id': 5857, 'synset': 'black.n.06', 'name': 'black'}, {'id': 5858, 'synset': 'blackboard_eraser.n.01', 'name': 'blackboard_eraser'}, {'id': 5859, 'synset': 'black_box.n.01', 'name': 'black_box'}, {'id': 5860, 'synset': 'blackface.n.01', 'name': 'blackface'}, {'id': 5861, 'synset': 'blackjack.n.02', 'name': 'blackjack'}, {'id': 5862, 'synset': 'black_tie.n.02', 'name': 'black_tie'}, {'id': 5863, 'synset': 'blackwash.n.03', 'name': 'blackwash'}, {'id': 5864, 'synset': 'bladder.n.02', 'name': 'bladder'}, {'id': 5865, 'synset': 'blade.n.09', 'name': 'blade'}, {'id': 5866, 'synset': 'blade.n.08', 'name': 'blade'}, {'id': 5867, 'synset': 'blade.n.07', 'name': 'blade'}, {'id': 5868, 'synset': 'blank.n.04', 'name': 'blank'}, {'id': 5869, 'synset': 'blast_furnace.n.01', 'name': 'blast_furnace'}, {'id': 5870, 'synset': 'blasting_cap.n.01', 'name': 'blasting_cap'}, {'id': 5871, 'synset': 'blind.n.03', 'name': 'blind'}, {'id': 5872, 'synset': 'blind_curve.n.01', 'name': 'blind_curve'}, {'id': 5873, 'synset': 'blindfold.n.01', 'name': 'blindfold'}, {'id': 5874, 'synset': 'bling.n.01', 'name': 'bling'}, {'id': 5875, 'synset': 'blister_pack.n.01', 'name': 'blister_pack'}, {'id': 5876, 'synset': 'block.n.05', 'name': 'block'}, {'id': 5877, 'synset': 'blockade.n.02', 'name': 'blockade'}, {'id': 5878, 'synset': 'blockade-runner.n.01', 'name': 'blockade-runner'}, {'id': 5879, 'synset': 'block_and_tackle.n.01', 'name': 'block_and_tackle'}, {'id': 5880, 'synset': 'blockbuster.n.01', 'name': 'blockbuster'}, {'id': 5881, 'synset': 'blockhouse.n.01', 'name': 'blockhouse'}, {'id': 5882, 'synset': 'block_plane.n.01', 'name': 'block_plane'}, {'id': 5883, 'synset': 'bloodmobile.n.01', 'name': 'bloodmobile'}, {'id': 5884, 'synset': 'bloomers.n.01', 'name': 'bloomers'}, {'id': 5885, 'synset': 'blower.n.01', 'name': 'blower'}, {'id': 5886, 'synset': 'blowtorch.n.01', 'name': 'blowtorch'}, {'id': 5887, 'synset': 'blucher.n.02', 'name': 'blucher'}, {'id': 5888, 'synset': 'bludgeon.n.01', 'name': 'bludgeon'}, {'id': 5889, 'synset': 'blue.n.02', 'name': 'blue'}, {'id': 5890, 'synset': 'blue_chip.n.02', 'name': 'blue_chip'}, {'id': 5891, 'synset': 'blunderbuss.n.01', 'name': 'blunderbuss'}, {'id': 5892, 'synset': 'blunt_file.n.01', 'name': 'blunt_file'}, {'id': 5893, 'synset': 'boarding.n.02', 'name': 'boarding'}, {'id': 5894, 'synset': 'boarding_house.n.01', 'name': 'boarding_house'}, {'id': 5895, 'synset': 'boardroom.n.01', 'name': 'boardroom'}, {'id': 5896, 'synset': 'boards.n.02', 'name': 'boards'}, {'id': 5897, 'synset': 'boater.n.01', 'name': 'boater'}, {'id': 5898, 'synset': 'boat_hook.n.01', 'name': 'boat_hook'}, {'id': 5899, 'synset': 'boathouse.n.01', 'name': 'boathouse'}, {'id': 5900, 'synset': "boatswain's_chair.n.01", 'name': "boatswain's_chair"}, {'id': 5901, 'synset': 'boat_train.n.01', 'name': 'boat_train'}, {'id': 5902, 'synset': 'boatyard.n.01', 'name': 'boatyard'}, {'id': 5903, 'synset': 'bobsled.n.02', 'name': 'bobsled'}, {'id': 5904, 'synset': 'bobsled.n.01', 'name': 'bobsled'}, {'id': 5905, 'synset': 'bocce_ball.n.01', 'name': 'bocce_ball'}, {'id': 5906, 'synset': 'bodega.n.01', 'name': 'bodega'}, {'id': 5907, 'synset': 'bodice.n.01', 'name': 'bodice'}, {'id': 5908, 'synset': 'bodkin.n.04', 'name': 'bodkin'}, {'id': 5909, 'synset': 'bodkin.n.03', 'name': 'bodkin'}, {'id': 5910, 'synset': 'bodkin.n.02', 'name': 'bodkin'}, {'id': 5911, 'synset': 'body.n.11', 'name': 'body'}, {'id': 5912, 'synset': 'body_armor.n.01', 'name': 'body_armor'}, {'id': 5913, 'synset': 'body_lotion.n.01', 'name': 'body_lotion'}, {'id': 5914, 'synset': 'body_stocking.n.01', 'name': 'body_stocking'}, {'id': 5915, 'synset': 'body_plethysmograph.n.01', 'name': 'body_plethysmograph'}, {'id': 5916, 'synset': 'body_pad.n.01', 'name': 'body_pad'}, {'id': 5917, 'synset': 'bodywork.n.01', 'name': 'bodywork'}, {'id': 5918, 'synset': 'bofors_gun.n.01', 'name': 'Bofors_gun'}, {'id': 5919, 'synset': 'bogy.n.01', 'name': 'bogy'}, {'id': 5920, 'synset': 'boiler.n.01', 'name': 'boiler'}, {'id': 5921, 'synset': 'boiling_water_reactor.n.01', 'name': 'boiling_water_reactor'}, {'id': 5922, 'synset': 'bolero.n.02', 'name': 'bolero'}, {'id': 5923, 'synset': 'bollard.n.01', 'name': 'bollard'}, {'id': 5924, 'synset': 'bolo.n.02', 'name': 'bolo'}, {'id': 5925, 'synset': 'bolt.n.02', 'name': 'bolt'}, {'id': 5926, 'synset': 'bolt_cutter.n.01', 'name': 'bolt_cutter'}, {'id': 5927, 'synset': 'bomb.n.01', 'name': 'bomb'}, {'id': 5928, 'synset': 'bombazine.n.01', 'name': 'bombazine'}, {'id': 5929, 'synset': 'bomb_calorimeter.n.01', 'name': 'bomb_calorimeter'}, {'id': 5930, 'synset': 'bomber.n.01', 'name': 'bomber'}, {'id': 5931, 'synset': 'bomber_jacket.n.01', 'name': 'bomber_jacket'}, {'id': 5932, 'synset': 'bomblet.n.01', 'name': 'bomblet'}, {'id': 5933, 'synset': 'bomb_rack.n.01', 'name': 'bomb_rack'}, {'id': 5934, 'synset': 'bombshell.n.03', 'name': 'bombshell'}, {'id': 5935, 'synset': 'bomb_shelter.n.01', 'name': 'bomb_shelter'}, {'id': 5936, 'synset': 'bone-ash_cup.n.01', 'name': 'bone-ash_cup'}, {'id': 5937, 'synset': 'bone_china.n.01', 'name': 'bone_china'}, {'id': 5938, 'synset': 'bones.n.01', 'name': 'bones'}, {'id': 5939, 'synset': 'boneshaker.n.01', 'name': 'boneshaker'}, {'id': 5940, 'synset': 'bongo.n.01', 'name': 'bongo'}, {'id': 5941, 'synset': 'book.n.11', 'name': 'book'}, {'id': 5942, 'synset': 'book_bag.n.01', 'name': 'book_bag'}, {'id': 5943, 'synset': 'bookbindery.n.01', 'name': 'bookbindery'}, {'id': 5944, 'synset': 'bookend.n.01', 'name': 'bookend'}, {'id': 5945, 'synset': 'bookmobile.n.01', 'name': 'bookmobile'}, {'id': 5946, 'synset': 'bookshelf.n.01', 'name': 'bookshelf'}, {'id': 5947, 'synset': 'bookshop.n.01', 'name': 'bookshop'}, {'id': 5948, 'synset': 'boom.n.05', 'name': 'boom'}, {'id': 5949, 'synset': 'boomerang.n.01', 'name': 'boomerang'}, {'id': 5950, 'synset': 'booster.n.05', 'name': 'booster'}, {'id': 5951, 'synset': 'booster.n.04', 'name': 'booster'}, {'id': 5952, 'synset': 'boot.n.04', 'name': 'boot'}, {'id': 5953, 'synset': 'boot_camp.n.01', 'name': 'boot_camp'}, {'id': 5954, 'synset': 'bootee.n.01', 'name': 'bootee'}, {'id': 5955, 'synset': 'booth.n.02', 'name': 'booth'}, {'id': 5956, 'synset': 'booth.n.04', 'name': 'booth'}, {'id': 5957, 'synset': 'booth.n.01', 'name': 'booth'}, {'id': 5958, 'synset': 'boothose.n.01', 'name': 'boothose'}, {'id': 5959, 'synset': 'bootjack.n.01', 'name': 'bootjack'}, {'id': 5960, 'synset': 'bootlace.n.01', 'name': 'bootlace'}, {'id': 5961, 'synset': 'bootleg.n.02', 'name': 'bootleg'}, {'id': 5962, 'synset': 'bootstrap.n.01', 'name': 'bootstrap'}, {'id': 5963, 'synset': 'bore_bit.n.01', 'name': 'bore_bit'}, {'id': 5964, 'synset': 'boron_chamber.n.01', 'name': 'boron_chamber'}, {'id': 5965, 'synset': 'borstal.n.01', 'name': 'borstal'}, {'id': 5966, 'synset': 'bosom.n.03', 'name': 'bosom'}, {'id': 5967, 'synset': 'boston_rocker.n.01', 'name': 'Boston_rocker'}, {'id': 5968, 'synset': 'bota.n.01', 'name': 'bota'}, {'id': 5969, 'synset': 'bottle.n.03', 'name': 'bottle'}, {'id': 5970, 'synset': 'bottle_bank.n.01', 'name': 'bottle_bank'}, {'id': 5971, 'synset': 'bottlebrush.n.01', 'name': 'bottlebrush'}, {'id': 5972, 'synset': 'bottlecap.n.01', 'name': 'bottlecap'}, {'id': 5973, 'synset': 'bottling_plant.n.01', 'name': 'bottling_plant'}, {'id': 5974, 'synset': 'bottom.n.07', 'name': 'bottom'}, {'id': 5975, 'synset': 'boucle.n.01', 'name': 'boucle'}, {'id': 5976, 'synset': 'boudoir.n.01', 'name': 'boudoir'}, {'id': 5977, 'synset': 'boulle.n.01', 'name': 'boulle'}, {'id': 5978, 'synset': 'bouncing_betty.n.01', 'name': 'bouncing_betty'}, {'id': 5979, 'synset': 'boutique.n.01', 'name': 'boutique'}, {'id': 5980, 'synset': 'boutonniere.n.01', 'name': 'boutonniere'}, {'id': 5981, 'synset': 'bow.n.02', 'name': 'bow'}, {'id': 5982, 'synset': 'bow.n.01', 'name': 'bow'}, {'id': 5983, 'synset': 'bow_and_arrow.n.01', 'name': 'bow_and_arrow'}, {'id': 5984, 'synset': 'bowed_stringed_instrument.n.01', 'name': 'bowed_stringed_instrument'}, {'id': 5985, 'synset': 'bowie_knife.n.01', 'name': 'Bowie_knife'}, {'id': 5986, 'synset': 'bowl.n.01', 'name': 'bowl'}, {'id': 5987, 'synset': 'bowl.n.07', 'name': 'bowl'}, {'id': 5988, 'synset': 'bowline.n.01', 'name': 'bowline'}, {'id': 5989, 'synset': 'bowling_alley.n.01', 'name': 'bowling_alley'}, {'id': 5990, 'synset': 'bowling_equipment.n.01', 'name': 'bowling_equipment'}, {'id': 5991, 'synset': 'bowling_pin.n.01', 'name': 'bowling_pin'}, {'id': 5992, 'synset': 'bowling_shoe.n.01', 'name': 'bowling_shoe'}, {'id': 5993, 'synset': 'bowsprit.n.01', 'name': 'bowsprit'}, {'id': 5994, 'synset': 'bowstring.n.01', 'name': 'bowstring'}, {'id': 5995, 'synset': 'box.n.02', 'name': 'box'}, {'id': 5996, 'synset': 'box.n.08', 'name': 'box'}, {'id': 5997, 'synset': 'box_beam.n.01', 'name': 'box_beam'}, {'id': 5998, 'synset': 'box_camera.n.01', 'name': 'box_camera'}, {'id': 5999, 'synset': 'boxcar.n.01', 'name': 'boxcar'}, {'id': 6000, 'synset': 'box_coat.n.01', 'name': 'box_coat'}, {'id': 6001, 'synset': 'boxing_equipment.n.01', 'name': 'boxing_equipment'}, {'id': 6002, 'synset': 'box_office.n.02', 'name': 'box_office'}, {'id': 6003, 'synset': 'box_spring.n.01', 'name': 'box_spring'}, {'id': 6004, 'synset': 'box_wrench.n.01', 'name': 'box_wrench'}, {'id': 6005, 'synset': 'brace.n.09', 'name': 'brace'}, {'id': 6006, 'synset': 'brace.n.07', 'name': 'brace'}, {'id': 6007, 'synset': 'brace.n.01', 'name': 'brace'}, {'id': 6008, 'synset': 'brace_and_bit.n.01', 'name': 'brace_and_bit'}, {'id': 6009, 'synset': 'bracer.n.01', 'name': 'bracer'}, {'id': 6010, 'synset': 'brace_wrench.n.01', 'name': 'brace_wrench'}, {'id': 6011, 'synset': 'bracket.n.04', 'name': 'bracket'}, {'id': 6012, 'synset': 'bradawl.n.01', 'name': 'bradawl'}, {'id': 6013, 'synset': 'brake.n.01', 'name': 'brake'}, {'id': 6014, 'synset': 'brake.n.05', 'name': 'brake'}, {'id': 6015, 'synset': 'brake_band.n.01', 'name': 'brake_band'}, {'id': 6016, 'synset': 'brake_cylinder.n.01', 'name': 'brake_cylinder'}, {'id': 6017, 'synset': 'brake_disk.n.01', 'name': 'brake_disk'}, {'id': 6018, 'synset': 'brake_drum.n.01', 'name': 'brake_drum'}, {'id': 6019, 'synset': 'brake_lining.n.01', 'name': 'brake_lining'}, {'id': 6020, 'synset': 'brake_pad.n.01', 'name': 'brake_pad'}, {'id': 6021, 'synset': 'brake_pedal.n.01', 'name': 'brake_pedal'}, {'id': 6022, 'synset': 'brake_shoe.n.01', 'name': 'brake_shoe'}, {'id': 6023, 'synset': 'brake_system.n.01', 'name': 'brake_system'}, {'id': 6024, 'synset': 'brass.n.02', 'name': 'brass'}, {'id': 6025, 'synset': 'brass.n.05', 'name': 'brass'}, {'id': 6026, 'synset': 'brassard.n.01', 'name': 'brassard'}, {'id': 6027, 'synset': 'brasserie.n.01', 'name': 'brasserie'}, {'id': 6028, 'synset': 'brassie.n.01', 'name': 'brassie'}, {'id': 6029, 'synset': 'brass_knucks.n.01', 'name': 'brass_knucks'}, {'id': 6030, 'synset': 'brattice.n.01', 'name': 'brattice'}, {'id': 6031, 'synset': 'brazier.n.01', 'name': 'brazier'}, {'id': 6032, 'synset': 'breadbasket.n.03', 'name': 'breadbasket'}, {'id': 6033, 'synset': 'bread_knife.n.01', 'name': 'bread_knife'}, {'id': 6034, 'synset': 'breakable.n.01', 'name': 'breakable'}, {'id': 6035, 'synset': 'breakfast_area.n.01', 'name': 'breakfast_area'}, {'id': 6036, 'synset': 'breakfast_table.n.01', 'name': 'breakfast_table'}, {'id': 6037, 'synset': 'breakwater.n.01', 'name': 'breakwater'}, {'id': 6038, 'synset': 'breast_drill.n.01', 'name': 'breast_drill'}, {'id': 6039, 'synset': 'breast_implant.n.01', 'name': 'breast_implant'}, {'id': 6040, 'synset': 'breastplate.n.01', 'name': 'breastplate'}, {'id': 6041, 'synset': 'breast_pocket.n.01', 'name': 'breast_pocket'}, {'id': 6042, 'synset': 'breathalyzer.n.01', 'name': 'breathalyzer'}, {'id': 6043, 'synset': 'breechblock.n.01', 'name': 'breechblock'}, {'id': 6044, 'synset': 'breeches.n.01', 'name': 'breeches'}, {'id': 6045, 'synset': 'breeches_buoy.n.01', 'name': 'breeches_buoy'}, {'id': 6046, 'synset': 'breechloader.n.01', 'name': 'breechloader'}, {'id': 6047, 'synset': 'breeder_reactor.n.01', 'name': 'breeder_reactor'}, {'id': 6048, 'synset': 'bren.n.01', 'name': 'Bren'}, {'id': 6049, 'synset': 'brewpub.n.01', 'name': 'brewpub'}, {'id': 6050, 'synset': 'brick.n.01', 'name': 'brick'}, {'id': 6051, 'synset': 'brickkiln.n.01', 'name': 'brickkiln'}, {'id': 6052, 'synset': "bricklayer's_hammer.n.01", 'name': "bricklayer's_hammer"}, {'id': 6053, 'synset': 'brick_trowel.n.01', 'name': 'brick_trowel'}, {'id': 6054, 'synset': 'brickwork.n.01', 'name': 'brickwork'}, {'id': 6055, 'synset': 'bridge.n.01', 'name': 'bridge'}, {'id': 6056, 'synset': 'bridge.n.08', 'name': 'bridge'}, {'id': 6057, 'synset': 'bridle.n.01', 'name': 'bridle'}, {'id': 6058, 'synset': 'bridle_path.n.01', 'name': 'bridle_path'}, {'id': 6059, 'synset': 'bridoon.n.01', 'name': 'bridoon'}, {'id': 6060, 'synset': 'briefcase_bomb.n.01', 'name': 'briefcase_bomb'}, {'id': 6061, 'synset': 'briefcase_computer.n.01', 'name': 'briefcase_computer'}, {'id': 6062, 'synset': 'briefs.n.01', 'name': 'briefs'}, {'id': 6063, 'synset': 'brig.n.02', 'name': 'brig'}, {'id': 6064, 'synset': 'brig.n.01', 'name': 'brig'}, {'id': 6065, 'synset': 'brigandine.n.01', 'name': 'brigandine'}, {'id': 6066, 'synset': 'brigantine.n.01', 'name': 'brigantine'}, {'id': 6067, 'synset': 'brilliantine.n.01', 'name': 'brilliantine'}, {'id': 6068, 'synset': 'brilliant_pebble.n.01', 'name': 'brilliant_pebble'}, {'id': 6069, 'synset': 'brim.n.02', 'name': 'brim'}, {'id': 6070, 'synset': 'bristle_brush.n.01', 'name': 'bristle_brush'}, {'id': 6071, 'synset': 'britches.n.01', 'name': 'britches'}, {'id': 6072, 'synset': 'broad_arrow.n.03', 'name': 'broad_arrow'}, {'id': 6073, 'synset': 'broadax.n.01', 'name': 'broadax'}, {'id': 6074, 'synset': 'brochette.n.01', 'name': 'brochette'}, {'id': 6075, 'synset': 'broadcaster.n.02', 'name': 'broadcaster'}, {'id': 6076, 'synset': 'broadcloth.n.02', 'name': 'broadcloth'}, {'id': 6077, 'synset': 'broadcloth.n.01', 'name': 'broadcloth'}, {'id': 6078, 'synset': 'broad_hatchet.n.01', 'name': 'broad_hatchet'}, {'id': 6079, 'synset': 'broadloom.n.01', 'name': 'broadloom'}, {'id': 6080, 'synset': 'broadside.n.03', 'name': 'broadside'}, {'id': 6081, 'synset': 'broadsword.n.01', 'name': 'broadsword'}, {'id': 6082, 'synset': 'brocade.n.01', 'name': 'brocade'}, {'id': 6083, 'synset': 'brogan.n.01', 'name': 'brogan'}, {'id': 6084, 'synset': 'broiler.n.01', 'name': 'broiler'}, {'id': 6085, 'synset': 'broken_arch.n.01', 'name': 'broken_arch'}, {'id': 6086, 'synset': 'bronchoscope.n.01', 'name': 'bronchoscope'}, {'id': 6087, 'synset': 'broom_closet.n.01', 'name': 'broom_closet'}, {'id': 6088, 'synset': 'broomstick.n.01', 'name': 'broomstick'}, {'id': 6089, 'synset': 'brougham.n.01', 'name': 'brougham'}, {'id': 6090, 'synset': 'browning_automatic_rifle.n.01', 'name': 'Browning_automatic_rifle'}, {'id': 6091, 'synset': 'browning_machine_gun.n.01', 'name': 'Browning_machine_gun'}, {'id': 6092, 'synset': 'brownstone.n.02', 'name': 'brownstone'}, {'id': 6093, 'synset': 'brunch_coat.n.01', 'name': 'brunch_coat'}, {'id': 6094, 'synset': 'brush.n.02', 'name': 'brush'}, {'id': 6095, 'synset': 'brussels_carpet.n.01', 'name': 'Brussels_carpet'}, {'id': 6096, 'synset': 'brussels_lace.n.01', 'name': 'Brussels_lace'}, {'id': 6097, 'synset': 'bubble.n.04', 'name': 'bubble'}, {'id': 6098, 'synset': 'bubble_chamber.n.01', 'name': 'bubble_chamber'}, {'id': 6099, 'synset': 'bubble_jet_printer.n.01', 'name': 'bubble_jet_printer'}, {'id': 6100, 'synset': 'buckboard.n.01', 'name': 'buckboard'}, {'id': 6101, 'synset': 'bucket_seat.n.01', 'name': 'bucket_seat'}, {'id': 6102, 'synset': 'bucket_shop.n.02', 'name': 'bucket_shop'}, {'id': 6103, 'synset': 'buckle.n.01', 'name': 'buckle'}, {'id': 6104, 'synset': 'buckram.n.01', 'name': 'buckram'}, {'id': 6105, 'synset': 'bucksaw.n.01', 'name': 'bucksaw'}, {'id': 6106, 'synset': 'buckskins.n.01', 'name': 'buckskins'}, {'id': 6107, 'synset': 'buff.n.05', 'name': 'buff'}, {'id': 6108, 'synset': 'buffer.n.05', 'name': 'buffer'}, {'id': 6109, 'synset': 'buffer.n.04', 'name': 'buffer'}, {'id': 6110, 'synset': 'buffet.n.01', 'name': 'buffet'}, {'id': 6111, 'synset': 'buffing_wheel.n.01', 'name': 'buffing_wheel'}, {'id': 6112, 'synset': 'bugle.n.01', 'name': 'bugle'}, {'id': 6113, 'synset': 'building.n.01', 'name': 'building'}, {'id': 6114, 'synset': 'building_complex.n.01', 'name': 'building_complex'}, {'id': 6115, 'synset': 'bulldog_clip.n.01', 'name': 'bulldog_clip'}, {'id': 6116, 'synset': 'bulldog_wrench.n.01', 'name': 'bulldog_wrench'}, {'id': 6117, 'synset': 'bullet.n.01', 'name': 'bullet'}, {'id': 6118, 'synset': 'bullion.n.02', 'name': 'bullion'}, {'id': 6119, 'synset': 'bullnose.n.01', 'name': 'bullnose'}, {'id': 6120, 'synset': 'bullpen.n.02', 'name': 'bullpen'}, {'id': 6121, 'synset': 'bullpen.n.01', 'name': 'bullpen'}, {'id': 6122, 'synset': 'bullring.n.01', 'name': 'bullring'}, {'id': 6123, 'synset': 'bulwark.n.02', 'name': 'bulwark'}, {'id': 6124, 'synset': 'bumboat.n.01', 'name': 'bumboat'}, {'id': 6125, 'synset': 'bumper.n.02', 'name': 'bumper'}, {'id': 6126, 'synset': 'bumper.n.01', 'name': 'bumper'}, {'id': 6127, 'synset': 'bumper_car.n.01', 'name': 'bumper_car'}, {'id': 6128, 'synset': 'bumper_guard.n.01', 'name': 'bumper_guard'}, {'id': 6129, 'synset': 'bumper_jack.n.01', 'name': 'bumper_jack'}, {'id': 6130, 'synset': 'bundle.n.02', 'name': 'bundle'}, {'id': 6131, 'synset': 'bung.n.01', 'name': 'bung'}, {'id': 6132, 'synset': 'bungalow.n.01', 'name': 'bungalow'}, {'id': 6133, 'synset': 'bungee.n.01', 'name': 'bungee'}, {'id': 6134, 'synset': 'bunghole.n.02', 'name': 'bunghole'}, {'id': 6135, 'synset': 'bunk.n.03', 'name': 'bunk'}, {'id': 6136, 'synset': 'bunk.n.01', 'name': 'bunk'}, {'id': 6137, 'synset': 'bunker.n.01', 'name': 'bunker'}, {'id': 6138, 'synset': 'bunker.n.03', 'name': 'bunker'}, {'id': 6139, 'synset': 'bunker.n.02', 'name': 'bunker'}, {'id': 6140, 'synset': 'bunsen_burner.n.01', 'name': 'bunsen_burner'}, {'id': 6141, 'synset': 'bunting.n.01', 'name': 'bunting'}, {'id': 6142, 'synset': 'bur.n.02', 'name': 'bur'}, {'id': 6143, 'synset': 'burberry.n.01', 'name': 'Burberry'}, {'id': 6144, 'synset': 'burette.n.01', 'name': 'burette'}, {'id': 6145, 'synset': 'burglar_alarm.n.02', 'name': 'burglar_alarm'}, {'id': 6146, 'synset': 'burial_chamber.n.01', 'name': 'burial_chamber'}, {'id': 6147, 'synset': 'burial_garment.n.01', 'name': 'burial_garment'}, {'id': 6148, 'synset': 'burial_mound.n.01', 'name': 'burial_mound'}, {'id': 6149, 'synset': 'burin.n.01', 'name': 'burin'}, {'id': 6150, 'synset': 'burqa.n.01', 'name': 'burqa'}, {'id': 6151, 'synset': 'burlap.n.01', 'name': 'burlap'}, {'id': 6152, 'synset': 'burn_bag.n.01', 'name': 'burn_bag'}, {'id': 6153, 'synset': 'burner.n.01', 'name': 'burner'}, {'id': 6154, 'synset': 'burnous.n.01', 'name': 'burnous'}, {'id': 6155, 'synset': 'burp_gun.n.01', 'name': 'burp_gun'}, {'id': 6156, 'synset': 'burr.n.04', 'name': 'burr'}, {'id': 6157, 'synset': 'bushel_basket.n.01', 'name': 'bushel_basket'}, {'id': 6158, 'synset': 'bushing.n.02', 'name': 'bushing'}, {'id': 6159, 'synset': 'bush_jacket.n.01', 'name': 'bush_jacket'}, {'id': 6160, 'synset': 'business_suit.n.01', 'name': 'business_suit'}, {'id': 6161, 'synset': 'buskin.n.01', 'name': 'buskin'}, {'id': 6162, 'synset': 'bustier.n.01', 'name': 'bustier'}, {'id': 6163, 'synset': 'bustle.n.02', 'name': 'bustle'}, {'id': 6164, 'synset': 'butcher_knife.n.01', 'name': 'butcher_knife'}, {'id': 6165, 'synset': 'butcher_shop.n.01', 'name': 'butcher_shop'}, {'id': 6166, 'synset': 'butter_dish.n.01', 'name': 'butter_dish'}, {'id': 6167, 'synset': 'butterfly_valve.n.01', 'name': 'butterfly_valve'}, {'id': 6168, 'synset': 'butter_knife.n.01', 'name': 'butter_knife'}, {'id': 6169, 'synset': 'butt_hinge.n.01', 'name': 'butt_hinge'}, {'id': 6170, 'synset': 'butt_joint.n.01', 'name': 'butt_joint'}, {'id': 6171, 'synset': 'buttonhook.n.01', 'name': 'buttonhook'}, {'id': 6172, 'synset': 'buttress.n.01', 'name': 'buttress'}, {'id': 6173, 'synset': 'butt_shaft.n.01', 'name': 'butt_shaft'}, {'id': 6174, 'synset': 'butt_weld.n.01', 'name': 'butt_weld'}, {'id': 6175, 'synset': 'buzz_bomb.n.01', 'name': 'buzz_bomb'}, {'id': 6176, 'synset': 'buzzer.n.02', 'name': 'buzzer'}, {'id': 6177, 'synset': 'bvd.n.01', 'name': 'BVD'}, {'id': 6178, 'synset': 'bypass_condenser.n.01', 'name': 'bypass_condenser'}, {'id': 6179, 'synset': 'byway.n.01', 'name': 'byway'}, {'id': 6180, 'synset': 'cab.n.02', 'name': 'cab'}, {'id': 6181, 'synset': 'cab.n.01', 'name': 'cab'}, {'id': 6182, 'synset': 'cabaret.n.01', 'name': 'cabaret'}, {'id': 6183, 'synset': 'caber.n.01', 'name': 'caber'}, {'id': 6184, 'synset': 'cabin.n.03', 'name': 'cabin'}, {'id': 6185, 'synset': 'cabin.n.02', 'name': 'cabin'}, {'id': 6186, 'synset': 'cabin_class.n.01', 'name': 'cabin_class'}, {'id': 6187, 'synset': 'cabin_cruiser.n.01', 'name': 'cabin_cruiser'}, {'id': 6188, 'synset': 'cabinet.n.04', 'name': 'cabinet'}, {'id': 6189, 'synset': 'cabinetwork.n.01', 'name': 'cabinetwork'}, {'id': 6190, 'synset': 'cabin_liner.n.01', 'name': 'cabin_liner'}, {'id': 6191, 'synset': 'cable.n.06', 'name': 'cable'}, {'id': 6192, 'synset': 'cable.n.02', 'name': 'cable'}, {'id': 6193, 'synset': 'cable_car.n.01', 'name': 'cable_car'}, {'id': 6194, 'synset': 'cache.n.03', 'name': 'cache'}, {'id': 6195, 'synset': 'caddy.n.01', 'name': 'caddy'}, {'id': 6196, 'synset': 'caesium_clock.n.01', 'name': 'caesium_clock'}, {'id': 6197, 'synset': 'cafe.n.01', 'name': 'cafe'}, {'id': 6198, 'synset': 'cafeteria.n.01', 'name': 'cafeteria'}, {'id': 6199, 'synset': 'cafeteria_tray.n.01', 'name': 'cafeteria_tray'}, {'id': 6200, 'synset': 'caff.n.01', 'name': 'caff'}, {'id': 6201, 'synset': 'caftan.n.02', 'name': 'caftan'}, {'id': 6202, 'synset': 'caftan.n.01', 'name': 'caftan'}, {'id': 6203, 'synset': 'cage.n.01', 'name': 'cage'}, {'id': 6204, 'synset': 'cage.n.04', 'name': 'cage'}, {'id': 6205, 'synset': 'cagoule.n.01', 'name': 'cagoule'}, {'id': 6206, 'synset': 'caisson.n.02', 'name': 'caisson'}, {'id': 6207, 'synset': 'calash.n.02', 'name': 'calash'}, {'id': 6208, 'synset': 'calceus.n.01', 'name': 'calceus'}, {'id': 6209, 'synset': 'calcimine.n.01', 'name': 'calcimine'}, {'id': 6210, 'synset': 'caldron.n.01', 'name': 'caldron'}, {'id': 6211, 'synset': 'calico.n.01', 'name': 'calico'}, {'id': 6212, 'synset': 'caliper.n.01', 'name': 'caliper'}, {'id': 6213, 'synset': 'call-board.n.01', 'name': 'call-board'}, {'id': 6214, 'synset': 'call_center.n.01', 'name': 'call_center'}, {'id': 6215, 'synset': 'caller_id.n.01', 'name': 'caller_ID'}, {'id': 6216, 'synset': 'calliope.n.02', 'name': 'calliope'}, {'id': 6217, 'synset': 'calorimeter.n.01', 'name': 'calorimeter'}, {'id': 6218, 'synset': 'calpac.n.01', 'name': 'calpac'}, {'id': 6219, 'synset': 'camail.n.01', 'name': 'camail'}, {'id': 6220, 'synset': 'camber_arch.n.01', 'name': 'camber_arch'}, {'id': 6221, 'synset': 'cambric.n.01', 'name': 'cambric'}, {'id': 6222, 'synset': "camel's_hair.n.01", 'name': "camel's_hair"}, {'id': 6223, 'synset': 'camera_lucida.n.01', 'name': 'camera_lucida'}, {'id': 6224, 'synset': 'camera_obscura.n.01', 'name': 'camera_obscura'}, {'id': 6225, 'synset': 'camera_tripod.n.01', 'name': 'camera_tripod'}, {'id': 6226, 'synset': 'camise.n.01', 'name': 'camise'}, {'id': 6227, 'synset': 'camisole.n.02', 'name': 'camisole'}, {'id': 6228, 'synset': 'camisole.n.01', 'name': 'camisole'}, {'id': 6229, 'synset': 'camlet.n.02', 'name': 'camlet'}, {'id': 6230, 'synset': 'camouflage.n.03', 'name': 'camouflage'}, {'id': 6231, 'synset': 'camouflage.n.02', 'name': 'camouflage'}, {'id': 6232, 'synset': 'camp.n.01', 'name': 'camp'}, {'id': 6233, 'synset': 'camp.n.03', 'name': 'camp'}, {'id': 6234, 'synset': 'camp.n.07', 'name': 'camp'}, {'id': 6235, 'synset': 'campaign_hat.n.01', 'name': 'campaign_hat'}, {'id': 6236, 'synset': 'campanile.n.01', 'name': 'campanile'}, {'id': 6237, 'synset': 'camp_chair.n.01', 'name': 'camp_chair'}, {'id': 6238, 'synset': 'camper_trailer.n.01', 'name': 'camper_trailer'}, {'id': 6239, 'synset': 'campstool.n.01', 'name': 'campstool'}, {'id': 6240, 'synset': 'camshaft.n.01', 'name': 'camshaft'}, {'id': 6241, 'synset': 'canal.n.03', 'name': 'canal'}, {'id': 6242, 'synset': 'canal_boat.n.01', 'name': 'canal_boat'}, {'id': 6243, 'synset': 'candelabrum.n.01', 'name': 'candelabrum'}, {'id': 6244, 'synset': 'candid_camera.n.01', 'name': 'candid_camera'}, {'id': 6245, 'synset': 'candlepin.n.01', 'name': 'candlepin'}, {'id': 6246, 'synset': 'candlesnuffer.n.01', 'name': 'candlesnuffer'}, {'id': 6247, 'synset': 'candlewick.n.02', 'name': 'candlewick'}, {'id': 6248, 'synset': 'candy_thermometer.n.01', 'name': 'candy_thermometer'}, {'id': 6249, 'synset': 'cane.n.03', 'name': 'cane'}, {'id': 6250, 'synset': 'cangue.n.01', 'name': 'cangue'}, {'id': 6251, 'synset': 'cannery.n.01', 'name': 'cannery'}, {'id': 6252, 'synset': 'cannikin.n.02', 'name': 'cannikin'}, {'id': 6253, 'synset': 'cannikin.n.01', 'name': 'cannikin'}, {'id': 6254, 'synset': 'cannon.n.01', 'name': 'cannon'}, {'id': 6255, 'synset': 'cannon.n.04', 'name': 'cannon'}, {'id': 6256, 'synset': 'cannon.n.03', 'name': 'cannon'}, {'id': 6257, 'synset': 'cannon.n.02', 'name': 'cannon'}, {'id': 6258, 'synset': 'cannonball.n.01', 'name': 'cannonball'}, {'id': 6259, 'synset': 'canopic_jar.n.01', 'name': 'canopic_jar'}, {'id': 6260, 'synset': 'canopy.n.03', 'name': 'canopy'}, {'id': 6261, 'synset': 'canopy.n.02', 'name': 'canopy'}, {'id': 6262, 'synset': 'canopy.n.01', 'name': 'canopy'}, {'id': 6263, 'synset': 'canteen.n.05', 'name': 'canteen'}, {'id': 6264, 'synset': 'canteen.n.04', 'name': 'canteen'}, {'id': 6265, 'synset': 'canteen.n.03', 'name': 'canteen'}, {'id': 6266, 'synset': 'canteen.n.02', 'name': 'canteen'}, {'id': 6267, 'synset': 'cant_hook.n.01', 'name': 'cant_hook'}, {'id': 6268, 'synset': 'cantilever.n.01', 'name': 'cantilever'}, {'id': 6269, 'synset': 'cantilever_bridge.n.01', 'name': 'cantilever_bridge'}, {'id': 6270, 'synset': 'cantle.n.01', 'name': 'cantle'}, {'id': 6271, 'synset': 'canton_crepe.n.01', 'name': 'Canton_crepe'}, {'id': 6272, 'synset': 'canvas.n.01', 'name': 'canvas'}, {'id': 6273, 'synset': 'canvas.n.06', 'name': 'canvas'}, {'id': 6274, 'synset': 'canvas_tent.n.01', 'name': 'canvas_tent'}, {'id': 6275, 'synset': 'cap.n.04', 'name': 'cap'}, {'id': 6276, 'synset': 'capacitor.n.01', 'name': 'capacitor'}, {'id': 6277, 'synset': 'caparison.n.01', 'name': 'caparison'}, {'id': 6278, 'synset': 'capital_ship.n.01', 'name': 'capital_ship'}, {'id': 6279, 'synset': 'capitol.n.01', 'name': 'capitol'}, {'id': 6280, 'synset': 'cap_opener.n.01', 'name': 'cap_opener'}, {'id': 6281, 'synset': 'capote.n.02', 'name': 'capote'}, {'id': 6282, 'synset': 'capote.n.01', 'name': 'capote'}, {'id': 6283, 'synset': 'cap_screw.n.01', 'name': 'cap_screw'}, {'id': 6284, 'synset': 'capstan.n.01', 'name': 'capstan'}, {'id': 6285, 'synset': 'capstone.n.02', 'name': 'capstone'}, {'id': 6286, 'synset': 'capsule.n.01', 'name': 'capsule'}, {'id': 6287, 'synset': "captain's_chair.n.01", 'name': "captain's_chair"}, {'id': 6288, 'synset': 'carabiner.n.01', 'name': 'carabiner'}, {'id': 6289, 'synset': 'carafe.n.01', 'name': 'carafe'}, {'id': 6290, 'synset': 'caravansary.n.01', 'name': 'caravansary'}, {'id': 6291, 'synset': 'carbine.n.01', 'name': 'carbine'}, {'id': 6292, 'synset': 'car_bomb.n.01', 'name': 'car_bomb'}, {'id': 6293, 'synset': 'carbon_arc_lamp.n.01', 'name': 'carbon_arc_lamp'}, {'id': 6294, 'synset': 'carboy.n.01', 'name': 'carboy'}, {'id': 6295, 'synset': 'carburetor.n.01', 'name': 'carburetor'}, {'id': 6296, 'synset': 'car_carrier.n.01', 'name': 'car_carrier'}, {'id': 6297, 'synset': 'cardcase.n.01', 'name': 'cardcase'}, {'id': 6298, 'synset': 'cardiac_monitor.n.01', 'name': 'cardiac_monitor'}, {'id': 6299, 'synset': 'card_index.n.01', 'name': 'card_index'}, {'id': 6300, 'synset': 'cardiograph.n.01', 'name': 'cardiograph'}, {'id': 6301, 'synset': 'cardioid_microphone.n.01', 'name': 'cardioid_microphone'}, {'id': 6302, 'synset': 'car_door.n.01', 'name': 'car_door'}, {'id': 6303, 'synset': 'cardroom.n.01', 'name': 'cardroom'}, {'id': 6304, 'synset': 'card_table.n.02', 'name': 'card_table'}, {'id': 6305, 'synset': 'card_table.n.01', 'name': 'card_table'}, {'id': 6306, 'synset': 'car-ferry.n.01', 'name': 'car-ferry'}, {'id': 6307, 'synset': 'cargo_area.n.01', 'name': 'cargo_area'}, {'id': 6308, 'synset': 'cargo_container.n.01', 'name': 'cargo_container'}, {'id': 6309, 'synset': 'cargo_door.n.01', 'name': 'cargo_door'}, {'id': 6310, 'synset': 'cargo_hatch.n.01', 'name': 'cargo_hatch'}, {'id': 6311, 'synset': 'cargo_helicopter.n.01', 'name': 'cargo_helicopter'}, {'id': 6312, 'synset': 'cargo_liner.n.01', 'name': 'cargo_liner'}, {'id': 6313, 'synset': 'carillon.n.01', 'name': 'carillon'}, {'id': 6314, 'synset': 'car_mirror.n.01', 'name': 'car_mirror'}, {'id': 6315, 'synset': 'caroche.n.01', 'name': 'caroche'}, {'id': 6316, 'synset': 'carousel.n.02', 'name': 'carousel'}, {'id': 6317, 'synset': "carpenter's_hammer.n.01", 'name': "carpenter's_hammer"}, {'id': 6318, 'synset': "carpenter's_kit.n.01", 'name': "carpenter's_kit"}, {'id': 6319, 'synset': "carpenter's_level.n.01", 'name': "carpenter's_level"}, {'id': 6320, 'synset': "carpenter's_mallet.n.01", 'name': "carpenter's_mallet"}, {'id': 6321, 'synset': "carpenter's_rule.n.01", 'name': "carpenter's_rule"}, {'id': 6322, 'synset': "carpenter's_square.n.01", 'name': "carpenter's_square"}, {'id': 6323, 'synset': 'carpetbag.n.01', 'name': 'carpetbag'}, {'id': 6324, 'synset': 'carpet_beater.n.01', 'name': 'carpet_beater'}, {'id': 6325, 'synset': 'carpet_loom.n.01', 'name': 'carpet_loom'}, {'id': 6326, 'synset': 'carpet_pad.n.01', 'name': 'carpet_pad'}, {'id': 6327, 'synset': 'carpet_sweeper.n.01', 'name': 'carpet_sweeper'}, {'id': 6328, 'synset': 'carpet_tack.n.01', 'name': 'carpet_tack'}, {'id': 6329, 'synset': 'carport.n.01', 'name': 'carport'}, {'id': 6330, 'synset': 'carrack.n.01', 'name': 'carrack'}, {'id': 6331, 'synset': 'carrel.n.02', 'name': 'carrel'}, {'id': 6332, 'synset': 'carriage.n.04', 'name': 'carriage'}, {'id': 6333, 'synset': 'carriage_bolt.n.01', 'name': 'carriage_bolt'}, {'id': 6334, 'synset': 'carriageway.n.01', 'name': 'carriageway'}, {'id': 6335, 'synset': 'carriage_wrench.n.01', 'name': 'carriage_wrench'}, {'id': 6336, 'synset': 'carrick_bend.n.01', 'name': 'carrick_bend'}, {'id': 6337, 'synset': 'carrier.n.10', 'name': 'carrier'}, {'id': 6338, 'synset': 'carrycot.n.01', 'name': 'carrycot'}, {'id': 6339, 'synset': 'car_seat.n.01', 'name': 'car_seat'}, {'id': 6340, 'synset': 'car_tire.n.01', 'name': 'car_tire'}, {'id': 6341, 'synset': 'cartouche.n.01', 'name': 'cartouche'}, {'id': 6342, 'synset': 'car_train.n.01', 'name': 'car_train'}, {'id': 6343, 'synset': 'cartridge.n.01', 'name': 'cartridge'}, {'id': 6344, 'synset': 'cartridge.n.04', 'name': 'cartridge'}, {'id': 6345, 'synset': 'cartridge_belt.n.01', 'name': 'cartridge_belt'}, {'id': 6346, 'synset': 'cartridge_extractor.n.01', 'name': 'cartridge_extractor'}, {'id': 6347, 'synset': 'cartridge_fuse.n.01', 'name': 'cartridge_fuse'}, {'id': 6348, 'synset': 'cartridge_holder.n.01', 'name': 'cartridge_holder'}, {'id': 6349, 'synset': 'cartwheel.n.01', 'name': 'cartwheel'}, {'id': 6350, 'synset': 'carving_fork.n.01', 'name': 'carving_fork'}, {'id': 6351, 'synset': 'carving_knife.n.01', 'name': 'carving_knife'}, {'id': 6352, 'synset': 'car_wheel.n.01', 'name': 'car_wheel'}, {'id': 6353, 'synset': 'caryatid.n.01', 'name': 'caryatid'}, {'id': 6354, 'synset': 'cascade_liquefier.n.01', 'name': 'cascade_liquefier'}, {'id': 6355, 'synset': 'cascade_transformer.n.01', 'name': 'cascade_transformer'}, {'id': 6356, 'synset': 'case.n.05', 'name': 'case'}, {'id': 6357, 'synset': 'case.n.20', 'name': 'case'}, {'id': 6358, 'synset': 'case.n.18', 'name': 'case'}, {'id': 6359, 'synset': 'casein_paint.n.01', 'name': 'casein_paint'}, {'id': 6360, 'synset': 'case_knife.n.02', 'name': 'case_knife'}, {'id': 6361, 'synset': 'case_knife.n.01', 'name': 'case_knife'}, {'id': 6362, 'synset': 'casement.n.01', 'name': 'casement'}, {'id': 6363, 'synset': 'casement_window.n.01', 'name': 'casement_window'}, {'id': 6364, 'synset': 'casern.n.01', 'name': 'casern'}, {'id': 6365, 'synset': 'case_shot.n.01', 'name': 'case_shot'}, {'id': 6366, 'synset': 'cash_bar.n.01', 'name': 'cash_bar'}, {'id': 6367, 'synset': 'cashbox.n.01', 'name': 'cashbox'}, {'id': 6368, 'synset': 'cash_machine.n.01', 'name': 'cash_machine'}, {'id': 6369, 'synset': 'cashmere.n.01', 'name': 'cashmere'}, {'id': 6370, 'synset': 'casing.n.03', 'name': 'casing'}, {'id': 6371, 'synset': 'casino.n.01', 'name': 'casino'}, {'id': 6372, 'synset': 'casket.n.02', 'name': 'casket'}, {'id': 6373, 'synset': 'casque.n.01', 'name': 'casque'}, {'id': 6374, 'synset': 'casquet.n.01', 'name': 'casquet'}, {'id': 6375, 'synset': 'cassegrainian_telescope.n.01', 'name': 'Cassegrainian_telescope'}, {'id': 6376, 'synset': 'casserole.n.02', 'name': 'casserole'}, {'id': 6377, 'synset': 'cassette_deck.n.01', 'name': 'cassette_deck'}, {'id': 6378, 'synset': 'cassette_player.n.01', 'name': 'cassette_player'}, {'id': 6379, 'synset': 'cassette_recorder.n.01', 'name': 'cassette_recorder'}, {'id': 6380, 'synset': 'cassette_tape.n.01', 'name': 'cassette_tape'}, {'id': 6381, 'synset': 'cassock.n.01', 'name': 'cassock'}, {'id': 6382, 'synset': 'caster.n.03', 'name': 'caster'}, {'id': 6383, 'synset': 'caster.n.02', 'name': 'caster'}, {'id': 6384, 'synset': 'castle.n.02', 'name': 'castle'}, {'id': 6385, 'synset': 'castle.n.03', 'name': 'castle'}, {'id': 6386, 'synset': 'catacomb.n.01', 'name': 'catacomb'}, {'id': 6387, 'synset': 'catafalque.n.01', 'name': 'catafalque'}, {'id': 6388, 'synset': 'catalytic_converter.n.01', 'name': 'catalytic_converter'}, {'id': 6389, 'synset': 'catalytic_cracker.n.01', 'name': 'catalytic_cracker'}, {'id': 6390, 'synset': 'catamaran.n.01', 'name': 'catamaran'}, {'id': 6391, 'synset': 'catapult.n.03', 'name': 'catapult'}, {'id': 6392, 'synset': 'catapult.n.02', 'name': 'catapult'}, {'id': 6393, 'synset': 'catboat.n.01', 'name': 'catboat'}, {'id': 6394, 'synset': 'cat_box.n.01', 'name': 'cat_box'}, {'id': 6395, 'synset': 'catch.n.07', 'name': 'catch'}, {'id': 6396, 'synset': 'catchall.n.01', 'name': 'catchall'}, {'id': 6397, 'synset': "catcher's_mask.n.01", 'name': "catcher's_mask"}, {'id': 6398, 'synset': 'catchment.n.01', 'name': 'catchment'}, {'id': 6399, 'synset': 'caterpillar.n.02', 'name': 'Caterpillar'}, {'id': 6400, 'synset': 'cathedra.n.01', 'name': 'cathedra'}, {'id': 6401, 'synset': 'cathedral.n.01', 'name': 'cathedral'}, {'id': 6402, 'synset': 'cathedral.n.02', 'name': 'cathedral'}, {'id': 6403, 'synset': 'catheter.n.01', 'name': 'catheter'}, {'id': 6404, 'synset': 'cathode.n.01', 'name': 'cathode'}, {'id': 6405, 'synset': 'cathode-ray_tube.n.01', 'name': 'cathode-ray_tube'}, {'id': 6406, 'synset': "cat-o'-nine-tails.n.01", 'name': "cat-o'-nine-tails"}, {'id': 6407, 'synset': "cat's-paw.n.02", 'name': "cat's-paw"}, {'id': 6408, 'synset': 'catsup_bottle.n.01', 'name': 'catsup_bottle'}, {'id': 6409, 'synset': 'cattle_car.n.01', 'name': 'cattle_car'}, {'id': 6410, 'synset': 'cattle_guard.n.01', 'name': 'cattle_guard'}, {'id': 6411, 'synset': 'cattleship.n.01', 'name': 'cattleship'}, {'id': 6412, 'synset': 'cautery.n.01', 'name': 'cautery'}, {'id': 6413, 'synset': 'cavalier_hat.n.01', 'name': 'cavalier_hat'}, {'id': 6414, 'synset': 'cavalry_sword.n.01', 'name': 'cavalry_sword'}, {'id': 6415, 'synset': 'cavetto.n.01', 'name': 'cavetto'}, {'id': 6416, 'synset': 'cavity_wall.n.01', 'name': 'cavity_wall'}, {'id': 6417, 'synset': 'c_battery.n.01', 'name': 'C_battery'}, {'id': 6418, 'synset': 'c-clamp.n.01', 'name': 'C-clamp'}, {'id': 6419, 'synset': 'cd_drive.n.01', 'name': 'CD_drive'}, {'id': 6420, 'synset': 'cd-r.n.01', 'name': 'CD-R'}, {'id': 6421, 'synset': 'cd-rom.n.01', 'name': 'CD-ROM'}, {'id': 6422, 'synset': 'cd-rom_drive.n.01', 'name': 'CD-ROM_drive'}, {'id': 6423, 'synset': 'cedar_chest.n.01', 'name': 'cedar_chest'}, {'id': 6424, 'synset': 'ceiling.n.01', 'name': 'ceiling'}, {'id': 6425, 'synset': 'celesta.n.01', 'name': 'celesta'}, {'id': 6426, 'synset': 'cell.n.03', 'name': 'cell'}, {'id': 6427, 'synset': 'cell.n.07', 'name': 'cell'}, {'id': 6428, 'synset': 'cellar.n.03', 'name': 'cellar'}, {'id': 6429, 'synset': 'cellblock.n.01', 'name': 'cellblock'}, {'id': 6430, 'synset': 'cello.n.01', 'name': 'cello'}, {'id': 6431, 'synset': 'cellophane.n.01', 'name': 'cellophane'}, {'id': 6432, 'synset': 'cellulose_tape.n.01', 'name': 'cellulose_tape'}, {'id': 6433, 'synset': 'cenotaph.n.01', 'name': 'cenotaph'}, {'id': 6434, 'synset': 'censer.n.01', 'name': 'censer'}, {'id': 6435, 'synset': 'center.n.03', 'name': 'center'}, {'id': 6436, 'synset': 'center_punch.n.01', 'name': 'center_punch'}, {'id': 6437, 'synset': 'centigrade_thermometer.n.01', 'name': 'Centigrade_thermometer'}, {'id': 6438, 'synset': 'central_processing_unit.n.01', 'name': 'central_processing_unit'}, {'id': 6439, 'synset': 'centrifugal_pump.n.01', 'name': 'centrifugal_pump'}, {'id': 6440, 'synset': 'centrifuge.n.01', 'name': 'centrifuge'}, {'id': 6441, 'synset': 'ceramic.n.01', 'name': 'ceramic'}, {'id': 6442, 'synset': 'ceramic_ware.n.01', 'name': 'ceramic_ware'}, {'id': 6443, 'synset': 'cereal_bowl.n.01', 'name': 'cereal_bowl'}, {'id': 6444, 'synset': 'cereal_box.n.01', 'name': 'cereal_box'}, {'id': 6445, 'synset': 'cerecloth.n.01', 'name': 'cerecloth'}, {'id': 6446, 'synset': 'cesspool.n.01', 'name': 'cesspool'}, {'id': 6447, 'synset': 'chachka.n.02', 'name': 'chachka'}, {'id': 6448, 'synset': 'chador.n.01', 'name': 'chador'}, {'id': 6449, 'synset': 'chafing_dish.n.01', 'name': 'chafing_dish'}, {'id': 6450, 'synset': 'chain.n.03', 'name': 'chain'}, {'id': 6451, 'synset': 'chain.n.05', 'name': 'chain'}, {'id': 6452, 'synset': 'chainlink_fence.n.01', 'name': 'chainlink_fence'}, {'id': 6453, 'synset': 'chain_printer.n.01', 'name': 'chain_printer'}, {'id': 6454, 'synset': 'chain_saw.n.01', 'name': 'chain_saw'}, {'id': 6455, 'synset': 'chain_store.n.01', 'name': 'chain_store'}, {'id': 6456, 'synset': 'chain_tongs.n.01', 'name': 'chain_tongs'}, {'id': 6457, 'synset': 'chain_wrench.n.01', 'name': 'chain_wrench'}, {'id': 6458, 'synset': 'chair.n.05', 'name': 'chair'}, {'id': 6459, 'synset': 'chair_of_state.n.01', 'name': 'chair_of_state'}, {'id': 6460, 'synset': 'chairlift.n.01', 'name': 'chairlift'}, {'id': 6461, 'synset': 'chaise.n.02', 'name': 'chaise'}, {'id': 6462, 'synset': 'chalet.n.01', 'name': 'chalet'}, {'id': 6463, 'synset': 'chalk.n.04', 'name': 'chalk'}, {'id': 6464, 'synset': 'challis.n.01', 'name': 'challis'}, {'id': 6465, 'synset': 'chamberpot.n.01', 'name': 'chamberpot'}, {'id': 6466, 'synset': 'chambray.n.01', 'name': 'chambray'}, {'id': 6467, 'synset': 'chamfer_bit.n.01', 'name': 'chamfer_bit'}, {'id': 6468, 'synset': 'chamfer_plane.n.01', 'name': 'chamfer_plane'}, {'id': 6469, 'synset': 'chamois_cloth.n.01', 'name': 'chamois_cloth'}, {'id': 6470, 'synset': 'chancel.n.01', 'name': 'chancel'}, {'id': 6471, 'synset': 'chancellery.n.01', 'name': 'chancellery'}, {'id': 6472, 'synset': 'chancery.n.02', 'name': 'chancery'}, {'id': 6473, 'synset': 'chandlery.n.01', 'name': 'chandlery'}, {'id': 6474, 'synset': 'chanfron.n.01', 'name': 'chanfron'}, {'id': 6475, 'synset': 'chanter.n.01', 'name': 'chanter'}, {'id': 6476, 'synset': 'chantry.n.02', 'name': 'chantry'}, {'id': 6477, 'synset': 'chapel.n.01', 'name': 'chapel'}, {'id': 6478, 'synset': 'chapterhouse.n.02', 'name': 'chapterhouse'}, {'id': 6479, 'synset': 'chapterhouse.n.01', 'name': 'chapterhouse'}, {'id': 6480, 'synset': 'character_printer.n.01', 'name': 'character_printer'}, {'id': 6481, 'synset': 'charcuterie.n.01', 'name': 'charcuterie'}, {'id': 6482, 'synset': 'charge-exchange_accelerator.n.01', 'name': 'charge-exchange_accelerator'}, {'id': 6483, 'synset': 'charger.n.02', 'name': 'charger'}, {'id': 6484, 'synset': 'chariot.n.01', 'name': 'chariot'}, {'id': 6485, 'synset': 'chariot.n.02', 'name': 'chariot'}, {'id': 6486, 'synset': 'charnel_house.n.01', 'name': 'charnel_house'}, {'id': 6487, 'synset': 'chassis.n.03', 'name': 'chassis'}, {'id': 6488, 'synset': 'chassis.n.02', 'name': 'chassis'}, {'id': 6489, 'synset': 'chasuble.n.01', 'name': 'chasuble'}, {'id': 6490, 'synset': 'chateau.n.01', 'name': 'chateau'}, {'id': 6491, 'synset': 'chatelaine.n.02', 'name': 'chatelaine'}, {'id': 6492, 'synset': 'checker.n.03', 'name': 'checker'}, {'id': 6493, 'synset': 'checkout.n.03', 'name': 'checkout'}, {'id': 6494, 'synset': 'cheekpiece.n.01', 'name': 'cheekpiece'}, {'id': 6495, 'synset': 'cheeseboard.n.01', 'name': 'cheeseboard'}, {'id': 6496, 'synset': 'cheesecloth.n.01', 'name': 'cheesecloth'}, {'id': 6497, 'synset': 'cheese_cutter.n.01', 'name': 'cheese_cutter'}, {'id': 6498, 'synset': 'cheese_press.n.01', 'name': 'cheese_press'}, {'id': 6499, 'synset': 'chemical_bomb.n.01', 'name': 'chemical_bomb'}, {'id': 6500, 'synset': 'chemical_plant.n.01', 'name': 'chemical_plant'}, {'id': 6501, 'synset': 'chemical_reactor.n.01', 'name': 'chemical_reactor'}, {'id': 6502, 'synset': 'chemise.n.02', 'name': 'chemise'}, {'id': 6503, 'synset': 'chemise.n.01', 'name': 'chemise'}, {'id': 6504, 'synset': 'chenille.n.02', 'name': 'chenille'}, {'id': 6505, 'synset': 'chessman.n.01', 'name': 'chessman'}, {'id': 6506, 'synset': 'chest.n.02', 'name': 'chest'}, {'id': 6507, 'synset': 'chesterfield.n.02', 'name': 'chesterfield'}, {'id': 6508, 'synset': 'chest_of_drawers.n.01', 'name': 'chest_of_drawers'}, {'id': 6509, 'synset': 'chest_protector.n.01', 'name': 'chest_protector'}, {'id': 6510, 'synset': 'cheval-de-frise.n.01', 'name': 'cheval-de-frise'}, {'id': 6511, 'synset': 'cheval_glass.n.01', 'name': 'cheval_glass'}, {'id': 6512, 'synset': 'chicane.n.02', 'name': 'chicane'}, {'id': 6513, 'synset': 'chicken_coop.n.01', 'name': 'chicken_coop'}, {'id': 6514, 'synset': 'chicken_wire.n.01', 'name': 'chicken_wire'}, {'id': 6515, 'synset': 'chicken_yard.n.01', 'name': 'chicken_yard'}, {'id': 6516, 'synset': 'chiffon.n.01', 'name': 'chiffon'}, {'id': 6517, 'synset': 'chiffonier.n.01', 'name': 'chiffonier'}, {'id': 6518, 'synset': "child's_room.n.01", 'name': "child's_room"}, {'id': 6519, 'synset': 'chimney_breast.n.01', 'name': 'chimney_breast'}, {'id': 6520, 'synset': 'chimney_corner.n.01', 'name': 'chimney_corner'}, {'id': 6521, 'synset': 'china.n.02', 'name': 'china'}, {'id': 6522, 'synset': 'china_cabinet.n.01', 'name': 'china_cabinet'}, {'id': 6523, 'synset': 'chinchilla.n.02', 'name': 'chinchilla'}, {'id': 6524, 'synset': 'chinese_lantern.n.01', 'name': 'Chinese_lantern'}, {'id': 6525, 'synset': 'chinese_puzzle.n.01', 'name': 'Chinese_puzzle'}, {'id': 6526, 'synset': 'chinning_bar.n.01', 'name': 'chinning_bar'}, {'id': 6527, 'synset': 'chino.n.02', 'name': 'chino'}, {'id': 6528, 'synset': 'chino.n.01', 'name': 'chino'}, {'id': 6529, 'synset': 'chin_rest.n.01', 'name': 'chin_rest'}, {'id': 6530, 'synset': 'chin_strap.n.01', 'name': 'chin_strap'}, {'id': 6531, 'synset': 'chintz.n.01', 'name': 'chintz'}, {'id': 6532, 'synset': 'chip.n.07', 'name': 'chip'}, {'id': 6533, 'synset': 'chisel.n.01', 'name': 'chisel'}, {'id': 6534, 'synset': 'chlamys.n.02', 'name': 'chlamys'}, {'id': 6535, 'synset': 'choir.n.03', 'name': 'choir'}, {'id': 6536, 'synset': 'choir_loft.n.01', 'name': 'choir_loft'}, {'id': 6537, 'synset': 'choke.n.02', 'name': 'choke'}, {'id': 6538, 'synset': 'choke.n.01', 'name': 'choke'}, {'id': 6539, 'synset': 'chokey.n.01', 'name': 'chokey'}, {'id': 6540, 'synset': 'choo-choo.n.01', 'name': 'choo-choo'}, {'id': 6541, 'synset': 'chopine.n.01', 'name': 'chopine'}, {'id': 6542, 'synset': 'chordophone.n.01', 'name': 'chordophone'}, {'id': 6543, 'synset': 'christmas_stocking.n.01', 'name': 'Christmas_stocking'}, {'id': 6544, 'synset': 'chronograph.n.01', 'name': 'chronograph'}, {'id': 6545, 'synset': 'chronometer.n.01', 'name': 'chronometer'}, {'id': 6546, 'synset': 'chronoscope.n.01', 'name': 'chronoscope'}, {'id': 6547, 'synset': 'chuck.n.03', 'name': 'chuck'}, {'id': 6548, 'synset': 'chuck_wagon.n.01', 'name': 'chuck_wagon'}, {'id': 6549, 'synset': 'chukka.n.02', 'name': 'chukka'}, {'id': 6550, 'synset': 'church.n.02', 'name': 'church'}, {'id': 6551, 'synset': 'church_bell.n.01', 'name': 'church_bell'}, {'id': 6552, 'synset': 'church_hat.n.01', 'name': 'church_hat'}, {'id': 6553, 'synset': 'church_key.n.01', 'name': 'church_key'}, {'id': 6554, 'synset': 'church_tower.n.01', 'name': 'church_tower'}, {'id': 6555, 'synset': 'churidars.n.01', 'name': 'churidars'}, {'id': 6556, 'synset': 'churn.n.01', 'name': 'churn'}, {'id': 6557, 'synset': 'ciderpress.n.01', 'name': 'ciderpress'}, {'id': 6558, 'synset': 'cigar_band.n.01', 'name': 'cigar_band'}, {'id': 6559, 'synset': 'cigar_cutter.n.01', 'name': 'cigar_cutter'}, {'id': 6560, 'synset': 'cigarette_butt.n.01', 'name': 'cigarette_butt'}, {'id': 6561, 'synset': 'cigarette_holder.n.01', 'name': 'cigarette_holder'}, {'id': 6562, 'synset': 'cigar_lighter.n.01', 'name': 'cigar_lighter'}, {'id': 6563, 'synset': 'cinch.n.02', 'name': 'cinch'}, {'id': 6564, 'synset': 'cinema.n.02', 'name': 'cinema'}, {'id': 6565, 'synset': 'cinquefoil.n.02', 'name': 'cinquefoil'}, {'id': 6566, 'synset': 'circle.n.08', 'name': 'circle'}, {'id': 6567, 'synset': 'circlet.n.02', 'name': 'circlet'}, {'id': 6568, 'synset': 'circuit.n.01', 'name': 'circuit'}, {'id': 6569, 'synset': 'circuit_board.n.01', 'name': 'circuit_board'}, {'id': 6570, 'synset': 'circuit_breaker.n.01', 'name': 'circuit_breaker'}, {'id': 6571, 'synset': 'circuitry.n.01', 'name': 'circuitry'}, {'id': 6572, 'synset': 'circular_plane.n.01', 'name': 'circular_plane'}, {'id': 6573, 'synset': 'circular_saw.n.01', 'name': 'circular_saw'}, {'id': 6574, 'synset': 'circus_tent.n.01', 'name': 'circus_tent'}, {'id': 6575, 'synset': 'cistern.n.03', 'name': 'cistern'}, {'id': 6576, 'synset': 'cittern.n.01', 'name': 'cittern'}, {'id': 6577, 'synset': 'city_hall.n.01', 'name': 'city_hall'}, {'id': 6578, 'synset': 'cityscape.n.02', 'name': 'cityscape'}, {'id': 6579, 'synset': 'city_university.n.01', 'name': 'city_university'}, {'id': 6580, 'synset': 'civies.n.01', 'name': 'civies'}, {'id': 6581, 'synset': 'civilian_clothing.n.01', 'name': 'civilian_clothing'}, {'id': 6582, 'synset': 'clack_valve.n.01', 'name': 'clack_valve'}, {'id': 6583, 'synset': 'clamp.n.01', 'name': 'clamp'}, {'id': 6584, 'synset': 'clamshell.n.02', 'name': 'clamshell'}, {'id': 6585, 'synset': 'clapper.n.03', 'name': 'clapper'}, {'id': 6586, 'synset': 'clapperboard.n.01', 'name': 'clapperboard'}, {'id': 6587, 'synset': 'clarence.n.01', 'name': 'clarence'}, {'id': 6588, 'synset': 'clark_cell.n.01', 'name': 'Clark_cell'}, {'id': 6589, 'synset': 'clasp_knife.n.01', 'name': 'clasp_knife'}, {'id': 6590, 'synset': 'classroom.n.01', 'name': 'classroom'}, {'id': 6591, 'synset': 'clavichord.n.01', 'name': 'clavichord'}, {'id': 6592, 'synset': 'clavier.n.02', 'name': 'clavier'}, {'id': 6593, 'synset': 'clay_pigeon.n.01', 'name': 'clay_pigeon'}, {'id': 6594, 'synset': 'claymore_mine.n.01', 'name': 'claymore_mine'}, {'id': 6595, 'synset': 'claymore.n.01', 'name': 'claymore'}, {'id': 6596, 'synset': 'cleaners.n.01', 'name': 'cleaners'}, {'id': 6597, 'synset': 'cleaning_implement.n.01', 'name': 'cleaning_implement'}, {'id': 6598, 'synset': 'cleaning_pad.n.01', 'name': 'cleaning_pad'}, {'id': 6599, 'synset': 'clean_room.n.01', 'name': 'clean_room'}, {'id': 6600, 'synset': 'clearway.n.01', 'name': 'clearway'}, {'id': 6601, 'synset': 'cleat.n.01', 'name': 'cleat'}, {'id': 6602, 'synset': 'cleats.n.01', 'name': 'cleats'}, {'id': 6603, 'synset': 'cleaver.n.01', 'name': 'cleaver'}, {'id': 6604, 'synset': 'clerestory.n.01', 'name': 'clerestory'}, {'id': 6605, 'synset': 'clevis.n.01', 'name': 'clevis'}, {'id': 6606, 'synset': 'clews.n.01', 'name': 'clews'}, {'id': 6607, 'synset': 'cliff_dwelling.n.01', 'name': 'cliff_dwelling'}, {'id': 6608, 'synset': 'climbing_frame.n.01', 'name': 'climbing_frame'}, {'id': 6609, 'synset': 'clinch.n.03', 'name': 'clinch'}, {'id': 6610, 'synset': 'clinch.n.02', 'name': 'clinch'}, {'id': 6611, 'synset': 'clincher.n.03', 'name': 'clincher'}, {'id': 6612, 'synset': 'clinic.n.03', 'name': 'clinic'}, {'id': 6613, 'synset': 'clinical_thermometer.n.01', 'name': 'clinical_thermometer'}, {'id': 6614, 'synset': 'clinker.n.02', 'name': 'clinker'}, {'id': 6615, 'synset': 'clinometer.n.01', 'name': 'clinometer'}, {'id': 6616, 'synset': 'clip_lead.n.01', 'name': 'clip_lead'}, {'id': 6617, 'synset': 'clip-on.n.01', 'name': 'clip-on'}, {'id': 6618, 'synset': 'clipper.n.04', 'name': 'clipper'}, {'id': 6619, 'synset': 'clipper.n.02', 'name': 'clipper'}, {'id': 6620, 'synset': 'cloak.n.01', 'name': 'cloak'}, {'id': 6621, 'synset': 'cloakroom.n.02', 'name': 'cloakroom'}, {'id': 6622, 'synset': 'cloche.n.02', 'name': 'cloche'}, {'id': 6623, 'synset': 'cloche.n.01', 'name': 'cloche'}, {'id': 6624, 'synset': 'clock_pendulum.n.01', 'name': 'clock_pendulum'}, {'id': 6625, 'synset': 'clock_radio.n.01', 'name': 'clock_radio'}, {'id': 6626, 'synset': 'clockwork.n.01', 'name': 'clockwork'}, {'id': 6627, 'synset': 'clog.n.01', 'name': 'clog'}, {'id': 6628, 'synset': 'cloisonne.n.01', 'name': 'cloisonne'}, {'id': 6629, 'synset': 'cloister.n.02', 'name': 'cloister'}, {'id': 6630, 'synset': 'closed_circuit.n.01', 'name': 'closed_circuit'}, {'id': 6631, 'synset': 'closed-circuit_television.n.01', 'name': 'closed-circuit_television'}, {'id': 6632, 'synset': 'closed_loop.n.01', 'name': 'closed_loop'}, {'id': 6633, 'synset': 'closet.n.04', 'name': 'closet'}, {'id': 6634, 'synset': 'closeup_lens.n.01', 'name': 'closeup_lens'}, {'id': 6635, 'synset': 'cloth_cap.n.01', 'name': 'cloth_cap'}, {'id': 6636, 'synset': 'cloth_covering.n.01', 'name': 'cloth_covering'}, {'id': 6637, 'synset': 'clothesbrush.n.01', 'name': 'clothesbrush'}, {'id': 6638, 'synset': 'clothes_closet.n.01', 'name': 'clothes_closet'}, {'id': 6639, 'synset': 'clothes_dryer.n.01', 'name': 'clothes_dryer'}, {'id': 6640, 'synset': 'clotheshorse.n.01', 'name': 'clotheshorse'}, {'id': 6641, 'synset': 'clothes_tree.n.01', 'name': 'clothes_tree'}, {'id': 6642, 'synset': 'clothing.n.01', 'name': 'clothing'}, {'id': 6643, 'synset': 'clothing_store.n.01', 'name': 'clothing_store'}, {'id': 6644, 'synset': 'clout_nail.n.01', 'name': 'clout_nail'}, {'id': 6645, 'synset': 'clove_hitch.n.01', 'name': 'clove_hitch'}, {'id': 6646, 'synset': 'club_car.n.01', 'name': 'club_car'}, {'id': 6647, 'synset': 'clubroom.n.01', 'name': 'clubroom'}, {'id': 6648, 'synset': 'cluster_bomb.n.01', 'name': 'cluster_bomb'}, {'id': 6649, 'synset': 'clutch.n.07', 'name': 'clutch'}, {'id': 6650, 'synset': 'clutch.n.06', 'name': 'clutch'}, {'id': 6651, 'synset': 'coach.n.04', 'name': 'coach'}, {'id': 6652, 'synset': 'coach_house.n.01', 'name': 'coach_house'}, {'id': 6653, 'synset': 'coal_car.n.01', 'name': 'coal_car'}, {'id': 6654, 'synset': 'coal_chute.n.01', 'name': 'coal_chute'}, {'id': 6655, 'synset': 'coal_house.n.01', 'name': 'coal_house'}, {'id': 6656, 'synset': 'coal_shovel.n.01', 'name': 'coal_shovel'}, {'id': 6657, 'synset': 'coaming.n.01', 'name': 'coaming'}, {'id': 6658, 'synset': 'coaster_brake.n.01', 'name': 'coaster_brake'}, {'id': 6659, 'synset': 'coat_button.n.01', 'name': 'coat_button'}, {'id': 6660, 'synset': 'coat_closet.n.01', 'name': 'coat_closet'}, {'id': 6661, 'synset': 'coatdress.n.01', 'name': 'coatdress'}, {'id': 6662, 'synset': 'coatee.n.01', 'name': 'coatee'}, {'id': 6663, 'synset': 'coating.n.01', 'name': 'coating'}, {'id': 6664, 'synset': 'coating.n.03', 'name': 'coating'}, {'id': 6665, 'synset': 'coat_of_paint.n.01', 'name': 'coat_of_paint'}, {'id': 6666, 'synset': 'coattail.n.01', 'name': 'coattail'}, {'id': 6667, 'synset': 'coaxial_cable.n.01', 'name': 'coaxial_cable'}, {'id': 6668, 'synset': 'cobweb.n.03', 'name': 'cobweb'}, {'id': 6669, 'synset': 'cobweb.n.01', 'name': 'cobweb'}, {'id': 6670, 'synset': 'cockcroft_and_walton_accelerator.n.01', 'name': 'Cockcroft_and_Walton_accelerator'}, {'id': 6671, 'synset': 'cocked_hat.n.01', 'name': 'cocked_hat'}, {'id': 6672, 'synset': 'cockhorse.n.01', 'name': 'cockhorse'}, {'id': 6673, 'synset': 'cockleshell.n.01', 'name': 'cockleshell'}, {'id': 6674, 'synset': 'cockpit.n.01', 'name': 'cockpit'}, {'id': 6675, 'synset': 'cockpit.n.03', 'name': 'cockpit'}, {'id': 6676, 'synset': 'cockpit.n.02', 'name': 'cockpit'}, {'id': 6677, 'synset': 'cockscomb.n.03', 'name': 'cockscomb'}, {'id': 6678, 'synset': 'cocktail_dress.n.01', 'name': 'cocktail_dress'}, {'id': 6679, 'synset': 'cocktail_lounge.n.01', 'name': 'cocktail_lounge'}, {'id': 6680, 'synset': 'cocktail_shaker.n.01', 'name': 'cocktail_shaker'}, {'id': 6681, 'synset': 'cocotte.n.02', 'name': 'cocotte'}, {'id': 6682, 'synset': 'codpiece.n.01', 'name': 'codpiece'}, {'id': 6683, 'synset': 'coelostat.n.01', 'name': 'coelostat'}, {'id': 6684, 'synset': 'coffee_can.n.01', 'name': 'coffee_can'}, {'id': 6685, 'synset': 'coffee_cup.n.01', 'name': 'coffee_cup'}, {'id': 6686, 'synset': 'coffee_filter.n.01', 'name': 'coffee_filter'}, {'id': 6687, 'synset': 'coffee_mill.n.01', 'name': 'coffee_mill'}, {'id': 6688, 'synset': 'coffee_mug.n.01', 'name': 'coffee_mug'}, {'id': 6689, 'synset': 'coffee_stall.n.01', 'name': 'coffee_stall'}, {'id': 6690, 'synset': 'coffee_urn.n.01', 'name': 'coffee_urn'}, {'id': 6691, 'synset': 'coffer.n.02', 'name': 'coffer'}, {'id': 6692, 'synset': 'coffey_still.n.01', 'name': 'Coffey_still'}, {'id': 6693, 'synset': 'coffin.n.01', 'name': 'coffin'}, {'id': 6694, 'synset': 'cog.n.02', 'name': 'cog'}, {'id': 6695, 'synset': 'coif.n.02', 'name': 'coif'}, {'id': 6696, 'synset': 'coil.n.01', 'name': 'coil'}, {'id': 6697, 'synset': 'coil.n.06', 'name': 'coil'}, {'id': 6698, 'synset': 'coil.n.03', 'name': 'coil'}, {'id': 6699, 'synset': 'coil_spring.n.01', 'name': 'coil_spring'}, {'id': 6700, 'synset': 'coin_box.n.01', 'name': 'coin_box'}, {'id': 6701, 'synset': 'cold_cathode.n.01', 'name': 'cold_cathode'}, {'id': 6702, 'synset': 'cold_chisel.n.01', 'name': 'cold_chisel'}, {'id': 6703, 'synset': 'cold_cream.n.01', 'name': 'cold_cream'}, {'id': 6704, 'synset': 'cold_frame.n.01', 'name': 'cold_frame'}, {'id': 6705, 'synset': 'collar.n.01', 'name': 'collar'}, {'id': 6706, 'synset': 'collar.n.03', 'name': 'collar'}, {'id': 6707, 'synset': 'college.n.03', 'name': 'college'}, {'id': 6708, 'synset': 'collet.n.02', 'name': 'collet'}, {'id': 6709, 'synset': 'collider.n.01', 'name': 'collider'}, {'id': 6710, 'synset': 'colliery.n.01', 'name': 'colliery'}, {'id': 6711, 'synset': 'collimator.n.02', 'name': 'collimator'}, {'id': 6712, 'synset': 'collimator.n.01', 'name': 'collimator'}, {'id': 6713, 'synset': 'cologne.n.02', 'name': 'cologne'}, {'id': 6714, 'synset': 'colonnade.n.01', 'name': 'colonnade'}, {'id': 6715, 'synset': 'colonoscope.n.01', 'name': 'colonoscope'}, {'id': 6716, 'synset': 'colorimeter.n.01', 'name': 'colorimeter'}, {'id': 6717, 'synset': 'colors.n.02', 'name': 'colors'}, {'id': 6718, 'synset': 'color_television.n.01', 'name': 'color_television'}, {'id': 6719, 'synset': 'color_tube.n.01', 'name': 'color_tube'}, {'id': 6720, 'synset': 'color_wash.n.01', 'name': 'color_wash'}, {'id': 6721, 'synset': 'colt.n.02', 'name': 'Colt'}, {'id': 6722, 'synset': 'colter.n.01', 'name': 'colter'}, {'id': 6723, 'synset': 'columbarium.n.03', 'name': 'columbarium'}, {'id': 6724, 'synset': 'columbarium.n.02', 'name': 'columbarium'}, {'id': 6725, 'synset': 'column.n.07', 'name': 'column'}, {'id': 6726, 'synset': 'column.n.06', 'name': 'column'}, {'id': 6727, 'synset': 'comb.n.01', 'name': 'comb'}, {'id': 6728, 'synset': 'comb.n.03', 'name': 'comb'}, {'id': 6729, 'synset': 'comber.n.03', 'name': 'comber'}, {'id': 6730, 'synset': 'combination_plane.n.01', 'name': 'combination_plane'}, {'id': 6731, 'synset': 'combine.n.01', 'name': 'combine'}, {'id': 6732, 'synset': 'command_module.n.01', 'name': 'command_module'}, {'id': 6733, 'synset': 'commissary.n.01', 'name': 'commissary'}, {'id': 6734, 'synset': 'commissary.n.02', 'name': 'commissary'}, {'id': 6735, 'synset': 'commodity.n.01', 'name': 'commodity'}, {'id': 6736, 'synset': 'common_ax.n.01', 'name': 'common_ax'}, {'id': 6737, 'synset': 'common_room.n.01', 'name': 'common_room'}, {'id': 6738, 'synset': 'communications_satellite.n.01', 'name': 'communications_satellite'}, {'id': 6739, 'synset': 'communication_system.n.01', 'name': 'communication_system'}, {'id': 6740, 'synset': 'community_center.n.01', 'name': 'community_center'}, {'id': 6741, 'synset': 'commutator.n.01', 'name': 'commutator'}, {'id': 6742, 'synset': 'commuter.n.01', 'name': 'commuter'}, {'id': 6743, 'synset': 'compact.n.01', 'name': 'compact'}, {'id': 6744, 'synset': 'compact.n.03', 'name': 'compact'}, {'id': 6745, 'synset': 'compact_disk.n.01', 'name': 'compact_disk'}, {'id': 6746, 'synset': 'compact-disk_burner.n.01', 'name': 'compact-disk_burner'}, {'id': 6747, 'synset': 'companionway.n.01', 'name': 'companionway'}, {'id': 6748, 'synset': 'compartment.n.02', 'name': 'compartment'}, {'id': 6749, 'synset': 'compartment.n.01', 'name': 'compartment'}, {'id': 6750, 'synset': 'compass.n.04', 'name': 'compass'}, {'id': 6751, 'synset': 'compass_card.n.01', 'name': 'compass_card'}, {'id': 6752, 'synset': 'compass_saw.n.01', 'name': 'compass_saw'}, {'id': 6753, 'synset': 'compound.n.03', 'name': 'compound'}, {'id': 6754, 'synset': 'compound_lens.n.01', 'name': 'compound_lens'}, {'id': 6755, 'synset': 'compound_lever.n.01', 'name': 'compound_lever'}, {'id': 6756, 'synset': 'compound_microscope.n.01', 'name': 'compound_microscope'}, {'id': 6757, 'synset': 'compress.n.01', 'name': 'compress'}, {'id': 6758, 'synset': 'compression_bandage.n.01', 'name': 'compression_bandage'}, {'id': 6759, 'synset': 'compressor.n.01', 'name': 'compressor'}, {'id': 6760, 'synset': 'computer.n.01', 'name': 'computer'}, {'id': 6761, 'synset': 'computer_circuit.n.01', 'name': 'computer_circuit'}, {'id': 6762, 'synset': 'computerized_axial_tomography_scanner.n.01', 'name': 'computerized_axial_tomography_scanner'}, {'id': 6763, 'synset': 'computer_monitor.n.01', 'name': 'computer_monitor'}, {'id': 6764, 'synset': 'computer_network.n.01', 'name': 'computer_network'}, {'id': 6765, 'synset': 'computer_screen.n.01', 'name': 'computer_screen'}, {'id': 6766, 'synset': 'computer_store.n.01', 'name': 'computer_store'}, {'id': 6767, 'synset': 'computer_system.n.01', 'name': 'computer_system'}, {'id': 6768, 'synset': 'concentration_camp.n.01', 'name': 'concentration_camp'}, {'id': 6769, 'synset': 'concert_grand.n.01', 'name': 'concert_grand'}, {'id': 6770, 'synset': 'concert_hall.n.01', 'name': 'concert_hall'}, {'id': 6771, 'synset': 'concertina.n.02', 'name': 'concertina'}, {'id': 6772, 'synset': 'concertina.n.01', 'name': 'concertina'}, {'id': 6773, 'synset': 'concrete_mixer.n.01', 'name': 'concrete_mixer'}, {'id': 6774, 'synset': 'condensation_pump.n.01', 'name': 'condensation_pump'}, {'id': 6775, 'synset': 'condenser.n.04', 'name': 'condenser'}, {'id': 6776, 'synset': 'condenser.n.03', 'name': 'condenser'}, {'id': 6777, 'synset': 'condenser.n.02', 'name': 'condenser'}, {'id': 6778, 'synset': 'condenser_microphone.n.01', 'name': 'condenser_microphone'}, {'id': 6779, 'synset': 'condominium.n.02', 'name': 'condominium'}, {'id': 6780, 'synset': 'condominium.n.01', 'name': 'condominium'}, {'id': 6781, 'synset': 'conductor.n.04', 'name': 'conductor'}, {'id': 6782, 'synset': 'cone_clutch.n.01', 'name': 'cone_clutch'}, {'id': 6783, 'synset': 'confectionery.n.02', 'name': 'confectionery'}, {'id': 6784, 'synset': 'conference_center.n.01', 'name': 'conference_center'}, {'id': 6785, 'synset': 'conference_room.n.01', 'name': 'conference_room'}, {'id': 6786, 'synset': 'conference_table.n.01', 'name': 'conference_table'}, {'id': 6787, 'synset': 'confessional.n.01', 'name': 'confessional'}, {'id': 6788, 'synset': 'conformal_projection.n.01', 'name': 'conformal_projection'}, {'id': 6789, 'synset': 'congress_boot.n.01', 'name': 'congress_boot'}, {'id': 6790, 'synset': 'conic_projection.n.01', 'name': 'conic_projection'}, {'id': 6791, 'synset': 'connecting_rod.n.01', 'name': 'connecting_rod'}, {'id': 6792, 'synset': 'connecting_room.n.01', 'name': 'connecting_room'}, {'id': 6793, 'synset': 'connection.n.03', 'name': 'connection'}, {'id': 6794, 'synset': 'conning_tower.n.02', 'name': 'conning_tower'}, {'id': 6795, 'synset': 'conning_tower.n.01', 'name': 'conning_tower'}, {'id': 6796, 'synset': 'conservatory.n.03', 'name': 'conservatory'}, {'id': 6797, 'synset': 'conservatory.n.02', 'name': 'conservatory'}, {'id': 6798, 'synset': 'console.n.03', 'name': 'console'}, {'id': 6799, 'synset': 'console.n.02', 'name': 'console'}, {'id': 6800, 'synset': 'console_table.n.01', 'name': 'console_table'}, {'id': 6801, 'synset': 'consulate.n.01', 'name': 'consulate'}, {'id': 6802, 'synset': 'contact.n.07', 'name': 'contact'}, {'id': 6803, 'synset': 'contact.n.09', 'name': 'contact'}, {'id': 6804, 'synset': 'container.n.01', 'name': 'container'}, {'id': 6805, 'synset': 'container_ship.n.01', 'name': 'container_ship'}, {'id': 6806, 'synset': 'containment.n.02', 'name': 'containment'}, {'id': 6807, 'synset': 'contrabassoon.n.01', 'name': 'contrabassoon'}, {'id': 6808, 'synset': 'control_center.n.01', 'name': 'control_center'}, {'id': 6809, 'synset': 'control_circuit.n.01', 'name': 'control_circuit'}, {'id': 6810, 'synset': 'control_key.n.01', 'name': 'control_key'}, {'id': 6811, 'synset': 'control_panel.n.01', 'name': 'control_panel'}, {'id': 6812, 'synset': 'control_rod.n.01', 'name': 'control_rod'}, {'id': 6813, 'synset': 'control_room.n.01', 'name': 'control_room'}, {'id': 6814, 'synset': 'control_system.n.01', 'name': 'control_system'}, {'id': 6815, 'synset': 'control_tower.n.01', 'name': 'control_tower'}, {'id': 6816, 'synset': 'convector.n.01', 'name': 'convector'}, {'id': 6817, 'synset': 'convenience_store.n.01', 'name': 'convenience_store'}, {'id': 6818, 'synset': 'convent.n.01', 'name': 'convent'}, {'id': 6819, 'synset': 'conventicle.n.02', 'name': 'conventicle'}, {'id': 6820, 'synset': 'converging_lens.n.01', 'name': 'converging_lens'}, {'id': 6821, 'synset': 'converter.n.01', 'name': 'converter'}, {'id': 6822, 'synset': 'conveyance.n.03', 'name': 'conveyance'}, {'id': 6823, 'synset': 'conveyer_belt.n.01', 'name': 'conveyer_belt'}, {'id': 6824, 'synset': 'cookfire.n.01', 'name': 'cookfire'}, {'id': 6825, 'synset': 'cookhouse.n.02', 'name': 'cookhouse'}, {'id': 6826, 'synset': 'cookie_cutter.n.01', 'name': 'cookie_cutter'}, {'id': 6827, 'synset': 'cookie_jar.n.01', 'name': 'cookie_jar'}, {'id': 6828, 'synset': 'cookie_sheet.n.01', 'name': 'cookie_sheet'}, {'id': 6829, 'synset': 'cookstove.n.01', 'name': 'cookstove'}, {'id': 6830, 'synset': 'coolant_system.n.01', 'name': 'coolant_system'}, {'id': 6831, 'synset': 'cooling_system.n.02', 'name': 'cooling_system'}, {'id': 6832, 'synset': 'cooling_system.n.01', 'name': 'cooling_system'}, {'id': 6833, 'synset': 'cooling_tower.n.01', 'name': 'cooling_tower'}, {'id': 6834, 'synset': 'coonskin_cap.n.01', 'name': 'coonskin_cap'}, {'id': 6835, 'synset': 'cope.n.02', 'name': 'cope'}, {'id': 6836, 'synset': 'coping_saw.n.01', 'name': 'coping_saw'}, {'id': 6837, 'synset': 'copperware.n.01', 'name': 'copperware'}, {'id': 6838, 'synset': 'copyholder.n.01', 'name': 'copyholder'}, {'id': 6839, 'synset': 'coquille.n.02', 'name': 'coquille'}, {'id': 6840, 'synset': 'coracle.n.01', 'name': 'coracle'}, {'id': 6841, 'synset': 'corbel.n.01', 'name': 'corbel'}, {'id': 6842, 'synset': 'corbel_arch.n.01', 'name': 'corbel_arch'}, {'id': 6843, 'synset': 'corbel_step.n.01', 'name': 'corbel_step'}, {'id': 6844, 'synset': 'corbie_gable.n.01', 'name': 'corbie_gable'}, {'id': 6845, 'synset': 'cord.n.04', 'name': 'cord'}, {'id': 6846, 'synset': 'cord.n.03', 'name': 'cord'}, {'id': 6847, 'synset': 'cordage.n.02', 'name': 'cordage'}, {'id': 6848, 'synset': 'cords.n.01', 'name': 'cords'}, {'id': 6849, 'synset': 'core.n.10', 'name': 'core'}, {'id': 6850, 'synset': 'core_bit.n.01', 'name': 'core_bit'}, {'id': 6851, 'synset': 'core_drill.n.01', 'name': 'core_drill'}, {'id': 6852, 'synset': 'corer.n.01', 'name': 'corer'}, {'id': 6853, 'synset': 'corker.n.02', 'name': 'corker'}, {'id': 6854, 'synset': 'corncrib.n.01', 'name': 'corncrib'}, {'id': 6855, 'synset': 'corner.n.11', 'name': 'corner'}, {'id': 6856, 'synset': 'corner.n.03', 'name': 'corner'}, {'id': 6857, 'synset': 'corner_post.n.01', 'name': 'corner_post'}, {'id': 6858, 'synset': 'cornice.n.03', 'name': 'cornice'}, {'id': 6859, 'synset': 'cornice.n.02', 'name': 'cornice'}, {'id': 6860, 'synset': 'correctional_institution.n.01', 'name': 'correctional_institution'}, {'id': 6861, 'synset': 'corrugated_fastener.n.01', 'name': 'corrugated_fastener'}, {'id': 6862, 'synset': 'corselet.n.01', 'name': 'corselet'}, {'id': 6863, 'synset': 'cosmetic.n.01', 'name': 'cosmetic'}, {'id': 6864, 'synset': 'cosmotron.n.01', 'name': 'cosmotron'}, {'id': 6865, 'synset': 'costume.n.01', 'name': 'costume'}, {'id': 6866, 'synset': 'costume.n.02', 'name': 'costume'}, {'id': 6867, 'synset': 'costume.n.03', 'name': 'costume'}, {'id': 6868, 'synset': 'cosy.n.01', 'name': 'cosy'}, {'id': 6869, 'synset': 'cot.n.03', 'name': 'cot'}, {'id': 6870, 'synset': 'cottage_tent.n.01', 'name': 'cottage_tent'}, {'id': 6871, 'synset': 'cotter.n.03', 'name': 'cotter'}, {'id': 6872, 'synset': 'cotter_pin.n.01', 'name': 'cotter_pin'}, {'id': 6873, 'synset': 'cotton.n.02', 'name': 'cotton'}, {'id': 6874, 'synset': 'cotton_flannel.n.01', 'name': 'cotton_flannel'}, {'id': 6875, 'synset': 'cotton_mill.n.01', 'name': 'cotton_mill'}, {'id': 6876, 'synset': 'couch.n.03', 'name': 'couch'}, {'id': 6877, 'synset': 'couch.n.02', 'name': 'couch'}, {'id': 6878, 'synset': 'couchette.n.01', 'name': 'couchette'}, {'id': 6879, 'synset': 'coude_telescope.n.01', 'name': 'coude_telescope'}, {'id': 6880, 'synset': 'counter.n.01', 'name': 'counter'}, {'id': 6881, 'synset': 'counter.n.03', 'name': 'counter'}, {'id': 6882, 'synset': 'counter.n.02', 'name': 'counter'}, {'id': 6883, 'synset': 'counterbore.n.01', 'name': 'counterbore'}, {'id': 6884, 'synset': 'counter_tube.n.01', 'name': 'counter_tube'}, {'id': 6885, 'synset': 'country_house.n.01', 'name': 'country_house'}, {'id': 6886, 'synset': 'country_store.n.01', 'name': 'country_store'}, {'id': 6887, 'synset': 'coupe.n.01', 'name': 'coupe'}, {'id': 6888, 'synset': 'coupling.n.02', 'name': 'coupling'}, {'id': 6889, 'synset': 'court.n.10', 'name': 'court'}, {'id': 6890, 'synset': 'court.n.04', 'name': 'court'}, {'id': 6891, 'synset': 'court.n.02', 'name': 'court'}, {'id': 6892, 'synset': 'court.n.09', 'name': 'court'}, {'id': 6893, 'synset': 'courtelle.n.01', 'name': 'Courtelle'}, {'id': 6894, 'synset': 'courthouse.n.02', 'name': 'courthouse'}, {'id': 6895, 'synset': 'courthouse.n.01', 'name': 'courthouse'}, {'id': 6896, 'synset': 'covered_bridge.n.01', 'name': 'covered_bridge'}, {'id': 6897, 'synset': 'covered_couch.n.01', 'name': 'covered_couch'}, {'id': 6898, 'synset': 'covered_wagon.n.01', 'name': 'covered_wagon'}, {'id': 6899, 'synset': 'covering.n.02', 'name': 'covering'}, {'id': 6900, 'synset': 'coverlet.n.01', 'name': 'coverlet'}, {'id': 6901, 'synset': 'cover_plate.n.01', 'name': 'cover_plate'}, {'id': 6902, 'synset': 'cowbarn.n.01', 'name': 'cowbarn'}, {'id': 6903, 'synset': 'cowboy_boot.n.01', 'name': 'cowboy_boot'}, {'id': 6904, 'synset': 'cowhide.n.03', 'name': 'cowhide'}, {'id': 6905, 'synset': 'cowl.n.02', 'name': 'cowl'}, {'id': 6906, 'synset': 'cow_pen.n.01', 'name': 'cow_pen'}, {'id': 6907, 'synset': 'cpu_board.n.01', 'name': 'CPU_board'}, {'id': 6908, 'synset': 'crackle.n.02', 'name': 'crackle'}, {'id': 6909, 'synset': 'cradle.n.01', 'name': 'cradle'}, {'id': 6910, 'synset': 'craft.n.02', 'name': 'craft'}, {'id': 6911, 'synset': 'cramp.n.03', 'name': 'cramp'}, {'id': 6912, 'synset': 'crampon.n.02', 'name': 'crampon'}, {'id': 6913, 'synset': 'crampon.n.01', 'name': 'crampon'}, {'id': 6914, 'synset': 'crane.n.04', 'name': 'crane'}, {'id': 6915, 'synset': 'craniometer.n.01', 'name': 'craniometer'}, {'id': 6916, 'synset': 'crank.n.04', 'name': 'crank'}, {'id': 6917, 'synset': 'crankcase.n.01', 'name': 'crankcase'}, {'id': 6918, 'synset': 'crankshaft.n.01', 'name': 'crankshaft'}, {'id': 6919, 'synset': 'crash_barrier.n.01', 'name': 'crash_barrier'}, {'id': 6920, 'synset': 'crash_helmet.n.01', 'name': 'crash_helmet'}, {'id': 6921, 'synset': 'cravat.n.01', 'name': 'cravat'}, {'id': 6922, 'synset': 'crazy_quilt.n.01', 'name': 'crazy_quilt'}, {'id': 6923, 'synset': 'cream.n.03', 'name': 'cream'}, {'id': 6924, 'synset': 'creche.n.01', 'name': 'creche'}, {'id': 6925, 'synset': 'creche.n.02', 'name': 'creche'}, {'id': 6926, 'synset': 'credenza.n.01', 'name': 'credenza'}, {'id': 6927, 'synset': 'creel.n.01', 'name': 'creel'}, {'id': 6928, 'synset': 'crematory.n.02', 'name': 'crematory'}, {'id': 6929, 'synset': 'crematory.n.01', 'name': 'crematory'}, {'id': 6930, 'synset': 'crepe.n.03', 'name': 'crepe'}, {'id': 6931, 'synset': 'crepe_de_chine.n.01', 'name': 'crepe_de_Chine'}, {'id': 6932, 'synset': 'crescent_wrench.n.01', 'name': 'crescent_wrench'}, {'id': 6933, 'synset': 'cretonne.n.01', 'name': 'cretonne'}, {'id': 6934, 'synset': 'crib.n.03', 'name': 'crib'}, {'id': 6935, 'synset': 'cricket_ball.n.01', 'name': 'cricket_ball'}, {'id': 6936, 'synset': 'cricket_bat.n.01', 'name': 'cricket_bat'}, {'id': 6937, 'synset': 'cricket_equipment.n.01', 'name': 'cricket_equipment'}, {'id': 6938, 'synset': 'cringle.n.01', 'name': 'cringle'}, {'id': 6939, 'synset': 'crinoline.n.03', 'name': 'crinoline'}, {'id': 6940, 'synset': 'crinoline.n.02', 'name': 'crinoline'}, {'id': 6941, 'synset': 'crochet_needle.n.01', 'name': 'crochet_needle'}, {'id': 6942, 'synset': 'crock_pot.n.01', 'name': 'Crock_Pot'}, {'id': 6943, 'synset': 'crook.n.03', 'name': 'crook'}, {'id': 6944, 'synset': 'crookes_radiometer.n.01', 'name': 'Crookes_radiometer'}, {'id': 6945, 'synset': 'crookes_tube.n.01', 'name': 'Crookes_tube'}, {'id': 6946, 'synset': 'croquet_ball.n.01', 'name': 'croquet_ball'}, {'id': 6947, 'synset': 'croquet_equipment.n.01', 'name': 'croquet_equipment'}, {'id': 6948, 'synset': 'croquet_mallet.n.01', 'name': 'croquet_mallet'}, {'id': 6949, 'synset': 'cross.n.01', 'name': 'cross'}, {'id': 6950, 'synset': 'crossbar.n.03', 'name': 'crossbar'}, {'id': 6951, 'synset': 'crossbar.n.02', 'name': 'crossbar'}, {'id': 6952, 'synset': 'crossbench.n.01', 'name': 'crossbench'}, {'id': 6953, 'synset': 'cross_bit.n.01', 'name': 'cross_bit'}, {'id': 6954, 'synset': 'crossbow.n.01', 'name': 'crossbow'}, {'id': 6955, 'synset': 'crosscut_saw.n.01', 'name': 'crosscut_saw'}, {'id': 6956, 'synset': 'crossjack.n.01', 'name': 'crossjack'}, {'id': 6957, 'synset': 'crosspiece.n.02', 'name': 'crosspiece'}, {'id': 6958, 'synset': 'crotchet.n.04', 'name': 'crotchet'}, {'id': 6959, 'synset': "croupier's_rake.n.01", 'name': "croupier's_rake"}, {'id': 6960, 'synset': 'crown.n.11', 'name': 'crown'}, {'id': 6961, 'synset': 'crown_jewels.n.01', 'name': 'crown_jewels'}, {'id': 6962, 'synset': 'crown_lens.n.01', 'name': 'crown_lens'}, {'id': 6963, 'synset': "crow's_nest.n.01", 'name': "crow's_nest"}, {'id': 6964, 'synset': 'crucible.n.01', 'name': 'crucible'}, {'id': 6965, 'synset': 'cruet.n.01', 'name': 'cruet'}, {'id': 6966, 'synset': 'cruet-stand.n.01', 'name': 'cruet-stand'}, {'id': 6967, 'synset': 'cruise_control.n.01', 'name': 'cruise_control'}, {'id': 6968, 'synset': 'cruise_missile.n.01', 'name': 'cruise_missile'}, {'id': 6969, 'synset': 'cruiser.n.02', 'name': 'cruiser'}, {'id': 6970, 'synset': 'crupper.n.01', 'name': 'crupper'}, {'id': 6971, 'synset': 'cruse.n.01', 'name': 'cruse'}, {'id': 6972, 'synset': 'crusher.n.01', 'name': 'crusher'}, {'id': 6973, 'synset': 'cryometer.n.01', 'name': 'cryometer'}, {'id': 6974, 'synset': 'cryoscope.n.01', 'name': 'cryoscope'}, {'id': 6975, 'synset': 'cryostat.n.01', 'name': 'cryostat'}, {'id': 6976, 'synset': 'crypt.n.01', 'name': 'crypt'}, {'id': 6977, 'synset': 'crystal.n.06', 'name': 'crystal'}, {'id': 6978, 'synset': 'crystal_detector.n.01', 'name': 'crystal_detector'}, {'id': 6979, 'synset': 'crystal_microphone.n.01', 'name': 'crystal_microphone'}, {'id': 6980, 'synset': 'crystal_oscillator.n.01', 'name': 'crystal_oscillator'}, {'id': 6981, 'synset': 'crystal_set.n.01', 'name': 'crystal_set'}, {'id': 6982, 'synset': 'cubitiere.n.01', 'name': 'cubitiere'}, {'id': 6983, 'synset': 'cucking_stool.n.01', 'name': 'cucking_stool'}, {'id': 6984, 'synset': 'cuckoo_clock.n.01', 'name': 'cuckoo_clock'}, {'id': 6985, 'synset': 'cuddy.n.01', 'name': 'cuddy'}, {'id': 6986, 'synset': 'cudgel.n.01', 'name': 'cudgel'}, {'id': 6987, 'synset': 'cue.n.04', 'name': 'cue'}, {'id': 6988, 'synset': 'cue_ball.n.01', 'name': 'cue_ball'}, {'id': 6989, 'synset': 'cuff.n.01', 'name': 'cuff'}, {'id': 6990, 'synset': 'cuirass.n.01', 'name': 'cuirass'}, {'id': 6991, 'synset': 'cuisse.n.01', 'name': 'cuisse'}, {'id': 6992, 'synset': 'cul.n.01', 'name': 'cul'}, {'id': 6993, 'synset': 'culdoscope.n.01', 'name': 'culdoscope'}, {'id': 6994, 'synset': 'cullis.n.01', 'name': 'cullis'}, {'id': 6995, 'synset': 'culotte.n.01', 'name': 'culotte'}, {'id': 6996, 'synset': 'cultivator.n.02', 'name': 'cultivator'}, {'id': 6997, 'synset': 'culverin.n.02', 'name': 'culverin'}, {'id': 6998, 'synset': 'culverin.n.01', 'name': 'culverin'}, {'id': 6999, 'synset': 'culvert.n.01', 'name': 'culvert'}, {'id': 7000, 'synset': 'cup_hook.n.01', 'name': 'cup_hook'}, {'id': 7001, 'synset': 'cupola.n.02', 'name': 'cupola'}, {'id': 7002, 'synset': 'cupola.n.01', 'name': 'cupola'}, {'id': 7003, 'synset': 'curb.n.02', 'name': 'curb'}, {'id': 7004, 'synset': 'curb_roof.n.01', 'name': 'curb_roof'}, {'id': 7005, 'synset': 'curbstone.n.01', 'name': 'curbstone'}, {'id': 7006, 'synset': 'curette.n.01', 'name': 'curette'}, {'id': 7007, 'synset': 'currycomb.n.01', 'name': 'currycomb'}, {'id': 7008, 'synset': 'cursor.n.01', 'name': 'cursor'}, {'id': 7009, 'synset': 'customhouse.n.01', 'name': 'customhouse'}, {'id': 7010, 'synset': 'cutaway.n.01', 'name': 'cutaway'}, {'id': 7011, 'synset': 'cutlas.n.01', 'name': 'cutlas'}, {'id': 7012, 'synset': 'cutoff.n.03', 'name': 'cutoff'}, {'id': 7013, 'synset': 'cutout.n.01', 'name': 'cutout'}, {'id': 7014, 'synset': 'cutter.n.06', 'name': 'cutter'}, {'id': 7015, 'synset': 'cutter.n.05', 'name': 'cutter'}, {'id': 7016, 'synset': 'cutting_implement.n.01', 'name': 'cutting_implement'}, {'id': 7017, 'synset': 'cutting_room.n.01', 'name': 'cutting_room'}, {'id': 7018, 'synset': 'cutty_stool.n.01', 'name': 'cutty_stool'}, {'id': 7019, 'synset': 'cutwork.n.01', 'name': 'cutwork'}, {'id': 7020, 'synset': 'cybercafe.n.01', 'name': 'cybercafe'}, {'id': 7021, 'synset': 'cyclopean_masonry.n.01', 'name': 'cyclopean_masonry'}, {'id': 7022, 'synset': 'cyclostyle.n.01', 'name': 'cyclostyle'}, {'id': 7023, 'synset': 'cyclotron.n.01', 'name': 'cyclotron'}, {'id': 7024, 'synset': 'cylinder.n.03', 'name': 'cylinder'}, {'id': 7025, 'synset': 'cylinder_lock.n.01', 'name': 'cylinder_lock'}, {'id': 7026, 'synset': 'dacha.n.01', 'name': 'dacha'}, {'id': 7027, 'synset': 'dacron.n.01', 'name': 'Dacron'}, {'id': 7028, 'synset': 'dado.n.02', 'name': 'dado'}, {'id': 7029, 'synset': 'dado_plane.n.01', 'name': 'dado_plane'}, {'id': 7030, 'synset': 'dairy.n.01', 'name': 'dairy'}, {'id': 7031, 'synset': 'dais.n.01', 'name': 'dais'}, {'id': 7032, 'synset': 'daisy_print_wheel.n.01', 'name': 'daisy_print_wheel'}, {'id': 7033, 'synset': 'daisywheel_printer.n.01', 'name': 'daisywheel_printer'}, {'id': 7034, 'synset': 'dam.n.01', 'name': 'dam'}, {'id': 7035, 'synset': 'damask.n.02', 'name': 'damask'}, {'id': 7036, 'synset': 'dampener.n.01', 'name': 'dampener'}, {'id': 7037, 'synset': 'damper.n.02', 'name': 'damper'}, {'id': 7038, 'synset': 'damper_block.n.01', 'name': 'damper_block'}, {'id': 7039, 'synset': 'dark_lantern.n.01', 'name': 'dark_lantern'}, {'id': 7040, 'synset': 'darkroom.n.01', 'name': 'darkroom'}, {'id': 7041, 'synset': 'darning_needle.n.01', 'name': 'darning_needle'}, {'id': 7042, 'synset': 'dart.n.02', 'name': 'dart'}, {'id': 7043, 'synset': 'dart.n.01', 'name': 'dart'}, {'id': 7044, 'synset': 'dashboard.n.02', 'name': 'dashboard'}, {'id': 7045, 'synset': 'dashiki.n.01', 'name': 'dashiki'}, {'id': 7046, 'synset': 'dash-pot.n.01', 'name': 'dash-pot'}, {'id': 7047, 'synset': 'data_converter.n.01', 'name': 'data_converter'}, {'id': 7048, 'synset': 'data_input_device.n.01', 'name': 'data_input_device'}, {'id': 7049, 'synset': 'data_multiplexer.n.01', 'name': 'data_multiplexer'}, {'id': 7050, 'synset': 'data_system.n.01', 'name': 'data_system'}, {'id': 7051, 'synset': 'davenport.n.03', 'name': 'davenport'}, {'id': 7052, 'synset': 'davenport.n.02', 'name': 'davenport'}, {'id': 7053, 'synset': 'davit.n.01', 'name': 'davit'}, {'id': 7054, 'synset': 'daybed.n.01', 'name': 'daybed'}, {'id': 7055, 'synset': 'daybook.n.02', 'name': 'daybook'}, {'id': 7056, 'synset': 'day_nursery.n.01', 'name': 'day_nursery'}, {'id': 7057, 'synset': 'day_school.n.03', 'name': 'day_school'}, {'id': 7058, 'synset': 'dead_axle.n.01', 'name': 'dead_axle'}, {'id': 7059, 'synset': 'deadeye.n.02', 'name': 'deadeye'}, {'id': 7060, 'synset': 'deadhead.n.02', 'name': 'deadhead'}, {'id': 7061, 'synset': 'deanery.n.01', 'name': 'deanery'}, {'id': 7062, 'synset': 'deathbed.n.02', 'name': 'deathbed'}, {'id': 7063, 'synset': 'death_camp.n.01', 'name': 'death_camp'}, {'id': 7064, 'synset': 'death_house.n.01', 'name': 'death_house'}, {'id': 7065, 'synset': 'death_knell.n.02', 'name': 'death_knell'}, {'id': 7066, 'synset': 'death_seat.n.01', 'name': 'death_seat'}, {'id': 7067, 'synset': 'deck.n.02', 'name': 'deck'}, {'id': 7068, 'synset': 'deck.n.04', 'name': 'deck'}, {'id': 7069, 'synset': 'deck-house.n.01', 'name': 'deck-house'}, {'id': 7070, 'synset': 'deckle.n.02', 'name': 'deckle'}, {'id': 7071, 'synset': 'deckle_edge.n.01', 'name': 'deckle_edge'}, {'id': 7072, 'synset': 'declinometer.n.01', 'name': 'declinometer'}, {'id': 7073, 'synset': 'decoder.n.02', 'name': 'decoder'}, {'id': 7074, 'synset': 'decolletage.n.01', 'name': 'decolletage'}, {'id': 7075, 'synset': 'decoupage.n.01', 'name': 'decoupage'}, {'id': 7076, 'synset': 'dedicated_file_server.n.01', 'name': 'dedicated_file_server'}, {'id': 7077, 'synset': 'deep-freeze.n.01', 'name': 'deep-freeze'}, {'id': 7078, 'synset': 'deerstalker.n.01', 'name': 'deerstalker'}, {'id': 7079, 'synset': 'defense_system.n.01', 'name': 'defense_system'}, {'id': 7080, 'synset': 'defensive_structure.n.01', 'name': 'defensive_structure'}, {'id': 7081, 'synset': 'defibrillator.n.01', 'name': 'defibrillator'}, {'id': 7082, 'synset': 'defilade.n.01', 'name': 'defilade'}, {'id': 7083, 'synset': 'deflector.n.01', 'name': 'deflector'}, {'id': 7084, 'synset': 'delayed_action.n.01', 'name': 'delayed_action'}, {'id': 7085, 'synset': 'delay_line.n.01', 'name': 'delay_line'}, {'id': 7086, 'synset': 'delft.n.01', 'name': 'delft'}, {'id': 7087, 'synset': 'delicatessen.n.02', 'name': 'delicatessen'}, {'id': 7088, 'synset': 'delivery_truck.n.01', 'name': 'delivery_truck'}, {'id': 7089, 'synset': 'delta_wing.n.01', 'name': 'delta_wing'}, {'id': 7090, 'synset': 'demijohn.n.01', 'name': 'demijohn'}, {'id': 7091, 'synset': 'demitasse.n.02', 'name': 'demitasse'}, {'id': 7092, 'synset': 'den.n.04', 'name': 'den'}, {'id': 7093, 'synset': 'denim.n.02', 'name': 'denim'}, {'id': 7094, 'synset': 'densimeter.n.01', 'name': 'densimeter'}, {'id': 7095, 'synset': 'densitometer.n.01', 'name': 'densitometer'}, {'id': 7096, 'synset': 'dental_appliance.n.01', 'name': 'dental_appliance'}, {'id': 7097, 'synset': 'dental_implant.n.01', 'name': 'dental_implant'}, {'id': 7098, 'synset': "dentist's_drill.n.01", 'name': "dentist's_drill"}, {'id': 7099, 'synset': 'denture.n.01', 'name': 'denture'}, {'id': 7100, 'synset': 'deodorant.n.01', 'name': 'deodorant'}, {'id': 7101, 'synset': 'department_store.n.01', 'name': 'department_store'}, {'id': 7102, 'synset': 'departure_lounge.n.01', 'name': 'departure_lounge'}, {'id': 7103, 'synset': 'depilatory.n.02', 'name': 'depilatory'}, {'id': 7104, 'synset': 'depressor.n.03', 'name': 'depressor'}, {'id': 7105, 'synset': 'depth_finder.n.01', 'name': 'depth_finder'}, {'id': 7106, 'synset': 'depth_gauge.n.01', 'name': 'depth_gauge'}, {'id': 7107, 'synset': 'derrick.n.02', 'name': 'derrick'}, {'id': 7108, 'synset': 'derrick.n.01', 'name': 'derrick'}, {'id': 7109, 'synset': 'derringer.n.01', 'name': 'derringer'}, {'id': 7110, 'synset': 'desk_phone.n.01', 'name': 'desk_phone'}, {'id': 7111, 'synset': 'desktop_computer.n.01', 'name': 'desktop_computer'}, {'id': 7112, 'synset': 'dessert_spoon.n.01', 'name': 'dessert_spoon'}, {'id': 7113, 'synset': 'destroyer.n.01', 'name': 'destroyer'}, {'id': 7114, 'synset': 'destroyer_escort.n.01', 'name': 'destroyer_escort'}, {'id': 7115, 'synset': 'detached_house.n.01', 'name': 'detached_house'}, {'id': 7116, 'synset': 'detector.n.01', 'name': 'detector'}, {'id': 7117, 'synset': 'detector.n.03', 'name': 'detector'}, {'id': 7118, 'synset': 'detention_home.n.01', 'name': 'detention_home'}, {'id': 7119, 'synset': 'detonating_fuse.n.01', 'name': 'detonating_fuse'}, {'id': 7120, 'synset': 'detonator.n.01', 'name': 'detonator'}, {'id': 7121, 'synset': 'developer.n.02', 'name': 'developer'}, {'id': 7122, 'synset': 'device.n.01', 'name': 'device'}, {'id': 7123, 'synset': 'dewar_flask.n.01', 'name': 'Dewar_flask'}, {'id': 7124, 'synset': 'dhoti.n.01', 'name': 'dhoti'}, {'id': 7125, 'synset': 'dhow.n.01', 'name': 'dhow'}, {'id': 7126, 'synset': 'dial.n.04', 'name': 'dial'}, {'id': 7127, 'synset': 'dial.n.03', 'name': 'dial'}, {'id': 7128, 'synset': 'dial.n.02', 'name': 'dial'}, {'id': 7129, 'synset': 'dialog_box.n.01', 'name': 'dialog_box'}, {'id': 7130, 'synset': 'dial_telephone.n.01', 'name': 'dial_telephone'}, {'id': 7131, 'synset': 'dialyzer.n.01', 'name': 'dialyzer'}, {'id': 7132, 'synset': 'diamante.n.02', 'name': 'diamante'}, {'id': 7133, 'synset': 'diaper.n.02', 'name': 'diaper'}, {'id': 7134, 'synset': 'diaphone.n.01', 'name': 'diaphone'}, {'id': 7135, 'synset': 'diaphragm.n.01', 'name': 'diaphragm'}, {'id': 7136, 'synset': 'diaphragm.n.04', 'name': 'diaphragm'}, {'id': 7137, 'synset': 'diathermy_machine.n.01', 'name': 'diathermy_machine'}, {'id': 7138, 'synset': 'dibble.n.01', 'name': 'dibble'}, {'id': 7139, 'synset': 'dice_cup.n.01', 'name': 'dice_cup'}, {'id': 7140, 'synset': 'dicer.n.01', 'name': 'dicer'}, {'id': 7141, 'synset': 'dickey.n.02', 'name': 'dickey'}, {'id': 7142, 'synset': 'dickey.n.01', 'name': 'dickey'}, {'id': 7143, 'synset': 'dictaphone.n.01', 'name': 'Dictaphone'}, {'id': 7144, 'synset': 'die.n.03', 'name': 'die'}, {'id': 7145, 'synset': 'diesel.n.02', 'name': 'diesel'}, {'id': 7146, 'synset': 'diesel-electric_locomotive.n.01', 'name': 'diesel-electric_locomotive'}, {'id': 7147, 'synset': 'diesel-hydraulic_locomotive.n.01', 'name': 'diesel-hydraulic_locomotive'}, {'id': 7148, 'synset': 'diesel_locomotive.n.01', 'name': 'diesel_locomotive'}, {'id': 7149, 'synset': 'diestock.n.01', 'name': 'diestock'}, {'id': 7150, 'synset': 'differential_analyzer.n.01', 'name': 'differential_analyzer'}, {'id': 7151, 'synset': 'differential_gear.n.01', 'name': 'differential_gear'}, {'id': 7152, 'synset': 'diffuser.n.02', 'name': 'diffuser'}, {'id': 7153, 'synset': 'diffuser.n.01', 'name': 'diffuser'}, {'id': 7154, 'synset': 'digester.n.01', 'name': 'digester'}, {'id': 7155, 'synset': 'diggings.n.02', 'name': 'diggings'}, {'id': 7156, 'synset': 'digital-analog_converter.n.01', 'name': 'digital-analog_converter'}, {'id': 7157, 'synset': 'digital_audiotape.n.01', 'name': 'digital_audiotape'}, {'id': 7158, 'synset': 'digital_camera.n.01', 'name': 'digital_camera'}, {'id': 7159, 'synset': 'digital_clock.n.01', 'name': 'digital_clock'}, {'id': 7160, 'synset': 'digital_computer.n.01', 'name': 'digital_computer'}, {'id': 7161, 'synset': 'digital_display.n.01', 'name': 'digital_display'}, {'id': 7162, 'synset': 'digital_subscriber_line.n.01', 'name': 'digital_subscriber_line'}, {'id': 7163, 'synset': 'digital_voltmeter.n.01', 'name': 'digital_voltmeter'}, {'id': 7164, 'synset': 'digital_watch.n.01', 'name': 'digital_watch'}, {'id': 7165, 'synset': 'digitizer.n.01', 'name': 'digitizer'}, {'id': 7166, 'synset': 'dilator.n.03', 'name': 'dilator'}, {'id': 7167, 'synset': 'dildo.n.01', 'name': 'dildo'}, {'id': 7168, 'synset': 'dimity.n.01', 'name': 'dimity'}, {'id': 7169, 'synset': 'dimmer.n.01', 'name': 'dimmer'}, {'id': 7170, 'synset': 'diner.n.03', 'name': 'diner'}, {'id': 7171, 'synset': 'dinette.n.01', 'name': 'dinette'}, {'id': 7172, 'synset': 'dining_area.n.01', 'name': 'dining_area'}, {'id': 7173, 'synset': 'dining_car.n.01', 'name': 'dining_car'}, {'id': 7174, 'synset': 'dining-hall.n.01', 'name': 'dining-hall'}, {'id': 7175, 'synset': 'dining_room.n.01', 'name': 'dining_room'}, {'id': 7176, 'synset': 'dining-room_furniture.n.01', 'name': 'dining-room_furniture'}, {'id': 7177, 'synset': 'dining-room_table.n.01', 'name': 'dining-room_table'}, {'id': 7178, 'synset': 'dinner_bell.n.01', 'name': 'dinner_bell'}, {'id': 7179, 'synset': 'dinner_dress.n.01', 'name': 'dinner_dress'}, {'id': 7180, 'synset': 'dinner_napkin.n.01', 'name': 'dinner_napkin'}, {'id': 7181, 'synset': 'dinner_pail.n.01', 'name': 'dinner_pail'}, {'id': 7182, 'synset': 'dinner_table.n.01', 'name': 'dinner_table'}, {'id': 7183, 'synset': 'dinner_theater.n.01', 'name': 'dinner_theater'}, {'id': 7184, 'synset': 'diode.n.02', 'name': 'diode'}, {'id': 7185, 'synset': 'diode.n.01', 'name': 'diode'}, {'id': 7186, 'synset': 'dip.n.07', 'name': 'dip'}, {'id': 7187, 'synset': 'diplomatic_building.n.01', 'name': 'diplomatic_building'}, {'id': 7188, 'synset': 'dipole.n.02', 'name': 'dipole'}, {'id': 7189, 'synset': 'dipper.n.01', 'name': 'dipper'}, {'id': 7190, 'synset': 'dipstick.n.01', 'name': 'dipstick'}, {'id': 7191, 'synset': 'dip_switch.n.01', 'name': 'DIP_switch'}, {'id': 7192, 'synset': 'directional_antenna.n.01', 'name': 'directional_antenna'}, {'id': 7193, 'synset': 'directional_microphone.n.01', 'name': 'directional_microphone'}, {'id': 7194, 'synset': 'direction_finder.n.01', 'name': 'direction_finder'}, {'id': 7195, 'synset': 'dirk.n.01', 'name': 'dirk'}, {'id': 7196, 'synset': 'dirndl.n.02', 'name': 'dirndl'}, {'id': 7197, 'synset': 'dirndl.n.01', 'name': 'dirndl'}, {'id': 7198, 'synset': 'dirty_bomb.n.01', 'name': 'dirty_bomb'}, {'id': 7199, 'synset': 'discharge_lamp.n.01', 'name': 'discharge_lamp'}, {'id': 7200, 'synset': 'discharge_pipe.n.01', 'name': 'discharge_pipe'}, {'id': 7201, 'synset': 'disco.n.02', 'name': 'disco'}, {'id': 7202, 'synset': 'discount_house.n.01', 'name': 'discount_house'}, {'id': 7203, 'synset': 'discus.n.02', 'name': 'discus'}, {'id': 7204, 'synset': 'disguise.n.02', 'name': 'disguise'}, {'id': 7205, 'synset': 'dishpan.n.01', 'name': 'dishpan'}, {'id': 7206, 'synset': 'dish_rack.n.01', 'name': 'dish_rack'}, {'id': 7207, 'synset': 'disk.n.02', 'name': 'disk'}, {'id': 7208, 'synset': 'disk_brake.n.01', 'name': 'disk_brake'}, {'id': 7209, 'synset': 'disk_clutch.n.01', 'name': 'disk_clutch'}, {'id': 7210, 'synset': 'disk_controller.n.01', 'name': 'disk_controller'}, {'id': 7211, 'synset': 'disk_drive.n.01', 'name': 'disk_drive'}, {'id': 7212, 'synset': 'diskette.n.01', 'name': 'diskette'}, {'id': 7213, 'synset': 'disk_harrow.n.01', 'name': 'disk_harrow'}, {'id': 7214, 'synset': 'dispatch_case.n.01', 'name': 'dispatch_case'}, {'id': 7215, 'synset': 'dispensary.n.01', 'name': 'dispensary'}, {'id': 7216, 'synset': 'display.n.06', 'name': 'display'}, {'id': 7217, 'synset': 'display_adapter.n.01', 'name': 'display_adapter'}, {'id': 7218, 'synset': 'display_panel.n.01', 'name': 'display_panel'}, {'id': 7219, 'synset': 'display_window.n.01', 'name': 'display_window'}, {'id': 7220, 'synset': 'disposal.n.04', 'name': 'disposal'}, {'id': 7221, 'synset': 'disrupting_explosive.n.01', 'name': 'disrupting_explosive'}, {'id': 7222, 'synset': 'distaff.n.02', 'name': 'distaff'}, {'id': 7223, 'synset': 'distillery.n.01', 'name': 'distillery'}, {'id': 7224, 'synset': 'distributor.n.04', 'name': 'distributor'}, {'id': 7225, 'synset': 'distributor_cam.n.01', 'name': 'distributor_cam'}, {'id': 7226, 'synset': 'distributor_cap.n.01', 'name': 'distributor_cap'}, {'id': 7227, 'synset': 'distributor_housing.n.01', 'name': 'distributor_housing'}, {'id': 7228, 'synset': 'distributor_point.n.01', 'name': 'distributor_point'}, {'id': 7229, 'synset': 'ditch.n.01', 'name': 'ditch'}, {'id': 7230, 'synset': 'ditch_spade.n.01', 'name': 'ditch_spade'}, {'id': 7231, 'synset': 'ditty_bag.n.01', 'name': 'ditty_bag'}, {'id': 7232, 'synset': 'divan.n.01', 'name': 'divan'}, {'id': 7233, 'synset': 'divan.n.04', 'name': 'divan'}, {'id': 7234, 'synset': 'dive_bomber.n.01', 'name': 'dive_bomber'}, {'id': 7235, 'synset': 'diverging_lens.n.01', 'name': 'diverging_lens'}, {'id': 7236, 'synset': 'divided_highway.n.01', 'name': 'divided_highway'}, {'id': 7237, 'synset': 'divider.n.04', 'name': 'divider'}, {'id': 7238, 'synset': 'diving_bell.n.01', 'name': 'diving_bell'}, {'id': 7239, 'synset': 'divining_rod.n.01', 'name': 'divining_rod'}, {'id': 7240, 'synset': 'diving_suit.n.01', 'name': 'diving_suit'}, {'id': 7241, 'synset': 'dixie.n.02', 'name': 'dixie'}, {'id': 7242, 'synset': 'dock.n.05', 'name': 'dock'}, {'id': 7243, 'synset': 'doeskin.n.02', 'name': 'doeskin'}, {'id': 7244, 'synset': 'dogcart.n.01', 'name': 'dogcart'}, {'id': 7245, 'synset': 'doggie_bag.n.01', 'name': 'doggie_bag'}, {'id': 7246, 'synset': 'dogsled.n.01', 'name': 'dogsled'}, {'id': 7247, 'synset': 'dog_wrench.n.01', 'name': 'dog_wrench'}, {'id': 7248, 'synset': 'doily.n.01', 'name': 'doily'}, {'id': 7249, 'synset': 'dolly.n.02', 'name': 'dolly'}, {'id': 7250, 'synset': 'dolman.n.02', 'name': 'dolman'}, {'id': 7251, 'synset': 'dolman.n.01', 'name': 'dolman'}, {'id': 7252, 'synset': 'dolman_sleeve.n.01', 'name': 'dolman_sleeve'}, {'id': 7253, 'synset': 'dolmen.n.01', 'name': 'dolmen'}, {'id': 7254, 'synset': 'dome.n.04', 'name': 'dome'}, {'id': 7255, 'synset': 'dome.n.03', 'name': 'dome'}, {'id': 7256, 'synset': 'domino.n.03', 'name': 'domino'}, {'id': 7257, 'synset': 'dongle.n.01', 'name': 'dongle'}, {'id': 7258, 'synset': 'donkey_jacket.n.01', 'name': 'donkey_jacket'}, {'id': 7259, 'synset': 'door.n.01', 'name': 'door'}, {'id': 7260, 'synset': 'door.n.05', 'name': 'door'}, {'id': 7261, 'synset': 'door.n.04', 'name': 'door'}, {'id': 7262, 'synset': 'doorbell.n.01', 'name': 'doorbell'}, {'id': 7263, 'synset': 'doorframe.n.01', 'name': 'doorframe'}, {'id': 7264, 'synset': 'doorjamb.n.01', 'name': 'doorjamb'}, {'id': 7265, 'synset': 'doorlock.n.01', 'name': 'doorlock'}, {'id': 7266, 'synset': 'doornail.n.01', 'name': 'doornail'}, {'id': 7267, 'synset': 'doorplate.n.01', 'name': 'doorplate'}, {'id': 7268, 'synset': 'doorsill.n.01', 'name': 'doorsill'}, {'id': 7269, 'synset': 'doorstop.n.01', 'name': 'doorstop'}, {'id': 7270, 'synset': 'doppler_radar.n.01', 'name': 'Doppler_radar'}, {'id': 7271, 'synset': 'dormer.n.01', 'name': 'dormer'}, {'id': 7272, 'synset': 'dormer_window.n.01', 'name': 'dormer_window'}, {'id': 7273, 'synset': 'dormitory.n.01', 'name': 'dormitory'}, {'id': 7274, 'synset': 'dormitory.n.02', 'name': 'dormitory'}, {'id': 7275, 'synset': 'dosemeter.n.01', 'name': 'dosemeter'}, {'id': 7276, 'synset': 'dossal.n.01', 'name': 'dossal'}, {'id': 7277, 'synset': 'dot_matrix_printer.n.01', 'name': 'dot_matrix_printer'}, {'id': 7278, 'synset': 'double_bed.n.01', 'name': 'double_bed'}, {'id': 7279, 'synset': 'double-bitted_ax.n.01', 'name': 'double-bitted_ax'}, {'id': 7280, 'synset': 'double_boiler.n.01', 'name': 'double_boiler'}, {'id': 7281, 'synset': 'double-breasted_jacket.n.01', 'name': 'double-breasted_jacket'}, {'id': 7282, 'synset': 'double-breasted_suit.n.01', 'name': 'double-breasted_suit'}, {'id': 7283, 'synset': 'double_door.n.01', 'name': 'double_door'}, {'id': 7284, 'synset': 'double_glazing.n.01', 'name': 'double_glazing'}, {'id': 7285, 'synset': 'double-hung_window.n.01', 'name': 'double-hung_window'}, {'id': 7286, 'synset': 'double_knit.n.01', 'name': 'double_knit'}, {'id': 7287, 'synset': 'doubler.n.01', 'name': 'doubler'}, {'id': 7288, 'synset': 'double_reed.n.02', 'name': 'double_reed'}, {'id': 7289, 'synset': 'double-reed_instrument.n.01', 'name': 'double-reed_instrument'}, {'id': 7290, 'synset': 'doublet.n.01', 'name': 'doublet'}, {'id': 7291, 'synset': 'doubletree.n.01', 'name': 'doubletree'}, {'id': 7292, 'synset': 'douche.n.01', 'name': 'douche'}, {'id': 7293, 'synset': 'dovecote.n.01', 'name': 'dovecote'}, {'id': 7294, 'synset': "dover's_powder.n.01", 'name': "Dover's_powder"}, {'id': 7295, 'synset': 'dovetail.n.01', 'name': 'dovetail'}, {'id': 7296, 'synset': 'dovetail_plane.n.01', 'name': 'dovetail_plane'}, {'id': 7297, 'synset': 'dowel.n.01', 'name': 'dowel'}, {'id': 7298, 'synset': 'downstage.n.01', 'name': 'downstage'}, {'id': 7299, 'synset': 'drafting_instrument.n.01', 'name': 'drafting_instrument'}, {'id': 7300, 'synset': 'drafting_table.n.01', 'name': 'drafting_table'}, {'id': 7301, 'synset': 'dragunov.n.01', 'name': 'Dragunov'}, {'id': 7302, 'synset': 'drainage_ditch.n.01', 'name': 'drainage_ditch'}, {'id': 7303, 'synset': 'drainage_system.n.01', 'name': 'drainage_system'}, {'id': 7304, 'synset': 'drain_basket.n.01', 'name': 'drain_basket'}, {'id': 7305, 'synset': 'drainplug.n.01', 'name': 'drainplug'}, {'id': 7306, 'synset': 'drape.n.03', 'name': 'drape'}, {'id': 7307, 'synset': 'drapery.n.02', 'name': 'drapery'}, {'id': 7308, 'synset': 'drawbar.n.01', 'name': 'drawbar'}, {'id': 7309, 'synset': 'drawbridge.n.01', 'name': 'drawbridge'}, {'id': 7310, 'synset': 'drawing_chalk.n.01', 'name': 'drawing_chalk'}, {'id': 7311, 'synset': 'drawing_room.n.01', 'name': 'drawing_room'}, {'id': 7312, 'synset': 'drawing_room.n.02', 'name': 'drawing_room'}, {'id': 7313, 'synset': 'drawknife.n.01', 'name': 'drawknife'}, {'id': 7314, 'synset': 'drawstring_bag.n.01', 'name': 'drawstring_bag'}, {'id': 7315, 'synset': 'dray.n.01', 'name': 'dray'}, {'id': 7316, 'synset': 'dreadnought.n.01', 'name': 'dreadnought'}, {'id': 7317, 'synset': 'dredge.n.01', 'name': 'dredge'}, {'id': 7318, 'synset': 'dredger.n.01', 'name': 'dredger'}, {'id': 7319, 'synset': 'dredging_bucket.n.01', 'name': 'dredging_bucket'}, {'id': 7320, 'synset': 'dress_blues.n.01', 'name': 'dress_blues'}, {'id': 7321, 'synset': 'dressing.n.04', 'name': 'dressing'}, {'id': 7322, 'synset': 'dressing_case.n.01', 'name': 'dressing_case'}, {'id': 7323, 'synset': 'dressing_gown.n.01', 'name': 'dressing_gown'}, {'id': 7324, 'synset': 'dressing_room.n.01', 'name': 'dressing_room'}, {'id': 7325, 'synset': 'dressing_sack.n.01', 'name': 'dressing_sack'}, {'id': 7326, 'synset': 'dressing_table.n.01', 'name': 'dressing_table'}, {'id': 7327, 'synset': 'dress_rack.n.01', 'name': 'dress_rack'}, {'id': 7328, 'synset': 'dress_shirt.n.01', 'name': 'dress_shirt'}, {'id': 7329, 'synset': 'dress_uniform.n.01', 'name': 'dress_uniform'}, {'id': 7330, 'synset': 'drift_net.n.01', 'name': 'drift_net'}, {'id': 7331, 'synset': 'electric_drill.n.01', 'name': 'electric_drill'}, {'id': 7332, 'synset': 'drilling_platform.n.01', 'name': 'drilling_platform'}, {'id': 7333, 'synset': 'drill_press.n.01', 'name': 'drill_press'}, {'id': 7334, 'synset': 'drill_rig.n.01', 'name': 'drill_rig'}, {'id': 7335, 'synset': 'drinking_fountain.n.01', 'name': 'drinking_fountain'}, {'id': 7336, 'synset': 'drinking_vessel.n.01', 'name': 'drinking_vessel'}, {'id': 7337, 'synset': 'drip_loop.n.01', 'name': 'drip_loop'}, {'id': 7338, 'synset': 'drip_mat.n.01', 'name': 'drip_mat'}, {'id': 7339, 'synset': 'drip_pan.n.02', 'name': 'drip_pan'}, {'id': 7340, 'synset': 'dripping_pan.n.01', 'name': 'dripping_pan'}, {'id': 7341, 'synset': 'drip_pot.n.01', 'name': 'drip_pot'}, {'id': 7342, 'synset': 'drive.n.02', 'name': 'drive'}, {'id': 7343, 'synset': 'drive.n.10', 'name': 'drive'}, {'id': 7344, 'synset': 'drive_line.n.01', 'name': 'drive_line'}, {'id': 7345, 'synset': 'driver.n.05', 'name': 'driver'}, {'id': 7346, 'synset': 'driveshaft.n.01', 'name': 'driveshaft'}, {'id': 7347, 'synset': 'driveway.n.01', 'name': 'driveway'}, {'id': 7348, 'synset': 'driving_iron.n.01', 'name': 'driving_iron'}, {'id': 7349, 'synset': 'driving_wheel.n.01', 'name': 'driving_wheel'}, {'id': 7350, 'synset': 'drogue.n.04', 'name': 'drogue'}, {'id': 7351, 'synset': 'drogue_parachute.n.01', 'name': 'drogue_parachute'}, {'id': 7352, 'synset': 'drone.n.05', 'name': 'drone'}, {'id': 7353, 'synset': 'drop_arch.n.01', 'name': 'drop_arch'}, {'id': 7354, 'synset': 'drop_cloth.n.02', 'name': 'drop_cloth'}, {'id': 7355, 'synset': 'drop_curtain.n.01', 'name': 'drop_curtain'}, {'id': 7356, 'synset': 'drop_forge.n.01', 'name': 'drop_forge'}, {'id': 7357, 'synset': 'drop-leaf_table.n.01', 'name': 'drop-leaf_table'}, {'id': 7358, 'synset': 'droshky.n.01', 'name': 'droshky'}, {'id': 7359, 'synset': 'drove.n.03', 'name': 'drove'}, {'id': 7360, 'synset': 'drugget.n.01', 'name': 'drugget'}, {'id': 7361, 'synset': 'drugstore.n.01', 'name': 'drugstore'}, {'id': 7362, 'synset': 'drum.n.04', 'name': 'drum'}, {'id': 7363, 'synset': 'drum_brake.n.01', 'name': 'drum_brake'}, {'id': 7364, 'synset': 'drumhead.n.01', 'name': 'drumhead'}, {'id': 7365, 'synset': 'drum_printer.n.01', 'name': 'drum_printer'}, {'id': 7366, 'synset': 'drum_sander.n.01', 'name': 'drum_sander'}, {'id': 7367, 'synset': 'dry_battery.n.01', 'name': 'dry_battery'}, {'id': 7368, 'synset': 'dry-bulb_thermometer.n.01', 'name': 'dry-bulb_thermometer'}, {'id': 7369, 'synset': 'dry_cell.n.01', 'name': 'dry_cell'}, {'id': 7370, 'synset': 'dry_dock.n.01', 'name': 'dry_dock'}, {'id': 7371, 'synset': 'dryer.n.01', 'name': 'dryer'}, {'id': 7372, 'synset': 'dry_fly.n.01', 'name': 'dry_fly'}, {'id': 7373, 'synset': 'dry_kiln.n.01', 'name': 'dry_kiln'}, {'id': 7374, 'synset': 'dry_masonry.n.01', 'name': 'dry_masonry'}, {'id': 7375, 'synset': 'dry_point.n.02', 'name': 'dry_point'}, {'id': 7376, 'synset': 'dry_wall.n.02', 'name': 'dry_wall'}, {'id': 7377, 'synset': 'dual_scan_display.n.01', 'name': 'dual_scan_display'}, {'id': 7378, 'synset': 'duck.n.04', 'name': 'duck'}, {'id': 7379, 'synset': 'duckboard.n.01', 'name': 'duckboard'}, {'id': 7380, 'synset': 'duckpin.n.01', 'name': 'duckpin'}, {'id': 7381, 'synset': 'dudeen.n.01', 'name': 'dudeen'}, {'id': 7382, 'synset': 'duffel.n.02', 'name': 'duffel'}, {'id': 7383, 'synset': 'duffel_coat.n.01', 'name': 'duffel_coat'}, {'id': 7384, 'synset': 'dugout.n.01', 'name': 'dugout'}, {'id': 7385, 'synset': 'dugout_canoe.n.01', 'name': 'dugout_canoe'}, {'id': 7386, 'synset': 'dulciana.n.01', 'name': 'dulciana'}, {'id': 7387, 'synset': 'dulcimer.n.02', 'name': 'dulcimer'}, {'id': 7388, 'synset': 'dulcimer.n.01', 'name': 'dulcimer'}, {'id': 7389, 'synset': 'dumb_bomb.n.01', 'name': 'dumb_bomb'}, {'id': 7390, 'synset': 'dumbwaiter.n.01', 'name': 'dumbwaiter'}, {'id': 7391, 'synset': 'dumdum.n.01', 'name': 'dumdum'}, {'id': 7392, 'synset': 'dumpcart.n.01', 'name': 'dumpcart'}, {'id': 7393, 'synset': 'dump_truck.n.01', 'name': 'dump_truck'}, {'id': 7394, 'synset': 'dumpy_level.n.01', 'name': 'Dumpy_level'}, {'id': 7395, 'synset': 'dunce_cap.n.01', 'name': 'dunce_cap'}, {'id': 7396, 'synset': 'dune_buggy.n.01', 'name': 'dune_buggy'}, {'id': 7397, 'synset': 'dungeon.n.02', 'name': 'dungeon'}, {'id': 7398, 'synset': 'duplex_apartment.n.01', 'name': 'duplex_apartment'}, {'id': 7399, 'synset': 'duplex_house.n.01', 'name': 'duplex_house'}, {'id': 7400, 'synset': 'duplicator.n.01', 'name': 'duplicator'}, {'id': 7401, 'synset': 'dust_bag.n.01', 'name': 'dust_bag'}, {'id': 7402, 'synset': 'dustcloth.n.01', 'name': 'dustcloth'}, {'id': 7403, 'synset': 'dust_cover.n.03', 'name': 'dust_cover'}, {'id': 7404, 'synset': 'dust_cover.n.02', 'name': 'dust_cover'}, {'id': 7405, 'synset': 'dustmop.n.01', 'name': 'dustmop'}, {'id': 7406, 'synset': 'dutch_oven.n.01', 'name': 'Dutch_oven'}, {'id': 7407, 'synset': 'dutch_oven.n.02', 'name': 'Dutch_oven'}, {'id': 7408, 'synset': 'dwelling.n.01', 'name': 'dwelling'}, {'id': 7409, 'synset': 'dye-works.n.01', 'name': 'dye-works'}, {'id': 7410, 'synset': 'dynamo.n.01', 'name': 'dynamo'}, {'id': 7411, 'synset': 'dynamometer.n.01', 'name': 'dynamometer'}, {'id': 7412, 'synset': 'eames_chair.n.01', 'name': 'Eames_chair'}, {'id': 7413, 'synset': 'earflap.n.01', 'name': 'earflap'}, {'id': 7414, 'synset': 'early_warning_radar.n.01', 'name': 'early_warning_radar'}, {'id': 7415, 'synset': 'early_warning_system.n.01', 'name': 'early_warning_system'}, {'id': 7416, 'synset': 'earmuff.n.01', 'name': 'earmuff'}, {'id': 7417, 'synset': 'earplug.n.02', 'name': 'earplug'}, {'id': 7418, 'synset': 'earthenware.n.01', 'name': 'earthenware'}, {'id': 7419, 'synset': 'earthwork.n.01', 'name': 'earthwork'}, {'id': 7420, 'synset': 'easy_chair.n.01', 'name': 'easy_chair'}, {'id': 7421, 'synset': 'eaves.n.01', 'name': 'eaves'}, {'id': 7422, 'synset': 'ecclesiastical_attire.n.01', 'name': 'ecclesiastical_attire'}, {'id': 7423, 'synset': 'echinus.n.01', 'name': 'echinus'}, {'id': 7424, 'synset': 'echocardiograph.n.01', 'name': 'echocardiograph'}, {'id': 7425, 'synset': 'edger.n.02', 'name': 'edger'}, {'id': 7426, 'synset': 'edge_tool.n.01', 'name': 'edge_tool'}, {'id': 7427, 'synset': 'efficiency_apartment.n.01', 'name': 'efficiency_apartment'}, {'id': 7428, 'synset': 'egg-and-dart.n.01', 'name': 'egg-and-dart'}, {'id': 7429, 'synset': 'egg_timer.n.01', 'name': 'egg_timer'}, {'id': 7430, 'synset': 'eiderdown.n.01', 'name': 'eiderdown'}, {'id': 7431, 'synset': 'eight_ball.n.01', 'name': 'eight_ball'}, {'id': 7432, 'synset': 'ejection_seat.n.01', 'name': 'ejection_seat'}, {'id': 7433, 'synset': 'elastic.n.02', 'name': 'elastic'}, {'id': 7434, 'synset': 'elastic_bandage.n.01', 'name': 'elastic_bandage'}, {'id': 7435, 'synset': 'elastoplast.n.01', 'name': 'Elastoplast'}, {'id': 7436, 'synset': 'elbow.n.04', 'name': 'elbow'}, {'id': 7437, 'synset': 'elbow_pad.n.01', 'name': 'elbow_pad'}, {'id': 7438, 'synset': 'electric.n.01', 'name': 'electric'}, {'id': 7439, 'synset': 'electrical_cable.n.01', 'name': 'electrical_cable'}, {'id': 7440, 'synset': 'electrical_contact.n.01', 'name': 'electrical_contact'}, {'id': 7441, 'synset': 'electrical_converter.n.01', 'name': 'electrical_converter'}, {'id': 7442, 'synset': 'electrical_device.n.01', 'name': 'electrical_device'}, {'id': 7443, 'synset': 'electrical_system.n.02', 'name': 'electrical_system'}, {'id': 7444, 'synset': 'electric_bell.n.01', 'name': 'electric_bell'}, {'id': 7445, 'synset': 'electric_blanket.n.01', 'name': 'electric_blanket'}, {'id': 7446, 'synset': 'electric_clock.n.01', 'name': 'electric_clock'}, {'id': 7447, 'synset': 'electric-discharge_lamp.n.01', 'name': 'electric-discharge_lamp'}, {'id': 7448, 'synset': 'electric_fan.n.01', 'name': 'electric_fan'}, {'id': 7449, 'synset': 'electric_frying_pan.n.01', 'name': 'electric_frying_pan'}, {'id': 7450, 'synset': 'electric_furnace.n.01', 'name': 'electric_furnace'}, {'id': 7451, 'synset': 'electric_guitar.n.01', 'name': 'electric_guitar'}, {'id': 7452, 'synset': 'electric_hammer.n.01', 'name': 'electric_hammer'}, {'id': 7453, 'synset': 'electric_heater.n.01', 'name': 'electric_heater'}, {'id': 7454, 'synset': 'electric_lamp.n.01', 'name': 'electric_lamp'}, {'id': 7455, 'synset': 'electric_locomotive.n.01', 'name': 'electric_locomotive'}, {'id': 7456, 'synset': 'electric_meter.n.01', 'name': 'electric_meter'}, {'id': 7457, 'synset': 'electric_mixer.n.01', 'name': 'electric_mixer'}, {'id': 7458, 'synset': 'electric_motor.n.01', 'name': 'electric_motor'}, {'id': 7459, 'synset': 'electric_organ.n.01', 'name': 'electric_organ'}, {'id': 7460, 'synset': 'electric_range.n.01', 'name': 'electric_range'}, {'id': 7461, 'synset': 'electric_toothbrush.n.01', 'name': 'electric_toothbrush'}, {'id': 7462, 'synset': 'electric_typewriter.n.01', 'name': 'electric_typewriter'}, {'id': 7463, 'synset': 'electro-acoustic_transducer.n.01', 'name': 'electro-acoustic_transducer'}, {'id': 7464, 'synset': 'electrode.n.01', 'name': 'electrode'}, {'id': 7465, 'synset': 'electrodynamometer.n.01', 'name': 'electrodynamometer'}, {'id': 7466, 'synset': 'electroencephalograph.n.01', 'name': 'electroencephalograph'}, {'id': 7467, 'synset': 'electrograph.n.01', 'name': 'electrograph'}, {'id': 7468, 'synset': 'electrolytic.n.01', 'name': 'electrolytic'}, {'id': 7469, 'synset': 'electrolytic_cell.n.01', 'name': 'electrolytic_cell'}, {'id': 7470, 'synset': 'electromagnet.n.01', 'name': 'electromagnet'}, {'id': 7471, 'synset': 'electrometer.n.01', 'name': 'electrometer'}, {'id': 7472, 'synset': 'electromyograph.n.01', 'name': 'electromyograph'}, {'id': 7473, 'synset': 'electron_accelerator.n.01', 'name': 'electron_accelerator'}, {'id': 7474, 'synset': 'electron_gun.n.01', 'name': 'electron_gun'}, {'id': 7475, 'synset': 'electronic_balance.n.01', 'name': 'electronic_balance'}, {'id': 7476, 'synset': 'electronic_converter.n.01', 'name': 'electronic_converter'}, {'id': 7477, 'synset': 'electronic_device.n.01', 'name': 'electronic_device'}, {'id': 7478, 'synset': 'electronic_equipment.n.01', 'name': 'electronic_equipment'}, {'id': 7479, 'synset': 'electronic_fetal_monitor.n.01', 'name': 'electronic_fetal_monitor'}, {'id': 7480, 'synset': 'electronic_instrument.n.01', 'name': 'electronic_instrument'}, {'id': 7481, 'synset': 'electronic_voltmeter.n.01', 'name': 'electronic_voltmeter'}, {'id': 7482, 'synset': 'electron_microscope.n.01', 'name': 'electron_microscope'}, {'id': 7483, 'synset': 'electron_multiplier.n.01', 'name': 'electron_multiplier'}, {'id': 7484, 'synset': 'electrophorus.n.01', 'name': 'electrophorus'}, {'id': 7485, 'synset': 'electroscope.n.01', 'name': 'electroscope'}, {'id': 7486, 'synset': 'electrostatic_generator.n.01', 'name': 'electrostatic_generator'}, {'id': 7487, 'synset': 'electrostatic_printer.n.01', 'name': 'electrostatic_printer'}, {'id': 7488, 'synset': 'elevator.n.01', 'name': 'elevator'}, {'id': 7489, 'synset': 'elevator.n.02', 'name': 'elevator'}, {'id': 7490, 'synset': 'elevator_shaft.n.01', 'name': 'elevator_shaft'}, {'id': 7491, 'synset': 'embankment.n.01', 'name': 'embankment'}, {'id': 7492, 'synset': 'embassy.n.01', 'name': 'embassy'}, {'id': 7493, 'synset': 'embellishment.n.02', 'name': 'embellishment'}, {'id': 7494, 'synset': 'emergency_room.n.01', 'name': 'emergency_room'}, {'id': 7495, 'synset': 'emesis_basin.n.01', 'name': 'emesis_basin'}, {'id': 7496, 'synset': 'emitter.n.01', 'name': 'emitter'}, {'id': 7497, 'synset': 'empty.n.01', 'name': 'empty'}, {'id': 7498, 'synset': 'emulsion.n.02', 'name': 'emulsion'}, {'id': 7499, 'synset': 'enamel.n.04', 'name': 'enamel'}, {'id': 7500, 'synset': 'enamel.n.03', 'name': 'enamel'}, {'id': 7501, 'synset': 'enamelware.n.01', 'name': 'enamelware'}, {'id': 7502, 'synset': 'encaustic.n.01', 'name': 'encaustic'}, {'id': 7503, 'synset': 'encephalogram.n.02', 'name': 'encephalogram'}, {'id': 7504, 'synset': 'enclosure.n.01', 'name': 'enclosure'}, {'id': 7505, 'synset': 'endoscope.n.01', 'name': 'endoscope'}, {'id': 7506, 'synset': 'energizer.n.02', 'name': 'energizer'}, {'id': 7507, 'synset': 'engine.n.01', 'name': 'engine'}, {'id': 7508, 'synset': 'engine.n.04', 'name': 'engine'}, {'id': 7509, 'synset': 'engineering.n.03', 'name': 'engineering'}, {'id': 7510, 'synset': 'enginery.n.01', 'name': 'enginery'}, {'id': 7511, 'synset': 'english_horn.n.01', 'name': 'English_horn'}, {'id': 7512, 'synset': 'english_saddle.n.01', 'name': 'English_saddle'}, {'id': 7513, 'synset': 'enlarger.n.01', 'name': 'enlarger'}, {'id': 7514, 'synset': 'ensemble.n.05', 'name': 'ensemble'}, {'id': 7515, 'synset': 'ensign.n.03', 'name': 'ensign'}, {'id': 7516, 'synset': 'entablature.n.01', 'name': 'entablature'}, {'id': 7517, 'synset': 'entertainment_center.n.01', 'name': 'entertainment_center'}, {'id': 7518, 'synset': 'entrenching_tool.n.01', 'name': 'entrenching_tool'}, {'id': 7519, 'synset': 'entrenchment.n.01', 'name': 'entrenchment'}, {'id': 7520, 'synset': 'envelope.n.02', 'name': 'envelope'}, {'id': 7521, 'synset': 'envelope.n.06', 'name': 'envelope'}, {'id': 7522, 'synset': 'eolith.n.01', 'name': 'eolith'}, {'id': 7523, 'synset': 'epauliere.n.01', 'name': 'epauliere'}, {'id': 7524, 'synset': 'epee.n.01', 'name': 'epee'}, {'id': 7525, 'synset': 'epergne.n.01', 'name': 'epergne'}, {'id': 7526, 'synset': 'epicyclic_train.n.01', 'name': 'epicyclic_train'}, {'id': 7527, 'synset': 'epidiascope.n.01', 'name': 'epidiascope'}, {'id': 7528, 'synset': 'epilating_wax.n.01', 'name': 'epilating_wax'}, {'id': 7529, 'synset': 'equalizer.n.01', 'name': 'equalizer'}, {'id': 7530, 'synset': 'equatorial.n.01', 'name': 'equatorial'}, {'id': 7531, 'synset': 'equipment.n.01', 'name': 'equipment'}, {'id': 7532, 'synset': 'erasable_programmable_read-only_memory.n.01', 'name': 'erasable_programmable_read-only_memory'}, {'id': 7533, 'synset': 'erecting_prism.n.01', 'name': 'erecting_prism'}, {'id': 7534, 'synset': 'erection.n.02', 'name': 'erection'}, {'id': 7535, 'synset': 'erlenmeyer_flask.n.01', 'name': 'Erlenmeyer_flask'}, {'id': 7536, 'synset': 'escape_hatch.n.01', 'name': 'escape_hatch'}, {'id': 7537, 'synset': 'escapement.n.01', 'name': 'escapement'}, {'id': 7538, 'synset': 'escape_wheel.n.01', 'name': 'escape_wheel'}, {'id': 7539, 'synset': 'escarpment.n.02', 'name': 'escarpment'}, {'id': 7540, 'synset': 'escutcheon.n.03', 'name': 'escutcheon'}, {'id': 7541, 'synset': 'esophagoscope.n.01', 'name': 'esophagoscope'}, {'id': 7542, 'synset': 'espadrille.n.01', 'name': 'espadrille'}, {'id': 7543, 'synset': 'espalier.n.01', 'name': 'espalier'}, {'id': 7544, 'synset': 'espresso_maker.n.01', 'name': 'espresso_maker'}, {'id': 7545, 'synset': 'espresso_shop.n.01', 'name': 'espresso_shop'}, {'id': 7546, 'synset': 'establishment.n.04', 'name': 'establishment'}, {'id': 7547, 'synset': 'estaminet.n.01', 'name': 'estaminet'}, {'id': 7548, 'synset': 'estradiol_patch.n.01', 'name': 'estradiol_patch'}, {'id': 7549, 'synset': 'etagere.n.01', 'name': 'etagere'}, {'id': 7550, 'synset': 'etamine.n.01', 'name': 'etamine'}, {'id': 7551, 'synset': 'etching.n.02', 'name': 'etching'}, {'id': 7552, 'synset': 'ethernet.n.01', 'name': 'ethernet'}, {'id': 7553, 'synset': 'ethernet_cable.n.01', 'name': 'ethernet_cable'}, {'id': 7554, 'synset': 'eton_jacket.n.01', 'name': 'Eton_jacket'}, {'id': 7555, 'synset': 'etui.n.01', 'name': 'etui'}, {'id': 7556, 'synset': 'eudiometer.n.01', 'name': 'eudiometer'}, {'id': 7557, 'synset': 'euphonium.n.01', 'name': 'euphonium'}, {'id': 7558, 'synset': 'evaporative_cooler.n.01', 'name': 'evaporative_cooler'}, {'id': 7559, 'synset': 'evening_bag.n.01', 'name': 'evening_bag'}, {'id': 7560, 'synset': 'exercise_bike.n.01', 'name': 'exercise_bike'}, {'id': 7561, 'synset': 'exercise_device.n.01', 'name': 'exercise_device'}, {'id': 7562, 'synset': 'exhaust.n.02', 'name': 'exhaust'}, {'id': 7563, 'synset': 'exhaust_fan.n.01', 'name': 'exhaust_fan'}, {'id': 7564, 'synset': 'exhaust_valve.n.01', 'name': 'exhaust_valve'}, {'id': 7565, 'synset': 'exhibition_hall.n.01', 'name': 'exhibition_hall'}, {'id': 7566, 'synset': 'exocet.n.01', 'name': 'Exocet'}, {'id': 7567, 'synset': 'expansion_bit.n.01', 'name': 'expansion_bit'}, {'id': 7568, 'synset': 'expansion_bolt.n.01', 'name': 'expansion_bolt'}, {'id': 7569, 'synset': 'explosive_detection_system.n.01', 'name': 'explosive_detection_system'}, {'id': 7570, 'synset': 'explosive_device.n.01', 'name': 'explosive_device'}, {'id': 7571, 'synset': 'explosive_trace_detection.n.01', 'name': 'explosive_trace_detection'}, {'id': 7572, 'synset': 'express.n.02', 'name': 'express'}, {'id': 7573, 'synset': 'extension.n.10', 'name': 'extension'}, {'id': 7574, 'synset': 'extension_cord.n.01', 'name': 'extension_cord'}, {'id': 7575, 'synset': 'external-combustion_engine.n.01', 'name': 'external-combustion_engine'}, {'id': 7576, 'synset': 'external_drive.n.01', 'name': 'external_drive'}, {'id': 7577, 'synset': 'extractor.n.01', 'name': 'extractor'}, {'id': 7578, 'synset': 'eyebrow_pencil.n.01', 'name': 'eyebrow_pencil'}, {'id': 7579, 'synset': 'eyecup.n.01', 'name': 'eyecup'}, {'id': 7580, 'synset': 'eyeliner.n.01', 'name': 'eyeliner'}, {'id': 7581, 'synset': 'eyepiece.n.01', 'name': 'eyepiece'}, {'id': 7582, 'synset': 'eyeshadow.n.01', 'name': 'eyeshadow'}, {'id': 7583, 'synset': 'fabric.n.01', 'name': 'fabric'}, {'id': 7584, 'synset': 'facade.n.01', 'name': 'facade'}, {'id': 7585, 'synset': 'face_guard.n.01', 'name': 'face_guard'}, {'id': 7586, 'synset': 'face_mask.n.01', 'name': 'face_mask'}, {'id': 7587, 'synset': 'faceplate.n.01', 'name': 'faceplate'}, {'id': 7588, 'synset': 'face_powder.n.01', 'name': 'face_powder'}, {'id': 7589, 'synset': 'face_veil.n.01', 'name': 'face_veil'}, {'id': 7590, 'synset': 'facing.n.03', 'name': 'facing'}, {'id': 7591, 'synset': 'facing.n.01', 'name': 'facing'}, {'id': 7592, 'synset': 'facing.n.02', 'name': 'facing'}, {'id': 7593, 'synset': 'facsimile.n.02', 'name': 'facsimile'}, {'id': 7594, 'synset': 'factory.n.01', 'name': 'factory'}, {'id': 7595, 'synset': 'factory_ship.n.01', 'name': 'factory_ship'}, {'id': 7596, 'synset': 'fagot.n.02', 'name': 'fagot'}, {'id': 7597, 'synset': 'fagot_stitch.n.01', 'name': 'fagot_stitch'}, {'id': 7598, 'synset': 'fahrenheit_thermometer.n.01', 'name': 'Fahrenheit_thermometer'}, {'id': 7599, 'synset': 'faience.n.01', 'name': 'faience'}, {'id': 7600, 'synset': 'faille.n.01', 'name': 'faille'}, {'id': 7601, 'synset': 'fairlead.n.01', 'name': 'fairlead'}, {'id': 7602, 'synset': 'fairy_light.n.01', 'name': 'fairy_light'}, {'id': 7603, 'synset': 'falchion.n.01', 'name': 'falchion'}, {'id': 7604, 'synset': 'fallboard.n.01', 'name': 'fallboard'}, {'id': 7605, 'synset': 'fallout_shelter.n.01', 'name': 'fallout_shelter'}, {'id': 7606, 'synset': 'false_face.n.01', 'name': 'false_face'}, {'id': 7607, 'synset': 'false_teeth.n.01', 'name': 'false_teeth'}, {'id': 7608, 'synset': 'family_room.n.01', 'name': 'family_room'}, {'id': 7609, 'synset': 'fan_belt.n.01', 'name': 'fan_belt'}, {'id': 7610, 'synset': 'fan_blade.n.01', 'name': 'fan_blade'}, {'id': 7611, 'synset': 'fancy_dress.n.01', 'name': 'fancy_dress'}, {'id': 7612, 'synset': 'fanion.n.01', 'name': 'fanion'}, {'id': 7613, 'synset': 'fanlight.n.03', 'name': 'fanlight'}, {'id': 7614, 'synset': 'fanjet.n.02', 'name': 'fanjet'}, {'id': 7615, 'synset': 'fanjet.n.01', 'name': 'fanjet'}, {'id': 7616, 'synset': 'fanny_pack.n.01', 'name': 'fanny_pack'}, {'id': 7617, 'synset': 'fan_tracery.n.01', 'name': 'fan_tracery'}, {'id': 7618, 'synset': 'fan_vaulting.n.01', 'name': 'fan_vaulting'}, {'id': 7619, 'synset': 'farm_building.n.01', 'name': 'farm_building'}, {'id': 7620, 'synset': "farmer's_market.n.01", 'name': "farmer's_market"}, {'id': 7621, 'synset': 'farmhouse.n.01', 'name': 'farmhouse'}, {'id': 7622, 'synset': 'farm_machine.n.01', 'name': 'farm_machine'}, {'id': 7623, 'synset': 'farmplace.n.01', 'name': 'farmplace'}, {'id': 7624, 'synset': 'farmyard.n.01', 'name': 'farmyard'}, {'id': 7625, 'synset': 'farthingale.n.01', 'name': 'farthingale'}, {'id': 7626, 'synset': 'fastener.n.02', 'name': 'fastener'}, {'id': 7627, 'synset': 'fast_reactor.n.01', 'name': 'fast_reactor'}, {'id': 7628, 'synset': 'fat_farm.n.01', 'name': 'fat_farm'}, {'id': 7629, 'synset': 'fatigues.n.01', 'name': 'fatigues'}, {'id': 7630, 'synset': 'fauld.n.01', 'name': 'fauld'}, {'id': 7631, 'synset': 'fauteuil.n.01', 'name': 'fauteuil'}, {'id': 7632, 'synset': 'feather_boa.n.01', 'name': 'feather_boa'}, {'id': 7633, 'synset': 'featheredge.n.01', 'name': 'featheredge'}, {'id': 7634, 'synset': 'feedback_circuit.n.01', 'name': 'feedback_circuit'}, {'id': 7635, 'synset': 'feedlot.n.01', 'name': 'feedlot'}, {'id': 7636, 'synset': 'fell.n.02', 'name': 'fell'}, {'id': 7637, 'synset': 'felloe.n.01', 'name': 'felloe'}, {'id': 7638, 'synset': 'felt.n.01', 'name': 'felt'}, {'id': 7639, 'synset': 'felt-tip_pen.n.01', 'name': 'felt-tip_pen'}, {'id': 7640, 'synset': 'felucca.n.01', 'name': 'felucca'}, {'id': 7641, 'synset': 'fence.n.01', 'name': 'fence'}, {'id': 7642, 'synset': 'fencing_mask.n.01', 'name': 'fencing_mask'}, {'id': 7643, 'synset': 'fencing_sword.n.01', 'name': 'fencing_sword'}, {'id': 7644, 'synset': 'fender.n.01', 'name': 'fender'}, {'id': 7645, 'synset': 'fender.n.02', 'name': 'fender'}, {'id': 7646, 'synset': 'ferrule.n.01', 'name': 'ferrule'}, {'id': 7647, 'synset': 'ferule.n.01', 'name': 'ferule'}, {'id': 7648, 'synset': 'festoon.n.01', 'name': 'festoon'}, {'id': 7649, 'synset': 'fetoscope.n.01', 'name': 'fetoscope'}, {'id': 7650, 'synset': 'fetter.n.01', 'name': 'fetter'}, {'id': 7651, 'synset': 'fez.n.02', 'name': 'fez'}, {'id': 7652, 'synset': 'fiber.n.05', 'name': 'fiber'}, {'id': 7653, 'synset': 'fiber_optic_cable.n.01', 'name': 'fiber_optic_cable'}, {'id': 7654, 'synset': 'fiberscope.n.01', 'name': 'fiberscope'}, {'id': 7655, 'synset': 'fichu.n.01', 'name': 'fichu'}, {'id': 7656, 'synset': 'fiddlestick.n.01', 'name': 'fiddlestick'}, {'id': 7657, 'synset': 'field_artillery.n.01', 'name': 'field_artillery'}, {'id': 7658, 'synset': 'field_coil.n.01', 'name': 'field_coil'}, {'id': 7659, 'synset': 'field-effect_transistor.n.01', 'name': 'field-effect_transistor'}, {'id': 7660, 'synset': 'field-emission_microscope.n.01', 'name': 'field-emission_microscope'}, {'id': 7661, 'synset': 'field_glass.n.01', 'name': 'field_glass'}, {'id': 7662, 'synset': 'field_hockey_ball.n.01', 'name': 'field_hockey_ball'}, {'id': 7663, 'synset': 'field_hospital.n.01', 'name': 'field_hospital'}, {'id': 7664, 'synset': 'field_house.n.01', 'name': 'field_house'}, {'id': 7665, 'synset': 'field_lens.n.01', 'name': 'field_lens'}, {'id': 7666, 'synset': 'field_magnet.n.01', 'name': 'field_magnet'}, {'id': 7667, 'synset': 'field-sequential_color_television.n.01', 'name': 'field-sequential_color_television'}, {'id': 7668, 'synset': 'field_tent.n.01', 'name': 'field_tent'}, {'id': 7669, 'synset': 'fieldwork.n.01', 'name': 'fieldwork'}, {'id': 7670, 'synset': 'fife.n.01', 'name': 'fife'}, {'id': 7671, 'synset': 'fifth_wheel.n.02', 'name': 'fifth_wheel'}, {'id': 7672, 'synset': 'fighting_chair.n.01', 'name': 'fighting_chair'}, {'id': 7673, 'synset': 'fig_leaf.n.02', 'name': 'fig_leaf'}, {'id': 7674, 'synset': 'figure_eight.n.01', 'name': 'figure_eight'}, {'id': 7675, 'synset': 'figure_loom.n.01', 'name': 'figure_loom'}, {'id': 7676, 'synset': 'figure_skate.n.01', 'name': 'figure_skate'}, {'id': 7677, 'synset': 'filament.n.04', 'name': 'filament'}, {'id': 7678, 'synset': 'filature.n.01', 'name': 'filature'}, {'id': 7679, 'synset': 'file_folder.n.01', 'name': 'file_folder'}, {'id': 7680, 'synset': 'file_server.n.01', 'name': 'file_server'}, {'id': 7681, 'synset': 'filigree.n.01', 'name': 'filigree'}, {'id': 7682, 'synset': 'filling.n.05', 'name': 'filling'}, {'id': 7683, 'synset': 'film.n.03', 'name': 'film'}, {'id': 7684, 'synset': 'film.n.05', 'name': 'film'}, {'id': 7685, 'synset': 'film_advance.n.01', 'name': 'film_advance'}, {'id': 7686, 'synset': 'filter.n.01', 'name': 'filter'}, {'id': 7687, 'synset': 'filter.n.02', 'name': 'filter'}, {'id': 7688, 'synset': 'finder.n.03', 'name': 'finder'}, {'id': 7689, 'synset': 'finery.n.01', 'name': 'finery'}, {'id': 7690, 'synset': 'fine-tooth_comb.n.01', 'name': 'fine-tooth_comb'}, {'id': 7691, 'synset': 'finger.n.03', 'name': 'finger'}, {'id': 7692, 'synset': 'fingerboard.n.03', 'name': 'fingerboard'}, {'id': 7693, 'synset': 'finger_bowl.n.01', 'name': 'finger_bowl'}, {'id': 7694, 'synset': 'finger_paint.n.01', 'name': 'finger_paint'}, {'id': 7695, 'synset': 'finger-painting.n.01', 'name': 'finger-painting'}, {'id': 7696, 'synset': 'finger_plate.n.01', 'name': 'finger_plate'}, {'id': 7697, 'synset': 'fingerstall.n.01', 'name': 'fingerstall'}, {'id': 7698, 'synset': 'finish_coat.n.02', 'name': 'finish_coat'}, {'id': 7699, 'synset': 'finish_coat.n.01', 'name': 'finish_coat'}, {'id': 7700, 'synset': 'finisher.n.05', 'name': 'finisher'}, {'id': 7701, 'synset': 'fin_keel.n.01', 'name': 'fin_keel'}, {'id': 7702, 'synset': 'fipple.n.01', 'name': 'fipple'}, {'id': 7703, 'synset': 'fipple_flute.n.01', 'name': 'fipple_flute'}, {'id': 7704, 'synset': 'fire.n.04', 'name': 'fire'}, {'id': 7705, 'synset': 'firearm.n.01', 'name': 'firearm'}, {'id': 7706, 'synset': 'fire_bell.n.01', 'name': 'fire_bell'}, {'id': 7707, 'synset': 'fireboat.n.01', 'name': 'fireboat'}, {'id': 7708, 'synset': 'firebox.n.01', 'name': 'firebox'}, {'id': 7709, 'synset': 'firebrick.n.01', 'name': 'firebrick'}, {'id': 7710, 'synset': 'fire_control_radar.n.01', 'name': 'fire_control_radar'}, {'id': 7711, 'synset': 'fire_control_system.n.01', 'name': 'fire_control_system'}, {'id': 7712, 'synset': 'fire_iron.n.01', 'name': 'fire_iron'}, {'id': 7713, 'synset': "fireman's_ax.n.01", 'name': "fireman's_ax"}, {'id': 7714, 'synset': 'fire_screen.n.01', 'name': 'fire_screen'}, {'id': 7715, 'synset': 'fire_tongs.n.01', 'name': 'fire_tongs'}, {'id': 7716, 'synset': 'fire_tower.n.01', 'name': 'fire_tower'}, {'id': 7717, 'synset': 'firewall.n.02', 'name': 'firewall'}, {'id': 7718, 'synset': 'firing_chamber.n.01', 'name': 'firing_chamber'}, {'id': 7719, 'synset': 'firing_pin.n.01', 'name': 'firing_pin'}, {'id': 7720, 'synset': 'firkin.n.02', 'name': 'firkin'}, {'id': 7721, 'synset': 'firmer_chisel.n.01', 'name': 'firmer_chisel'}, {'id': 7722, 'synset': 'first-aid_station.n.01', 'name': 'first-aid_station'}, {'id': 7723, 'synset': 'first_base.n.01', 'name': 'first_base'}, {'id': 7724, 'synset': 'first_class.n.03', 'name': 'first_class'}, {'id': 7725, 'synset': "fisherman's_bend.n.01", 'name': "fisherman's_bend"}, {'id': 7726, 'synset': "fisherman's_knot.n.01", 'name': "fisherman's_knot"}, {'id': 7727, 'synset': "fisherman's_lure.n.01", 'name': "fisherman's_lure"}, {'id': 7728, 'synset': 'fishhook.n.01', 'name': 'fishhook'}, {'id': 7729, 'synset': 'fishing_boat.n.01', 'name': 'fishing_boat'}, {'id': 7730, 'synset': 'fishing_gear.n.01', 'name': 'fishing_gear'}, {'id': 7731, 'synset': 'fish_joint.n.01', 'name': 'fish_joint'}, {'id': 7732, 'synset': 'fish_knife.n.01', 'name': 'fish_knife'}, {'id': 7733, 'synset': 'fishnet.n.01', 'name': 'fishnet'}, {'id': 7734, 'synset': 'fish_slice.n.01', 'name': 'fish_slice'}, {'id': 7735, 'synset': 'fitment.n.01', 'name': 'fitment'}, {'id': 7736, 'synset': 'fixative.n.02', 'name': 'fixative'}, {'id': 7737, 'synset': 'fixer-upper.n.01', 'name': 'fixer-upper'}, {'id': 7738, 'synset': 'flageolet.n.02', 'name': 'flageolet'}, {'id': 7739, 'synset': 'flagon.n.01', 'name': 'flagon'}, {'id': 7740, 'synset': 'flagship.n.02', 'name': 'flagship'}, {'id': 7741, 'synset': 'flail.n.01', 'name': 'flail'}, {'id': 7742, 'synset': 'flambeau.n.01', 'name': 'flambeau'}, {'id': 7743, 'synset': 'flamethrower.n.01', 'name': 'flamethrower'}, {'id': 7744, 'synset': 'flange.n.01', 'name': 'flange'}, {'id': 7745, 'synset': 'flannel.n.03', 'name': 'flannel'}, {'id': 7746, 'synset': 'flannelette.n.01', 'name': 'flannelette'}, {'id': 7747, 'synset': 'flap.n.05', 'name': 'flap'}, {'id': 7748, 'synset': 'flash.n.09', 'name': 'flash'}, {'id': 7749, 'synset': 'flash_camera.n.01', 'name': 'flash_camera'}, {'id': 7750, 'synset': 'flasher.n.02', 'name': 'flasher'}, {'id': 7751, 'synset': 'flashlight_battery.n.01', 'name': 'flashlight_battery'}, {'id': 7752, 'synset': 'flash_memory.n.01', 'name': 'flash_memory'}, {'id': 7753, 'synset': 'flask.n.01', 'name': 'flask'}, {'id': 7754, 'synset': 'flat_arch.n.01', 'name': 'flat_arch'}, {'id': 7755, 'synset': 'flatbed.n.02', 'name': 'flatbed'}, {'id': 7756, 'synset': 'flatbed_press.n.01', 'name': 'flatbed_press'}, {'id': 7757, 'synset': 'flat_bench.n.01', 'name': 'flat_bench'}, {'id': 7758, 'synset': 'flatcar.n.01', 'name': 'flatcar'}, {'id': 7759, 'synset': 'flat_file.n.01', 'name': 'flat_file'}, {'id': 7760, 'synset': 'flatlet.n.01', 'name': 'flatlet'}, {'id': 7761, 'synset': 'flat_panel_display.n.01', 'name': 'flat_panel_display'}, {'id': 7762, 'synset': 'flats.n.01', 'name': 'flats'}, {'id': 7763, 'synset': 'flat_tip_screwdriver.n.01', 'name': 'flat_tip_screwdriver'}, {'id': 7764, 'synset': 'fleet_ballistic_missile_submarine.n.01', 'name': 'fleet_ballistic_missile_submarine'}, {'id': 7765, 'synset': 'fleur-de-lis.n.02', 'name': 'fleur-de-lis'}, {'id': 7766, 'synset': 'flight_simulator.n.01', 'name': 'flight_simulator'}, {'id': 7767, 'synset': 'flintlock.n.02', 'name': 'flintlock'}, {'id': 7768, 'synset': 'flintlock.n.01', 'name': 'flintlock'}, {'id': 7769, 'synset': 'float.n.05', 'name': 'float'}, {'id': 7770, 'synset': 'floating_dock.n.01', 'name': 'floating_dock'}, {'id': 7771, 'synset': 'floatplane.n.01', 'name': 'floatplane'}, {'id': 7772, 'synset': 'flood.n.03', 'name': 'flood'}, {'id': 7773, 'synset': 'floor.n.01', 'name': 'floor'}, {'id': 7774, 'synset': 'floor.n.02', 'name': 'floor'}, {'id': 7775, 'synset': 'floor.n.09', 'name': 'floor'}, {'id': 7776, 'synset': 'floorboard.n.02', 'name': 'floorboard'}, {'id': 7777, 'synset': 'floor_cover.n.01', 'name': 'floor_cover'}, {'id': 7778, 'synset': 'floor_joist.n.01', 'name': 'floor_joist'}, {'id': 7779, 'synset': 'floor_lamp.n.01', 'name': 'floor_lamp'}, {'id': 7780, 'synset': 'flophouse.n.01', 'name': 'flophouse'}, {'id': 7781, 'synset': 'florist.n.02', 'name': 'florist'}, {'id': 7782, 'synset': 'floss.n.01', 'name': 'floss'}, {'id': 7783, 'synset': 'flotsam.n.01', 'name': 'flotsam'}, {'id': 7784, 'synset': 'flour_bin.n.01', 'name': 'flour_bin'}, {'id': 7785, 'synset': 'flour_mill.n.01', 'name': 'flour_mill'}, {'id': 7786, 'synset': 'flowerbed.n.01', 'name': 'flowerbed'}, {'id': 7787, 'synset': 'flugelhorn.n.01', 'name': 'flugelhorn'}, {'id': 7788, 'synset': 'fluid_drive.n.01', 'name': 'fluid_drive'}, {'id': 7789, 'synset': 'fluid_flywheel.n.01', 'name': 'fluid_flywheel'}, {'id': 7790, 'synset': 'flume.n.02', 'name': 'flume'}, {'id': 7791, 'synset': 'fluorescent_lamp.n.01', 'name': 'fluorescent_lamp'}, {'id': 7792, 'synset': 'fluoroscope.n.01', 'name': 'fluoroscope'}, {'id': 7793, 'synset': 'flush_toilet.n.01', 'name': 'flush_toilet'}, {'id': 7794, 'synset': 'flute.n.01', 'name': 'flute'}, {'id': 7795, 'synset': 'flux_applicator.n.01', 'name': 'flux_applicator'}, {'id': 7796, 'synset': 'fluxmeter.n.01', 'name': 'fluxmeter'}, {'id': 7797, 'synset': 'fly.n.05', 'name': 'fly'}, {'id': 7798, 'synset': 'flying_boat.n.01', 'name': 'flying_boat'}, {'id': 7799, 'synset': 'flying_buttress.n.01', 'name': 'flying_buttress'}, {'id': 7800, 'synset': 'flying_carpet.n.01', 'name': 'flying_carpet'}, {'id': 7801, 'synset': 'flying_jib.n.01', 'name': 'flying_jib'}, {'id': 7802, 'synset': 'fly_rod.n.01', 'name': 'fly_rod'}, {'id': 7803, 'synset': 'fly_tent.n.01', 'name': 'fly_tent'}, {'id': 7804, 'synset': 'flytrap.n.01', 'name': 'flytrap'}, {'id': 7805, 'synset': 'flywheel.n.01', 'name': 'flywheel'}, {'id': 7806, 'synset': 'fob.n.03', 'name': 'fob'}, {'id': 7807, 'synset': 'foghorn.n.02', 'name': 'foghorn'}, {'id': 7808, 'synset': 'foglamp.n.01', 'name': 'foglamp'}, {'id': 7809, 'synset': 'foil.n.05', 'name': 'foil'}, {'id': 7810, 'synset': 'fold.n.06', 'name': 'fold'}, {'id': 7811, 'synset': 'folder.n.02', 'name': 'folder'}, {'id': 7812, 'synset': 'folding_door.n.01', 'name': 'folding_door'}, {'id': 7813, 'synset': 'folding_saw.n.01', 'name': 'folding_saw'}, {'id': 7814, 'synset': 'food_court.n.01', 'name': 'food_court'}, {'id': 7815, 'synset': 'food_hamper.n.01', 'name': 'food_hamper'}, {'id': 7816, 'synset': 'foot.n.11', 'name': 'foot'}, {'id': 7817, 'synset': 'footage.n.01', 'name': 'footage'}, {'id': 7818, 'synset': 'football_stadium.n.01', 'name': 'football_stadium'}, {'id': 7819, 'synset': 'footbath.n.01', 'name': 'footbath'}, {'id': 7820, 'synset': 'foot_brake.n.01', 'name': 'foot_brake'}, {'id': 7821, 'synset': 'footbridge.n.01', 'name': 'footbridge'}, {'id': 7822, 'synset': 'foothold.n.02', 'name': 'foothold'}, {'id': 7823, 'synset': 'footlocker.n.01', 'name': 'footlocker'}, {'id': 7824, 'synset': 'foot_rule.n.01', 'name': 'foot_rule'}, {'id': 7825, 'synset': 'footwear.n.02', 'name': 'footwear'}, {'id': 7826, 'synset': 'footwear.n.01', 'name': 'footwear'}, {'id': 7827, 'synset': 'forceps.n.01', 'name': 'forceps'}, {'id': 7828, 'synset': 'force_pump.n.01', 'name': 'force_pump'}, {'id': 7829, 'synset': 'fore-and-after.n.01', 'name': 'fore-and-after'}, {'id': 7830, 'synset': 'fore-and-aft_sail.n.01', 'name': 'fore-and-aft_sail'}, {'id': 7831, 'synset': 'forecastle.n.01', 'name': 'forecastle'}, {'id': 7832, 'synset': 'forecourt.n.01', 'name': 'forecourt'}, {'id': 7833, 'synset': 'foredeck.n.01', 'name': 'foredeck'}, {'id': 7834, 'synset': 'fore_edge.n.01', 'name': 'fore_edge'}, {'id': 7835, 'synset': 'foreground.n.02', 'name': 'foreground'}, {'id': 7836, 'synset': 'foremast.n.01', 'name': 'foremast'}, {'id': 7837, 'synset': 'fore_plane.n.01', 'name': 'fore_plane'}, {'id': 7838, 'synset': 'foresail.n.01', 'name': 'foresail'}, {'id': 7839, 'synset': 'forestay.n.01', 'name': 'forestay'}, {'id': 7840, 'synset': 'foretop.n.01', 'name': 'foretop'}, {'id': 7841, 'synset': 'fore-topmast.n.01', 'name': 'fore-topmast'}, {'id': 7842, 'synset': 'fore-topsail.n.01', 'name': 'fore-topsail'}, {'id': 7843, 'synset': 'forge.n.01', 'name': 'forge'}, {'id': 7844, 'synset': 'fork.n.04', 'name': 'fork'}, {'id': 7845, 'synset': 'formalwear.n.01', 'name': 'formalwear'}, {'id': 7846, 'synset': 'formica.n.01', 'name': 'Formica'}, {'id': 7847, 'synset': 'fortification.n.01', 'name': 'fortification'}, {'id': 7848, 'synset': 'fortress.n.01', 'name': 'fortress'}, {'id': 7849, 'synset': 'forty-five.n.01', 'name': 'forty-five'}, {'id': 7850, 'synset': 'foucault_pendulum.n.01', 'name': 'Foucault_pendulum'}, {'id': 7851, 'synset': 'foulard.n.01', 'name': 'foulard'}, {'id': 7852, 'synset': 'foul-weather_gear.n.01', 'name': 'foul-weather_gear'}, {'id': 7853, 'synset': 'foundation_garment.n.01', 'name': 'foundation_garment'}, {'id': 7854, 'synset': 'foundry.n.01', 'name': 'foundry'}, {'id': 7855, 'synset': 'fountain.n.01', 'name': 'fountain'}, {'id': 7856, 'synset': 'fountain_pen.n.01', 'name': 'fountain_pen'}, {'id': 7857, 'synset': 'four-in-hand.n.01', 'name': 'four-in-hand'}, {'id': 7858, 'synset': 'four-poster.n.01', 'name': 'four-poster'}, {'id': 7859, 'synset': 'four-pounder.n.01', 'name': 'four-pounder'}, {'id': 7860, 'synset': 'four-stroke_engine.n.01', 'name': 'four-stroke_engine'}, {'id': 7861, 'synset': 'four-wheel_drive.n.02', 'name': 'four-wheel_drive'}, {'id': 7862, 'synset': 'four-wheel_drive.n.01', 'name': 'four-wheel_drive'}, {'id': 7863, 'synset': 'four-wheeler.n.01', 'name': 'four-wheeler'}, {'id': 7864, 'synset': 'fowling_piece.n.01', 'name': 'fowling_piece'}, {'id': 7865, 'synset': 'foxhole.n.01', 'name': 'foxhole'}, {'id': 7866, 'synset': 'fragmentation_bomb.n.01', 'name': 'fragmentation_bomb'}, {'id': 7867, 'synset': 'frail.n.02', 'name': 'frail'}, {'id': 7868, 'synset': 'fraise.n.02', 'name': 'fraise'}, {'id': 7869, 'synset': 'frame.n.10', 'name': 'frame'}, {'id': 7870, 'synset': 'frame.n.01', 'name': 'frame'}, {'id': 7871, 'synset': 'frame_buffer.n.01', 'name': 'frame_buffer'}, {'id': 7872, 'synset': 'framework.n.03', 'name': 'framework'}, {'id': 7873, 'synset': 'francis_turbine.n.01', 'name': 'Francis_turbine'}, {'id': 7874, 'synset': 'franking_machine.n.01', 'name': 'franking_machine'}, {'id': 7875, 'synset': 'free_house.n.01', 'name': 'free_house'}, {'id': 7876, 'synset': 'free-reed.n.01', 'name': 'free-reed'}, {'id': 7877, 'synset': 'free-reed_instrument.n.01', 'name': 'free-reed_instrument'}, {'id': 7878, 'synset': 'freewheel.n.01', 'name': 'freewheel'}, {'id': 7879, 'synset': 'freight_elevator.n.01', 'name': 'freight_elevator'}, {'id': 7880, 'synset': 'freight_liner.n.01', 'name': 'freight_liner'}, {'id': 7881, 'synset': 'freight_train.n.01', 'name': 'freight_train'}, {'id': 7882, 'synset': 'french_door.n.01', 'name': 'French_door'}, {'id': 7883, 'synset': 'french_horn.n.01', 'name': 'French_horn'}, {'id': 7884, 'synset': 'french_polish.n.02', 'name': 'French_polish'}, {'id': 7885, 'synset': 'french_roof.n.01', 'name': 'French_roof'}, {'id': 7886, 'synset': 'french_window.n.01', 'name': 'French_window'}, {'id': 7887, 'synset': 'fresnel_lens.n.01', 'name': 'Fresnel_lens'}, {'id': 7888, 'synset': 'fret.n.04', 'name': 'fret'}, {'id': 7889, 'synset': 'friary.n.01', 'name': 'friary'}, {'id': 7890, 'synset': 'friction_clutch.n.01', 'name': 'friction_clutch'}, {'id': 7891, 'synset': 'frieze.n.02', 'name': 'frieze'}, {'id': 7892, 'synset': 'frieze.n.01', 'name': 'frieze'}, {'id': 7893, 'synset': 'frigate.n.02', 'name': 'frigate'}, {'id': 7894, 'synset': 'frigate.n.01', 'name': 'frigate'}, {'id': 7895, 'synset': 'frill.n.03', 'name': 'frill'}, {'id': 7896, 'synset': 'frock.n.01', 'name': 'frock'}, {'id': 7897, 'synset': 'frock_coat.n.01', 'name': 'frock_coat'}, {'id': 7898, 'synset': 'frontlet.n.01', 'name': 'frontlet'}, {'id': 7899, 'synset': 'front_porch.n.01', 'name': 'front_porch'}, {'id': 7900, 'synset': 'front_projector.n.01', 'name': 'front_projector'}, {'id': 7901, 'synset': 'fruit_machine.n.01', 'name': 'fruit_machine'}, {'id': 7902, 'synset': 'fuel_filter.n.01', 'name': 'fuel_filter'}, {'id': 7903, 'synset': 'fuel_gauge.n.01', 'name': 'fuel_gauge'}, {'id': 7904, 'synset': 'fuel_injection.n.01', 'name': 'fuel_injection'}, {'id': 7905, 'synset': 'fuel_system.n.01', 'name': 'fuel_system'}, {'id': 7906, 'synset': 'full-dress_uniform.n.01', 'name': 'full-dress_uniform'}, {'id': 7907, 'synset': 'full_metal_jacket.n.01', 'name': 'full_metal_jacket'}, {'id': 7908, 'synset': 'full_skirt.n.01', 'name': 'full_skirt'}, {'id': 7909, 'synset': 'fumigator.n.02', 'name': 'fumigator'}, {'id': 7910, 'synset': 'funeral_home.n.01', 'name': 'funeral_home'}, {'id': 7911, 'synset': 'funny_wagon.n.01', 'name': 'funny_wagon'}, {'id': 7912, 'synset': 'fur.n.03', 'name': 'fur'}, {'id': 7913, 'synset': 'fur_coat.n.01', 'name': 'fur_coat'}, {'id': 7914, 'synset': 'fur_hat.n.01', 'name': 'fur_hat'}, {'id': 7915, 'synset': 'furnace.n.01', 'name': 'furnace'}, {'id': 7916, 'synset': 'furnace_lining.n.01', 'name': 'furnace_lining'}, {'id': 7917, 'synset': 'furnace_room.n.01', 'name': 'furnace_room'}, {'id': 7918, 'synset': 'furnishing.n.02', 'name': 'furnishing'}, {'id': 7919, 'synset': 'furnishing.n.01', 'name': 'furnishing'}, {'id': 7920, 'synset': 'furniture.n.01', 'name': 'furniture'}, {'id': 7921, 'synset': 'fur-piece.n.01', 'name': 'fur-piece'}, {'id': 7922, 'synset': 'furrow.n.01', 'name': 'furrow'}, {'id': 7923, 'synset': 'fuse.n.01', 'name': 'fuse'}, {'id': 7924, 'synset': 'fusee_drive.n.01', 'name': 'fusee_drive'}, {'id': 7925, 'synset': 'fuselage.n.01', 'name': 'fuselage'}, {'id': 7926, 'synset': 'fusil.n.01', 'name': 'fusil'}, {'id': 7927, 'synset': 'fustian.n.02', 'name': 'fustian'}, {'id': 7928, 'synset': 'gabardine.n.01', 'name': 'gabardine'}, {'id': 7929, 'synset': 'gable.n.01', 'name': 'gable'}, {'id': 7930, 'synset': 'gable_roof.n.01', 'name': 'gable_roof'}, {'id': 7931, 'synset': 'gadgetry.n.01', 'name': 'gadgetry'}, {'id': 7932, 'synset': 'gaff.n.03', 'name': 'gaff'}, {'id': 7933, 'synset': 'gaff.n.02', 'name': 'gaff'}, {'id': 7934, 'synset': 'gaff.n.01', 'name': 'gaff'}, {'id': 7935, 'synset': 'gaffsail.n.01', 'name': 'gaffsail'}, {'id': 7936, 'synset': 'gaff_topsail.n.01', 'name': 'gaff_topsail'}, {'id': 7937, 'synset': 'gaiter.n.03', 'name': 'gaiter'}, {'id': 7938, 'synset': 'gaiter.n.02', 'name': 'gaiter'}, {'id': 7939, 'synset': 'galilean_telescope.n.01', 'name': 'Galilean_telescope'}, {'id': 7940, 'synset': 'galleon.n.01', 'name': 'galleon'}, {'id': 7941, 'synset': 'gallery.n.04', 'name': 'gallery'}, {'id': 7942, 'synset': 'gallery.n.03', 'name': 'gallery'}, {'id': 7943, 'synset': 'galley.n.04', 'name': 'galley'}, {'id': 7944, 'synset': 'galley.n.03', 'name': 'galley'}, {'id': 7945, 'synset': 'galley.n.02', 'name': 'galley'}, {'id': 7946, 'synset': 'gallows.n.01', 'name': 'gallows'}, {'id': 7947, 'synset': 'gallows_tree.n.01', 'name': 'gallows_tree'}, {'id': 7948, 'synset': 'galvanometer.n.01', 'name': 'galvanometer'}, {'id': 7949, 'synset': 'gambling_house.n.01', 'name': 'gambling_house'}, {'id': 7950, 'synset': 'gambrel.n.01', 'name': 'gambrel'}, {'id': 7951, 'synset': 'game.n.09', 'name': 'game'}, {'id': 7952, 'synset': 'gamebag.n.01', 'name': 'gamebag'}, {'id': 7953, 'synset': 'game_equipment.n.01', 'name': 'game_equipment'}, {'id': 7954, 'synset': 'gaming_table.n.01', 'name': 'gaming_table'}, {'id': 7955, 'synset': 'gamp.n.01', 'name': 'gamp'}, {'id': 7956, 'synset': 'gangplank.n.01', 'name': 'gangplank'}, {'id': 7957, 'synset': 'gangsaw.n.01', 'name': 'gangsaw'}, {'id': 7958, 'synset': 'gangway.n.01', 'name': 'gangway'}, {'id': 7959, 'synset': 'gantlet.n.04', 'name': 'gantlet'}, {'id': 7960, 'synset': 'gantry.n.01', 'name': 'gantry'}, {'id': 7961, 'synset': 'garage.n.01', 'name': 'garage'}, {'id': 7962, 'synset': 'garage.n.02', 'name': 'garage'}, {'id': 7963, 'synset': 'garand_rifle.n.01', 'name': 'Garand_rifle'}, {'id': 7964, 'synset': 'garboard.n.01', 'name': 'garboard'}, {'id': 7965, 'synset': 'garden.n.01', 'name': 'garden'}, {'id': 7966, 'synset': 'garden.n.03', 'name': 'garden'}, {'id': 7967, 'synset': 'garden_rake.n.01', 'name': 'garden_rake'}, {'id': 7968, 'synset': 'garden_spade.n.01', 'name': 'garden_spade'}, {'id': 7969, 'synset': 'garden_tool.n.01', 'name': 'garden_tool'}, {'id': 7970, 'synset': 'garden_trowel.n.01', 'name': 'garden_trowel'}, {'id': 7971, 'synset': 'gargoyle.n.01', 'name': 'gargoyle'}, {'id': 7972, 'synset': 'garibaldi.n.02', 'name': 'garibaldi'}, {'id': 7973, 'synset': 'garlic_press.n.01', 'name': 'garlic_press'}, {'id': 7974, 'synset': 'garment.n.01', 'name': 'garment'}, {'id': 7975, 'synset': 'garment_bag.n.01', 'name': 'garment_bag'}, {'id': 7976, 'synset': 'garrison_cap.n.01', 'name': 'garrison_cap'}, {'id': 7977, 'synset': 'garrote.n.01', 'name': 'garrote'}, {'id': 7978, 'synset': 'garter.n.01', 'name': 'garter'}, {'id': 7979, 'synset': 'garter_belt.n.01', 'name': 'garter_belt'}, {'id': 7980, 'synset': 'garter_stitch.n.01', 'name': 'garter_stitch'}, {'id': 7981, 'synset': 'gas_guzzler.n.01', 'name': 'gas_guzzler'}, {'id': 7982, 'synset': 'gas_shell.n.01', 'name': 'gas_shell'}, {'id': 7983, 'synset': 'gas_bracket.n.01', 'name': 'gas_bracket'}, {'id': 7984, 'synset': 'gas_burner.n.01', 'name': 'gas_burner'}, {'id': 7985, 'synset': 'gas-cooled_reactor.n.01', 'name': 'gas-cooled_reactor'}, {'id': 7986, 'synset': 'gas-discharge_tube.n.01', 'name': 'gas-discharge_tube'}, {'id': 7987, 'synset': 'gas_engine.n.01', 'name': 'gas_engine'}, {'id': 7988, 'synset': 'gas_fixture.n.01', 'name': 'gas_fixture'}, {'id': 7989, 'synset': 'gas_furnace.n.01', 'name': 'gas_furnace'}, {'id': 7990, 'synset': 'gas_gun.n.01', 'name': 'gas_gun'}, {'id': 7991, 'synset': 'gas_heater.n.01', 'name': 'gas_heater'}, {'id': 7992, 'synset': 'gas_holder.n.01', 'name': 'gas_holder'}, {'id': 7993, 'synset': 'gasket.n.01', 'name': 'gasket'}, {'id': 7994, 'synset': 'gas_lamp.n.01', 'name': 'gas_lamp'}, {'id': 7995, 'synset': 'gas_maser.n.01', 'name': 'gas_maser'}, {'id': 7996, 'synset': 'gas_meter.n.01', 'name': 'gas_meter'}, {'id': 7997, 'synset': 'gasoline_engine.n.01', 'name': 'gasoline_engine'}, {'id': 7998, 'synset': 'gasoline_gauge.n.01', 'name': 'gasoline_gauge'}, {'id': 7999, 'synset': 'gas_oven.n.02', 'name': 'gas_oven'}, {'id': 8000, 'synset': 'gas_oven.n.01', 'name': 'gas_oven'}, {'id': 8001, 'synset': 'gas_pump.n.01', 'name': 'gas_pump'}, {'id': 8002, 'synset': 'gas_range.n.01', 'name': 'gas_range'}, {'id': 8003, 'synset': 'gas_ring.n.01', 'name': 'gas_ring'}, {'id': 8004, 'synset': 'gas_tank.n.01', 'name': 'gas_tank'}, {'id': 8005, 'synset': 'gas_thermometer.n.01', 'name': 'gas_thermometer'}, {'id': 8006, 'synset': 'gastroscope.n.01', 'name': 'gastroscope'}, {'id': 8007, 'synset': 'gas_turbine.n.01', 'name': 'gas_turbine'}, {'id': 8008, 'synset': 'gas-turbine_ship.n.01', 'name': 'gas-turbine_ship'}, {'id': 8009, 'synset': 'gat.n.01', 'name': 'gat'}, {'id': 8010, 'synset': 'gate.n.01', 'name': 'gate'}, {'id': 8011, 'synset': 'gatehouse.n.01', 'name': 'gatehouse'}, {'id': 8012, 'synset': 'gateleg_table.n.01', 'name': 'gateleg_table'}, {'id': 8013, 'synset': 'gatepost.n.01', 'name': 'gatepost'}, {'id': 8014, 'synset': 'gathered_skirt.n.01', 'name': 'gathered_skirt'}, {'id': 8015, 'synset': 'gatling_gun.n.01', 'name': 'Gatling_gun'}, {'id': 8016, 'synset': 'gauge.n.01', 'name': 'gauge'}, {'id': 8017, 'synset': 'gauntlet.n.03', 'name': 'gauntlet'}, {'id': 8018, 'synset': 'gauntlet.n.02', 'name': 'gauntlet'}, {'id': 8019, 'synset': 'gauze.n.02', 'name': 'gauze'}, {'id': 8020, 'synset': 'gauze.n.01', 'name': 'gauze'}, {'id': 8021, 'synset': 'gavel.n.01', 'name': 'gavel'}, {'id': 8022, 'synset': 'gazebo.n.01', 'name': 'gazebo'}, {'id': 8023, 'synset': 'gear.n.01', 'name': 'gear'}, {'id': 8024, 'synset': 'gear.n.04', 'name': 'gear'}, {'id': 8025, 'synset': 'gear.n.03', 'name': 'gear'}, {'id': 8026, 'synset': 'gearbox.n.01', 'name': 'gearbox'}, {'id': 8027, 'synset': 'gearing.n.01', 'name': 'gearing'}, {'id': 8028, 'synset': 'gearset.n.01', 'name': 'gearset'}, {'id': 8029, 'synset': 'gearshift.n.01', 'name': 'gearshift'}, {'id': 8030, 'synset': 'geiger_counter.n.01', 'name': 'Geiger_counter'}, {'id': 8031, 'synset': 'geiger_tube.n.01', 'name': 'Geiger_tube'}, {'id': 8032, 'synset': 'gene_chip.n.01', 'name': 'gene_chip'}, {'id': 8033, 'synset': 'general-purpose_bomb.n.01', 'name': 'general-purpose_bomb'}, {'id': 8034, 'synset': 'generator.n.01', 'name': 'generator'}, {'id': 8035, 'synset': 'generator.n.04', 'name': 'generator'}, {'id': 8036, 'synset': 'geneva_gown.n.01', 'name': 'Geneva_gown'}, {'id': 8037, 'synset': 'geodesic_dome.n.01', 'name': 'geodesic_dome'}, {'id': 8038, 'synset': 'georgette.n.01', 'name': 'georgette'}, {'id': 8039, 'synset': 'gharry.n.01', 'name': 'gharry'}, {'id': 8040, 'synset': 'ghat.n.01', 'name': 'ghat'}, {'id': 8041, 'synset': 'ghetto_blaster.n.01', 'name': 'ghetto_blaster'}, {'id': 8042, 'synset': 'gift_shop.n.01', 'name': 'gift_shop'}, {'id': 8043, 'synset': 'gift_wrapping.n.01', 'name': 'gift_wrapping'}, {'id': 8044, 'synset': 'gig.n.05', 'name': 'gig'}, {'id': 8045, 'synset': 'gig.n.04', 'name': 'gig'}, {'id': 8046, 'synset': 'gig.n.01', 'name': 'gig'}, {'id': 8047, 'synset': 'gig.n.03', 'name': 'gig'}, {'id': 8048, 'synset': 'gildhall.n.01', 'name': 'gildhall'}, {'id': 8049, 'synset': 'gill_net.n.01', 'name': 'gill_net'}, {'id': 8050, 'synset': 'gilt.n.01', 'name': 'gilt'}, {'id': 8051, 'synset': 'gimbal.n.01', 'name': 'gimbal'}, {'id': 8052, 'synset': 'gingham.n.01', 'name': 'gingham'}, {'id': 8053, 'synset': 'girandole.n.01', 'name': 'girandole'}, {'id': 8054, 'synset': 'girder.n.01', 'name': 'girder'}, {'id': 8055, 'synset': 'glass.n.07', 'name': 'glass'}, {'id': 8056, 'synset': 'glass_cutter.n.03', 'name': 'glass_cutter'}, {'id': 8057, 'synset': 'glasses_case.n.01', 'name': 'glasses_case'}, {'id': 8058, 'synset': 'glebe_house.n.01', 'name': 'glebe_house'}, {'id': 8059, 'synset': 'glengarry.n.01', 'name': 'Glengarry'}, {'id': 8060, 'synset': 'glider.n.01', 'name': 'glider'}, {'id': 8061, 'synset': 'global_positioning_system.n.01', 'name': 'Global_Positioning_System'}, {'id': 8062, 'synset': 'glockenspiel.n.01', 'name': 'glockenspiel'}, {'id': 8063, 'synset': 'glory_hole.n.01', 'name': 'glory_hole'}, {'id': 8064, 'synset': 'glove_compartment.n.01', 'name': 'glove_compartment'}, {'id': 8065, 'synset': 'glow_lamp.n.01', 'name': 'glow_lamp'}, {'id': 8066, 'synset': 'glow_tube.n.01', 'name': 'glow_tube'}, {'id': 8067, 'synset': 'glyptic_art.n.01', 'name': 'glyptic_art'}, {'id': 8068, 'synset': 'glyptics.n.01', 'name': 'glyptics'}, {'id': 8069, 'synset': 'gnomon.n.01', 'name': 'gnomon'}, {'id': 8070, 'synset': 'goal.n.03', 'name': 'goal'}, {'id': 8071, 'synset': 'goalmouth.n.01', 'name': 'goalmouth'}, {'id': 8072, 'synset': 'goalpost.n.01', 'name': 'goalpost'}, {'id': 8073, 'synset': 'goblet.n.01', 'name': 'goblet'}, {'id': 8074, 'synset': 'godown.n.01', 'name': 'godown'}, {'id': 8075, 'synset': 'go-kart.n.01', 'name': 'go-kart'}, {'id': 8076, 'synset': 'gold_plate.n.02', 'name': 'gold_plate'}, {'id': 8077, 'synset': 'golf_bag.n.01', 'name': 'golf_bag'}, {'id': 8078, 'synset': 'golf_ball.n.01', 'name': 'golf_ball'}, {'id': 8079, 'synset': 'golf-club_head.n.01', 'name': 'golf-club_head'}, {'id': 8080, 'synset': 'golf_equipment.n.01', 'name': 'golf_equipment'}, {'id': 8081, 'synset': 'golf_glove.n.01', 'name': 'golf_glove'}, {'id': 8082, 'synset': 'golliwog.n.01', 'name': 'golliwog'}, {'id': 8083, 'synset': 'gong.n.01', 'name': 'gong'}, {'id': 8084, 'synset': 'goniometer.n.01', 'name': 'goniometer'}, {'id': 8085, 'synset': 'gordian_knot.n.02', 'name': 'Gordian_knot'}, {'id': 8086, 'synset': 'gorget.n.01', 'name': 'gorget'}, {'id': 8087, 'synset': 'gossamer.n.01', 'name': 'gossamer'}, {'id': 8088, 'synset': 'gothic_arch.n.01', 'name': 'Gothic_arch'}, {'id': 8089, 'synset': 'gouache.n.01', 'name': 'gouache'}, {'id': 8090, 'synset': 'gouge.n.02', 'name': 'gouge'}, {'id': 8091, 'synset': 'gourd.n.01', 'name': 'gourd'}, {'id': 8092, 'synset': 'government_building.n.01', 'name': 'government_building'}, {'id': 8093, 'synset': 'government_office.n.01', 'name': 'government_office'}, {'id': 8094, 'synset': 'gown.n.01', 'name': 'gown'}, {'id': 8095, 'synset': 'gown.n.05', 'name': 'gown'}, {'id': 8096, 'synset': 'gown.n.04', 'name': 'gown'}, {'id': 8097, 'synset': 'grab.n.01', 'name': 'grab'}, {'id': 8098, 'synset': 'grab_bag.n.02', 'name': 'grab_bag'}, {'id': 8099, 'synset': 'grab_bar.n.01', 'name': 'grab_bar'}, {'id': 8100, 'synset': 'grace_cup.n.01', 'name': 'grace_cup'}, {'id': 8101, 'synset': 'grade_separation.n.01', 'name': 'grade_separation'}, {'id': 8102, 'synset': 'graduated_cylinder.n.01', 'name': 'graduated_cylinder'}, {'id': 8103, 'synset': 'graffito.n.01', 'name': 'graffito'}, {'id': 8104, 'synset': 'gramophone.n.01', 'name': 'gramophone'}, {'id': 8105, 'synset': 'granary.n.01', 'name': 'granary'}, {'id': 8106, 'synset': 'grandfather_clock.n.01', 'name': 'grandfather_clock'}, {'id': 8107, 'synset': 'grand_piano.n.01', 'name': 'grand_piano'}, {'id': 8108, 'synset': 'graniteware.n.01', 'name': 'graniteware'}, {'id': 8109, 'synset': 'granny_knot.n.01', 'name': 'granny_knot'}, {'id': 8110, 'synset': 'grape_arbor.n.01', 'name': 'grape_arbor'}, {'id': 8111, 'synset': 'grapnel.n.02', 'name': 'grapnel'}, {'id': 8112, 'synset': 'grapnel.n.01', 'name': 'grapnel'}, {'id': 8113, 'synset': 'grass_skirt.n.01', 'name': 'grass_skirt'}, {'id': 8114, 'synset': 'grate.n.01', 'name': 'grate'}, {'id': 8115, 'synset': 'grate.n.03', 'name': 'grate'}, {'id': 8116, 'synset': 'graver.n.01', 'name': 'graver'}, {'id': 8117, 'synset': 'gravimeter.n.02', 'name': 'gravimeter'}, {'id': 8118, 'synset': 'gravure.n.03', 'name': 'gravure'}, {'id': 8119, 'synset': 'grey.n.06', 'name': 'grey'}, {'id': 8120, 'synset': 'grease-gun.n.01', 'name': 'grease-gun'}, {'id': 8121, 'synset': 'greasepaint.n.01', 'name': 'greasepaint'}, {'id': 8122, 'synset': 'greasy_spoon.n.01', 'name': 'greasy_spoon'}, {'id': 8123, 'synset': 'greatcoat.n.01', 'name': 'greatcoat'}, {'id': 8124, 'synset': 'great_hall.n.01', 'name': 'great_hall'}, {'id': 8125, 'synset': 'greave.n.01', 'name': 'greave'}, {'id': 8126, 'synset': 'greengrocery.n.02', 'name': 'greengrocery'}, {'id': 8127, 'synset': 'greenhouse.n.01', 'name': 'greenhouse'}, {'id': 8128, 'synset': 'grenade.n.01', 'name': 'grenade'}, {'id': 8129, 'synset': 'grid.n.05', 'name': 'grid'}, {'id': 8130, 'synset': 'grille.n.02', 'name': 'grille'}, {'id': 8131, 'synset': 'grillroom.n.01', 'name': 'grillroom'}, {'id': 8132, 'synset': 'grinder.n.04', 'name': 'grinder'}, {'id': 8133, 'synset': 'grinding_wheel.n.01', 'name': 'grinding_wheel'}, {'id': 8134, 'synset': 'grindstone.n.01', 'name': 'grindstone'}, {'id': 8135, 'synset': 'gripsack.n.01', 'name': 'gripsack'}, {'id': 8136, 'synset': 'gristmill.n.01', 'name': 'gristmill'}, {'id': 8137, 'synset': 'grocery_store.n.01', 'name': 'grocery_store'}, {'id': 8138, 'synset': 'grogram.n.01', 'name': 'grogram'}, {'id': 8139, 'synset': 'groined_vault.n.01', 'name': 'groined_vault'}, {'id': 8140, 'synset': 'groover.n.01', 'name': 'groover'}, {'id': 8141, 'synset': 'grosgrain.n.01', 'name': 'grosgrain'}, {'id': 8142, 'synset': 'gros_point.n.01', 'name': 'gros_point'}, {'id': 8143, 'synset': 'ground.n.09', 'name': 'ground'}, {'id': 8144, 'synset': 'ground_bait.n.01', 'name': 'ground_bait'}, {'id': 8145, 'synset': 'ground_control.n.01', 'name': 'ground_control'}, {'id': 8146, 'synset': 'ground_floor.n.01', 'name': 'ground_floor'}, {'id': 8147, 'synset': 'groundsheet.n.01', 'name': 'groundsheet'}, {'id': 8148, 'synset': 'g-string.n.01', 'name': 'G-string'}, {'id': 8149, 'synset': 'guard.n.03', 'name': 'guard'}, {'id': 8150, 'synset': 'guard_boat.n.01', 'name': 'guard_boat'}, {'id': 8151, 'synset': 'guardroom.n.02', 'name': 'guardroom'}, {'id': 8152, 'synset': 'guardroom.n.01', 'name': 'guardroom'}, {'id': 8153, 'synset': 'guard_ship.n.01', 'name': 'guard_ship'}, {'id': 8154, 'synset': "guard's_van.n.01", 'name': "guard's_van"}, {'id': 8155, 'synset': 'gueridon.n.01', 'name': 'gueridon'}, {'id': 8156, 'synset': 'guarnerius.n.03', 'name': 'Guarnerius'}, {'id': 8157, 'synset': 'guesthouse.n.01', 'name': 'guesthouse'}, {'id': 8158, 'synset': 'guestroom.n.01', 'name': 'guestroom'}, {'id': 8159, 'synset': 'guidance_system.n.01', 'name': 'guidance_system'}, {'id': 8160, 'synset': 'guided_missile.n.01', 'name': 'guided_missile'}, {'id': 8161, 'synset': 'guided_missile_cruiser.n.01', 'name': 'guided_missile_cruiser'}, {'id': 8162, 'synset': 'guided_missile_frigate.n.01', 'name': 'guided_missile_frigate'}, {'id': 8163, 'synset': 'guildhall.n.01', 'name': 'guildhall'}, {'id': 8164, 'synset': 'guilloche.n.01', 'name': 'guilloche'}, {'id': 8165, 'synset': 'guillotine.n.02', 'name': 'guillotine'}, {'id': 8166, 'synset': 'guimpe.n.02', 'name': 'guimpe'}, {'id': 8167, 'synset': 'guimpe.n.01', 'name': 'guimpe'}, {'id': 8168, 'synset': 'guitar_pick.n.01', 'name': 'guitar_pick'}, {'id': 8169, 'synset': 'gulag.n.01', 'name': 'gulag'}, {'id': 8170, 'synset': 'gunboat.n.01', 'name': 'gunboat'}, {'id': 8171, 'synset': 'gun_carriage.n.01', 'name': 'gun_carriage'}, {'id': 8172, 'synset': 'gun_case.n.01', 'name': 'gun_case'}, {'id': 8173, 'synset': 'gun_emplacement.n.01', 'name': 'gun_emplacement'}, {'id': 8174, 'synset': 'gun_enclosure.n.01', 'name': 'gun_enclosure'}, {'id': 8175, 'synset': 'gunlock.n.01', 'name': 'gunlock'}, {'id': 8176, 'synset': 'gunnery.n.01', 'name': 'gunnery'}, {'id': 8177, 'synset': 'gunnysack.n.01', 'name': 'gunnysack'}, {'id': 8178, 'synset': 'gun_pendulum.n.01', 'name': 'gun_pendulum'}, {'id': 8179, 'synset': 'gun_room.n.01', 'name': 'gun_room'}, {'id': 8180, 'synset': 'gunsight.n.01', 'name': 'gunsight'}, {'id': 8181, 'synset': 'gun_trigger.n.01', 'name': 'gun_trigger'}, {'id': 8182, 'synset': 'gurney.n.01', 'name': 'gurney'}, {'id': 8183, 'synset': 'gusher.n.01', 'name': 'gusher'}, {'id': 8184, 'synset': 'gusset.n.03', 'name': 'gusset'}, {'id': 8185, 'synset': 'gusset.n.02', 'name': 'gusset'}, {'id': 8186, 'synset': 'guy.n.03', 'name': 'guy'}, {'id': 8187, 'synset': 'gymnastic_apparatus.n.01', 'name': 'gymnastic_apparatus'}, {'id': 8188, 'synset': 'gym_shoe.n.01', 'name': 'gym_shoe'}, {'id': 8189, 'synset': 'gym_suit.n.01', 'name': 'gym_suit'}, {'id': 8190, 'synset': 'gymslip.n.01', 'name': 'gymslip'}, {'id': 8191, 'synset': 'gypsy_cab.n.01', 'name': 'gypsy_cab'}, {'id': 8192, 'synset': 'gyrocompass.n.01', 'name': 'gyrocompass'}, {'id': 8193, 'synset': 'gyroscope.n.01', 'name': 'gyroscope'}, {'id': 8194, 'synset': 'gyrostabilizer.n.01', 'name': 'gyrostabilizer'}, {'id': 8195, 'synset': 'habergeon.n.01', 'name': 'habergeon'}, {'id': 8196, 'synset': 'habit.n.03', 'name': 'habit'}, {'id': 8197, 'synset': 'habit.n.05', 'name': 'habit'}, {'id': 8198, 'synset': 'hacienda.n.02', 'name': 'hacienda'}, {'id': 8199, 'synset': 'hacksaw.n.01', 'name': 'hacksaw'}, {'id': 8200, 'synset': 'haft.n.01', 'name': 'haft'}, {'id': 8201, 'synset': 'haircloth.n.01', 'name': 'haircloth'}, {'id': 8202, 'synset': 'hairdressing.n.01', 'name': 'hairdressing'}, {'id': 8203, 'synset': 'hairpiece.n.01', 'name': 'hairpiece'}, {'id': 8204, 'synset': 'hair_shirt.n.01', 'name': 'hair_shirt'}, {'id': 8205, 'synset': 'hair_slide.n.01', 'name': 'hair_slide'}, {'id': 8206, 'synset': 'hair_spray.n.01', 'name': 'hair_spray'}, {'id': 8207, 'synset': 'hairspring.n.01', 'name': 'hairspring'}, {'id': 8208, 'synset': 'hair_trigger.n.01', 'name': 'hair_trigger'}, {'id': 8209, 'synset': 'halberd.n.01', 'name': 'halberd'}, {'id': 8210, 'synset': 'half_binding.n.01', 'name': 'half_binding'}, {'id': 8211, 'synset': 'half_hatchet.n.01', 'name': 'half_hatchet'}, {'id': 8212, 'synset': 'half_hitch.n.01', 'name': 'half_hitch'}, {'id': 8213, 'synset': 'half_track.n.01', 'name': 'half_track'}, {'id': 8214, 'synset': 'hall.n.13', 'name': 'hall'}, {'id': 8215, 'synset': 'hall.n.03', 'name': 'hall'}, {'id': 8216, 'synset': 'hall.n.12', 'name': 'hall'}, {'id': 8217, 'synset': 'hall_of_fame.n.01', 'name': 'Hall_of_Fame'}, {'id': 8218, 'synset': 'hall_of_residence.n.01', 'name': 'hall_of_residence'}, {'id': 8219, 'synset': 'hallstand.n.01', 'name': 'hallstand'}, {'id': 8220, 'synset': 'halter.n.01', 'name': 'halter'}, {'id': 8221, 'synset': 'hame.n.01', 'name': 'hame'}, {'id': 8222, 'synset': 'hammer.n.07', 'name': 'hammer'}, {'id': 8223, 'synset': 'hammer.n.05', 'name': 'hammer'}, {'id': 8224, 'synset': 'hammerhead.n.02', 'name': 'hammerhead'}, {'id': 8225, 'synset': 'hand.n.08', 'name': 'hand'}, {'id': 8226, 'synset': 'handball.n.01', 'name': 'handball'}, {'id': 8227, 'synset': 'handbarrow.n.01', 'name': 'handbarrow'}, {'id': 8228, 'synset': 'handbell.n.01', 'name': 'handbell'}, {'id': 8229, 'synset': 'handbow.n.01', 'name': 'handbow'}, {'id': 8230, 'synset': 'hand_brake.n.01', 'name': 'hand_brake'}, {'id': 8231, 'synset': 'hand_calculator.n.01', 'name': 'hand_calculator'}, {'id': 8232, 'synset': 'handcar.n.01', 'name': 'handcar'}, {'id': 8233, 'synset': 'hand_cream.n.01', 'name': 'hand_cream'}, {'id': 8234, 'synset': 'hand_drill.n.01', 'name': 'hand_drill'}, {'id': 8235, 'synset': 'hand_glass.n.02', 'name': 'hand_glass'}, {'id': 8236, 'synset': 'hand_grenade.n.01', 'name': 'hand_grenade'}, {'id': 8237, 'synset': 'hand-held_computer.n.01', 'name': 'hand-held_computer'}, {'id': 8238, 'synset': 'handhold.n.01', 'name': 'handhold'}, {'id': 8239, 'synset': 'handlebar.n.01', 'name': 'handlebar'}, {'id': 8240, 'synset': 'handloom.n.01', 'name': 'handloom'}, {'id': 8241, 'synset': 'hand_lotion.n.01', 'name': 'hand_lotion'}, {'id': 8242, 'synset': 'hand_luggage.n.01', 'name': 'hand_luggage'}, {'id': 8243, 'synset': 'hand-me-down.n.01', 'name': 'hand-me-down'}, {'id': 8244, 'synset': 'hand_mower.n.01', 'name': 'hand_mower'}, {'id': 8245, 'synset': 'hand_pump.n.01', 'name': 'hand_pump'}, {'id': 8246, 'synset': 'handrest.n.01', 'name': 'handrest'}, {'id': 8247, 'synset': 'handset.n.01', 'name': 'handset'}, {'id': 8248, 'synset': 'hand_shovel.n.01', 'name': 'hand_shovel'}, {'id': 8249, 'synset': 'handspike.n.01', 'name': 'handspike'}, {'id': 8250, 'synset': 'handstamp.n.01', 'name': 'handstamp'}, {'id': 8251, 'synset': 'hand_throttle.n.01', 'name': 'hand_throttle'}, {'id': 8252, 'synset': 'hand_tool.n.01', 'name': 'hand_tool'}, {'id': 8253, 'synset': 'hand_truck.n.01', 'name': 'hand_truck'}, {'id': 8254, 'synset': 'handwear.n.01', 'name': 'handwear'}, {'id': 8255, 'synset': 'handwheel.n.02', 'name': 'handwheel'}, {'id': 8256, 'synset': 'handwheel.n.01', 'name': 'handwheel'}, {'id': 8257, 'synset': 'hangar_queen.n.01', 'name': 'hangar_queen'}, {'id': 8258, 'synset': 'hanger.n.02', 'name': 'hanger'}, {'id': 8259, 'synset': 'hang_glider.n.02', 'name': 'hang_glider'}, {'id': 8260, 'synset': "hangman's_rope.n.01", 'name': "hangman's_rope"}, {'id': 8261, 'synset': 'hank.n.01', 'name': 'hank'}, {'id': 8262, 'synset': 'hansom.n.01', 'name': 'hansom'}, {'id': 8263, 'synset': 'harbor.n.02', 'name': 'harbor'}, {'id': 8264, 'synset': 'hard_disc.n.01', 'name': 'hard_disc'}, {'id': 8265, 'synset': 'hard_hat.n.02', 'name': 'hard_hat'}, {'id': 8266, 'synset': 'hardtop.n.01', 'name': 'hardtop'}, {'id': 8267, 'synset': 'hardware.n.02', 'name': 'hardware'}, {'id': 8268, 'synset': 'hardware_store.n.01', 'name': 'hardware_store'}, {'id': 8269, 'synset': 'harmonica.n.01', 'name': 'harmonica'}, {'id': 8270, 'synset': 'harness.n.02', 'name': 'harness'}, {'id': 8271, 'synset': 'harness.n.01', 'name': 'harness'}, {'id': 8272, 'synset': 'harp.n.01', 'name': 'harp'}, {'id': 8273, 'synset': 'harp.n.02', 'name': 'harp'}, {'id': 8274, 'synset': 'harpoon.n.01', 'name': 'harpoon'}, {'id': 8275, 'synset': 'harpoon_gun.n.01', 'name': 'harpoon_gun'}, {'id': 8276, 'synset': 'harpoon_log.n.01', 'name': 'harpoon_log'}, {'id': 8277, 'synset': 'harpsichord.n.01', 'name': 'harpsichord'}, {'id': 8278, 'synset': 'harris_tweed.n.01', 'name': 'Harris_Tweed'}, {'id': 8279, 'synset': 'harrow.n.01', 'name': 'harrow'}, {'id': 8280, 'synset': 'harvester.n.02', 'name': 'harvester'}, {'id': 8281, 'synset': 'hash_house.n.01', 'name': 'hash_house'}, {'id': 8282, 'synset': 'hasp.n.01', 'name': 'hasp'}, {'id': 8283, 'synset': 'hatch.n.03', 'name': 'hatch'}, {'id': 8284, 'synset': 'hatchback.n.02', 'name': 'hatchback'}, {'id': 8285, 'synset': 'hatchback.n.01', 'name': 'hatchback'}, {'id': 8286, 'synset': 'hatchel.n.01', 'name': 'hatchel'}, {'id': 8287, 'synset': 'hatchet.n.02', 'name': 'hatchet'}, {'id': 8288, 'synset': 'hatpin.n.01', 'name': 'hatpin'}, {'id': 8289, 'synset': 'hauberk.n.01', 'name': 'hauberk'}, {'id': 8290, 'synset': 'hawaiian_guitar.n.01', 'name': 'Hawaiian_guitar'}, {'id': 8291, 'synset': 'hawse.n.01', 'name': 'hawse'}, {'id': 8292, 'synset': 'hawser.n.01', 'name': 'hawser'}, {'id': 8293, 'synset': 'hawser_bend.n.01', 'name': 'hawser_bend'}, {'id': 8294, 'synset': 'hay_bale.n.01', 'name': 'hay_bale'}, {'id': 8295, 'synset': 'hayfork.n.01', 'name': 'hayfork'}, {'id': 8296, 'synset': 'hayloft.n.01', 'name': 'hayloft'}, {'id': 8297, 'synset': 'haymaker.n.01', 'name': 'haymaker'}, {'id': 8298, 'synset': 'hayrack.n.02', 'name': 'hayrack'}, {'id': 8299, 'synset': 'hayrack.n.01', 'name': 'hayrack'}, {'id': 8300, 'synset': 'hazard.n.03', 'name': 'hazard'}, {'id': 8301, 'synset': 'head.n.31', 'name': 'head'}, {'id': 8302, 'synset': 'head.n.30', 'name': 'head'}, {'id': 8303, 'synset': 'head.n.29', 'name': 'head'}, {'id': 8304, 'synset': 'headdress.n.01', 'name': 'headdress'}, {'id': 8305, 'synset': 'header.n.05', 'name': 'header'}, {'id': 8306, 'synset': 'header.n.04', 'name': 'header'}, {'id': 8307, 'synset': 'header.n.03', 'name': 'header'}, {'id': 8308, 'synset': 'header.n.02', 'name': 'header'}, {'id': 8309, 'synset': 'headfast.n.01', 'name': 'headfast'}, {'id': 8310, 'synset': 'head_gasket.n.01', 'name': 'head_gasket'}, {'id': 8311, 'synset': 'head_gate.n.02', 'name': 'head_gate'}, {'id': 8312, 'synset': 'headgear.n.03', 'name': 'headgear'}, {'id': 8313, 'synset': 'headpiece.n.02', 'name': 'headpiece'}, {'id': 8314, 'synset': 'headpin.n.01', 'name': 'headpin'}, {'id': 8315, 'synset': 'headquarters.n.01', 'name': 'headquarters'}, {'id': 8316, 'synset': 'headrace.n.01', 'name': 'headrace'}, {'id': 8317, 'synset': 'headrest.n.02', 'name': 'headrest'}, {'id': 8318, 'synset': 'headsail.n.01', 'name': 'headsail'}, {'id': 8319, 'synset': 'head_shop.n.01', 'name': 'head_shop'}, {'id': 8320, 'synset': 'headstock.n.01', 'name': 'headstock'}, {'id': 8321, 'synset': 'health_spa.n.01', 'name': 'health_spa'}, {'id': 8322, 'synset': 'hearing_aid.n.02', 'name': 'hearing_aid'}, {'id': 8323, 'synset': 'hearing_aid.n.01', 'name': 'hearing_aid'}, {'id': 8324, 'synset': 'hearse.n.01', 'name': 'hearse'}, {'id': 8325, 'synset': 'hearth.n.02', 'name': 'hearth'}, {'id': 8326, 'synset': 'hearthrug.n.01', 'name': 'hearthrug'}, {'id': 8327, 'synset': 'heart-lung_machine.n.01', 'name': 'heart-lung_machine'}, {'id': 8328, 'synset': 'heat_engine.n.01', 'name': 'heat_engine'}, {'id': 8329, 'synset': 'heat_exchanger.n.01', 'name': 'heat_exchanger'}, {'id': 8330, 'synset': 'heating_pad.n.01', 'name': 'heating_pad'}, {'id': 8331, 'synset': 'heat_lamp.n.01', 'name': 'heat_lamp'}, {'id': 8332, 'synset': 'heat_pump.n.01', 'name': 'heat_pump'}, {'id': 8333, 'synset': 'heat-seeking_missile.n.01', 'name': 'heat-seeking_missile'}, {'id': 8334, 'synset': 'heat_shield.n.01', 'name': 'heat_shield'}, {'id': 8335, 'synset': 'heat_sink.n.01', 'name': 'heat_sink'}, {'id': 8336, 'synset': 'heaume.n.01', 'name': 'heaume'}, {'id': 8337, 'synset': 'heaver.n.01', 'name': 'heaver'}, {'id': 8338, 'synset': 'heavier-than-air_craft.n.01', 'name': 'heavier-than-air_craft'}, {'id': 8339, 'synset': 'heckelphone.n.01', 'name': 'heckelphone'}, {'id': 8340, 'synset': 'hectograph.n.01', 'name': 'hectograph'}, {'id': 8341, 'synset': 'hedge.n.01', 'name': 'hedge'}, {'id': 8342, 'synset': 'hedge_trimmer.n.01', 'name': 'hedge_trimmer'}, {'id': 8343, 'synset': 'helicon.n.01', 'name': 'helicon'}, {'id': 8344, 'synset': 'heliograph.n.01', 'name': 'heliograph'}, {'id': 8345, 'synset': 'heliometer.n.01', 'name': 'heliometer'}, {'id': 8346, 'synset': 'helm.n.01', 'name': 'helm'}, {'id': 8347, 'synset': 'helmet.n.01', 'name': 'helmet'}, {'id': 8348, 'synset': 'hematocrit.n.02', 'name': 'hematocrit'}, {'id': 8349, 'synset': 'hemming-stitch.n.01', 'name': 'hemming-stitch'}, {'id': 8350, 'synset': 'hemostat.n.01', 'name': 'hemostat'}, {'id': 8351, 'synset': 'hemstitch.n.01', 'name': 'hemstitch'}, {'id': 8352, 'synset': 'henroost.n.01', 'name': 'henroost'}, {'id': 8353, 'synset': 'heraldry.n.02', 'name': 'heraldry'}, {'id': 8354, 'synset': 'hermitage.n.01', 'name': 'hermitage'}, {'id': 8355, 'synset': 'herringbone.n.01', 'name': 'herringbone'}, {'id': 8356, 'synset': 'herringbone.n.02', 'name': 'herringbone'}, {'id': 8357, 'synset': 'herschelian_telescope.n.01', 'name': 'Herschelian_telescope'}, {'id': 8358, 'synset': 'hessian_boot.n.01', 'name': 'Hessian_boot'}, {'id': 8359, 'synset': 'heterodyne_receiver.n.01', 'name': 'heterodyne_receiver'}, {'id': 8360, 'synset': 'hibachi.n.01', 'name': 'hibachi'}, {'id': 8361, 'synset': 'hideaway.n.02', 'name': 'hideaway'}, {'id': 8362, 'synset': 'hi-fi.n.01', 'name': 'hi-fi'}, {'id': 8363, 'synset': 'high_altar.n.01', 'name': 'high_altar'}, {'id': 8364, 'synset': 'high-angle_gun.n.01', 'name': 'high-angle_gun'}, {'id': 8365, 'synset': 'highball_glass.n.01', 'name': 'highball_glass'}, {'id': 8366, 'synset': 'highboard.n.01', 'name': 'highboard'}, {'id': 8367, 'synset': 'highboy.n.01', 'name': 'highboy'}, {'id': 8368, 'synset': 'high_gear.n.01', 'name': 'high_gear'}, {'id': 8369, 'synset': 'high-hat_cymbal.n.01', 'name': 'high-hat_cymbal'}, {'id': 8370, 'synset': 'highlighter.n.02', 'name': 'highlighter'}, {'id': 8371, 'synset': 'highlighter.n.01', 'name': 'highlighter'}, {'id': 8372, 'synset': 'high-pass_filter.n.01', 'name': 'high-pass_filter'}, {'id': 8373, 'synset': 'high-rise.n.01', 'name': 'high-rise'}, {'id': 8374, 'synset': 'high_table.n.01', 'name': 'high_table'}, {'id': 8375, 'synset': 'high-warp_loom.n.01', 'name': 'high-warp_loom'}, {'id': 8376, 'synset': 'hijab.n.01', 'name': 'hijab'}, {'id': 8377, 'synset': 'hinging_post.n.01', 'name': 'hinging_post'}, {'id': 8378, 'synset': 'hip_boot.n.01', 'name': 'hip_boot'}, {'id': 8379, 'synset': 'hipflask.n.01', 'name': 'hipflask'}, {'id': 8380, 'synset': 'hip_pad.n.01', 'name': 'hip_pad'}, {'id': 8381, 'synset': 'hip_pocket.n.01', 'name': 'hip_pocket'}, {'id': 8382, 'synset': 'hippodrome.n.01', 'name': 'hippodrome'}, {'id': 8383, 'synset': 'hip_roof.n.01', 'name': 'hip_roof'}, {'id': 8384, 'synset': 'hitch.n.05', 'name': 'hitch'}, {'id': 8385, 'synset': 'hitch.n.04', 'name': 'hitch'}, {'id': 8386, 'synset': 'hitching_post.n.01', 'name': 'hitching_post'}, {'id': 8387, 'synset': 'hitchrack.n.01', 'name': 'hitchrack'}, {'id': 8388, 'synset': 'hob.n.03', 'name': 'hob'}, {'id': 8389, 'synset': 'hobble_skirt.n.01', 'name': 'hobble_skirt'}, {'id': 8390, 'synset': 'hockey_skate.n.01', 'name': 'hockey_skate'}, {'id': 8391, 'synset': 'hod.n.01', 'name': 'hod'}, {'id': 8392, 'synset': 'hodoscope.n.01', 'name': 'hodoscope'}, {'id': 8393, 'synset': 'hoe.n.01', 'name': 'hoe'}, {'id': 8394, 'synset': 'hoe_handle.n.01', 'name': 'hoe_handle'}, {'id': 8395, 'synset': 'hogshead.n.02', 'name': 'hogshead'}, {'id': 8396, 'synset': 'hoist.n.01', 'name': 'hoist'}, {'id': 8397, 'synset': 'hold.n.07', 'name': 'hold'}, {'id': 8398, 'synset': 'holder.n.01', 'name': 'holder'}, {'id': 8399, 'synset': 'holding_cell.n.01', 'name': 'holding_cell'}, {'id': 8400, 'synset': 'holding_device.n.01', 'name': 'holding_device'}, {'id': 8401, 'synset': 'holding_pen.n.01', 'name': 'holding_pen'}, {'id': 8402, 'synset': 'hollowware.n.01', 'name': 'hollowware'}, {'id': 8403, 'synset': 'holster.n.01', 'name': 'holster'}, {'id': 8404, 'synset': 'holster.n.02', 'name': 'holster'}, {'id': 8405, 'synset': 'holy_of_holies.n.02', 'name': 'holy_of_holies'}, {'id': 8406, 'synset': 'home.n.09', 'name': 'home'}, {'id': 8407, 'synset': 'home_appliance.n.01', 'name': 'home_appliance'}, {'id': 8408, 'synset': 'home_computer.n.01', 'name': 'home_computer'}, {'id': 8409, 'synset': 'home_room.n.01', 'name': 'home_room'}, {'id': 8410, 'synset': 'homespun.n.01', 'name': 'homespun'}, {'id': 8411, 'synset': 'homestead.n.03', 'name': 'homestead'}, {'id': 8412, 'synset': 'home_theater.n.01', 'name': 'home_theater'}, {'id': 8413, 'synset': 'homing_torpedo.n.01', 'name': 'homing_torpedo'}, {'id': 8414, 'synset': 'hone.n.01', 'name': 'hone'}, {'id': 8415, 'synset': 'honeycomb.n.02', 'name': 'honeycomb'}, {'id': 8416, 'synset': 'hood.n.09', 'name': 'hood'}, {'id': 8417, 'synset': 'hood.n.08', 'name': 'hood'}, {'id': 8418, 'synset': 'hood.n.07', 'name': 'hood'}, {'id': 8419, 'synset': 'hood.n.05', 'name': 'hood'}, {'id': 8420, 'synset': 'hood_latch.n.01', 'name': 'hood_latch'}, {'id': 8421, 'synset': 'hook.n.04', 'name': 'hook'}, {'id': 8422, 'synset': 'hook.n.01', 'name': 'hook'}, {'id': 8423, 'synset': 'hook_and_eye.n.01', 'name': 'hook_and_eye'}, {'id': 8424, 'synset': 'hookup.n.02', 'name': 'hookup'}, {'id': 8425, 'synset': 'hookup.n.01', 'name': 'hookup'}, {'id': 8426, 'synset': 'hook_wrench.n.01', 'name': 'hook_wrench'}, {'id': 8427, 'synset': 'hoopskirt.n.01', 'name': 'hoopskirt'}, {'id': 8428, 'synset': 'hoosegow.n.01', 'name': 'hoosegow'}, {'id': 8429, 'synset': 'hoover.n.04', 'name': 'Hoover'}, {'id': 8430, 'synset': 'hope_chest.n.01', 'name': 'hope_chest'}, {'id': 8431, 'synset': 'hopper.n.01', 'name': 'hopper'}, {'id': 8432, 'synset': 'hopsacking.n.01', 'name': 'hopsacking'}, {'id': 8433, 'synset': 'horizontal_bar.n.01', 'name': 'horizontal_bar'}, {'id': 8434, 'synset': 'horizontal_stabilizer.n.01', 'name': 'horizontal_stabilizer'}, {'id': 8435, 'synset': 'horizontal_tail.n.01', 'name': 'horizontal_tail'}, {'id': 8436, 'synset': 'horn.n.09', 'name': 'horn'}, {'id': 8437, 'synset': 'horn.n.01', 'name': 'horn'}, {'id': 8438, 'synset': 'horn.n.08', 'name': 'horn'}, {'id': 8439, 'synset': 'horn_button.n.01', 'name': 'horn_button'}, {'id': 8440, 'synset': 'hornpipe.n.03', 'name': 'hornpipe'}, {'id': 8441, 'synset': 'horse.n.02', 'name': 'horse'}, {'id': 8442, 'synset': 'horsebox.n.01', 'name': 'horsebox'}, {'id': 8443, 'synset': 'horsecar.n.01', 'name': 'horsecar'}, {'id': 8444, 'synset': 'horse_cart.n.01', 'name': 'horse_cart'}, {'id': 8445, 'synset': 'horsecloth.n.01', 'name': 'horsecloth'}, {'id': 8446, 'synset': 'horse-drawn_vehicle.n.01', 'name': 'horse-drawn_vehicle'}, {'id': 8447, 'synset': 'horsehair.n.02', 'name': 'horsehair'}, {'id': 8448, 'synset': 'horsehair_wig.n.01', 'name': 'horsehair_wig'}, {'id': 8449, 'synset': 'horseless_carriage.n.01', 'name': 'horseless_carriage'}, {'id': 8450, 'synset': 'horse_pistol.n.01', 'name': 'horse_pistol'}, {'id': 8451, 'synset': 'horseshoe.n.02', 'name': 'horseshoe'}, {'id': 8452, 'synset': 'horseshoe.n.01', 'name': 'horseshoe'}, {'id': 8453, 'synset': 'horse-trail.n.01', 'name': 'horse-trail'}, {'id': 8454, 'synset': 'horsewhip.n.01', 'name': 'horsewhip'}, {'id': 8455, 'synset': 'hose.n.02', 'name': 'hose'}, {'id': 8456, 'synset': 'hosiery.n.01', 'name': 'hosiery'}, {'id': 8457, 'synset': 'hospice.n.01', 'name': 'hospice'}, {'id': 8458, 'synset': 'hospital.n.01', 'name': 'hospital'}, {'id': 8459, 'synset': 'hospital_bed.n.01', 'name': 'hospital_bed'}, {'id': 8460, 'synset': 'hospital_room.n.01', 'name': 'hospital_room'}, {'id': 8461, 'synset': 'hospital_ship.n.01', 'name': 'hospital_ship'}, {'id': 8462, 'synset': 'hospital_train.n.01', 'name': 'hospital_train'}, {'id': 8463, 'synset': 'hostel.n.02', 'name': 'hostel'}, {'id': 8464, 'synset': 'hostel.n.01', 'name': 'hostel'}, {'id': 8465, 'synset': 'hotel.n.01', 'name': 'hotel'}, {'id': 8466, 'synset': 'hotel-casino.n.02', 'name': 'hotel-casino'}, {'id': 8467, 'synset': 'hotel-casino.n.01', 'name': 'hotel-casino'}, {'id': 8468, 'synset': 'hotel_room.n.01', 'name': 'hotel_room'}, {'id': 8469, 'synset': 'hot_line.n.01', 'name': 'hot_line'}, {'id': 8470, 'synset': 'hot_pants.n.02', 'name': 'hot_pants'}, {'id': 8471, 'synset': 'hot_rod.n.01', 'name': 'hot_rod'}, {'id': 8472, 'synset': 'hot_spot.n.03', 'name': 'hot_spot'}, {'id': 8473, 'synset': 'hot_tub.n.01', 'name': 'hot_tub'}, {'id': 8474, 'synset': 'hot-water_bottle.n.01', 'name': 'hot-water_bottle'}, {'id': 8475, 'synset': 'houndstooth_check.n.01', 'name': 'houndstooth_check'}, {'id': 8476, 'synset': 'hour_hand.n.01', 'name': 'hour_hand'}, {'id': 8477, 'synset': 'house.n.01', 'name': 'house'}, {'id': 8478, 'synset': 'house.n.12', 'name': 'house'}, {'id': 8479, 'synset': 'houselights.n.01', 'name': 'houselights'}, {'id': 8480, 'synset': 'house_of_cards.n.02', 'name': 'house_of_cards'}, {'id': 8481, 'synset': 'house_of_correction.n.01', 'name': 'house_of_correction'}, {'id': 8482, 'synset': 'house_paint.n.01', 'name': 'house_paint'}, {'id': 8483, 'synset': 'housetop.n.01', 'name': 'housetop'}, {'id': 8484, 'synset': 'housing.n.01', 'name': 'housing'}, {'id': 8485, 'synset': 'hovel.n.01', 'name': 'hovel'}, {'id': 8486, 'synset': 'hovercraft.n.01', 'name': 'hovercraft'}, {'id': 8487, 'synset': 'howdah.n.01', 'name': 'howdah'}, {'id': 8488, 'synset': 'huarache.n.01', 'name': 'huarache'}, {'id': 8489, 'synset': 'hub-and-spoke.n.01', 'name': 'hub-and-spoke'}, {'id': 8490, 'synset': 'hubcap.n.01', 'name': 'hubcap'}, {'id': 8491, 'synset': 'huck.n.01', 'name': 'huck'}, {'id': 8492, 'synset': 'hug-me-tight.n.01', 'name': 'hug-me-tight'}, {'id': 8493, 'synset': 'hula-hoop.n.01', 'name': 'hula-hoop'}, {'id': 8494, 'synset': 'hulk.n.02', 'name': 'hulk'}, {'id': 8495, 'synset': 'hull.n.06', 'name': 'hull'}, {'id': 8496, 'synset': 'humeral_veil.n.01', 'name': 'humeral_veil'}, {'id': 8497, 'synset': 'humvee.n.01', 'name': 'Humvee'}, {'id': 8498, 'synset': 'hunter.n.04', 'name': 'hunter'}, {'id': 8499, 'synset': 'hunting_knife.n.01', 'name': 'hunting_knife'}, {'id': 8500, 'synset': 'hurdle.n.01', 'name': 'hurdle'}, {'id': 8501, 'synset': 'hurricane_deck.n.01', 'name': 'hurricane_deck'}, {'id': 8502, 'synset': 'hurricane_lamp.n.01', 'name': 'hurricane_lamp'}, {'id': 8503, 'synset': 'hut.n.01', 'name': 'hut'}, {'id': 8504, 'synset': 'hutch.n.01', 'name': 'hutch'}, {'id': 8505, 'synset': 'hutment.n.01', 'name': 'hutment'}, {'id': 8506, 'synset': 'hydraulic_brake.n.01', 'name': 'hydraulic_brake'}, {'id': 8507, 'synset': 'hydraulic_press.n.01', 'name': 'hydraulic_press'}, {'id': 8508, 'synset': 'hydraulic_pump.n.01', 'name': 'hydraulic_pump'}, {'id': 8509, 'synset': 'hydraulic_system.n.01', 'name': 'hydraulic_system'}, {'id': 8510, 'synset': 'hydraulic_transmission.n.01', 'name': 'hydraulic_transmission'}, {'id': 8511, 'synset': 'hydroelectric_turbine.n.01', 'name': 'hydroelectric_turbine'}, {'id': 8512, 'synset': 'hydrofoil.n.02', 'name': 'hydrofoil'}, {'id': 8513, 'synset': 'hydrofoil.n.01', 'name': 'hydrofoil'}, {'id': 8514, 'synset': 'hydrogen_bomb.n.01', 'name': 'hydrogen_bomb'}, {'id': 8515, 'synset': 'hydrometer.n.01', 'name': 'hydrometer'}, {'id': 8516, 'synset': 'hygrodeik.n.01', 'name': 'hygrodeik'}, {'id': 8517, 'synset': 'hygrometer.n.01', 'name': 'hygrometer'}, {'id': 8518, 'synset': 'hygroscope.n.01', 'name': 'hygroscope'}, {'id': 8519, 'synset': 'hyperbaric_chamber.n.01', 'name': 'hyperbaric_chamber'}, {'id': 8520, 'synset': 'hypercoaster.n.01', 'name': 'hypercoaster'}, {'id': 8521, 'synset': 'hypermarket.n.01', 'name': 'hypermarket'}, {'id': 8522, 'synset': 'hypodermic_needle.n.01', 'name': 'hypodermic_needle'}, {'id': 8523, 'synset': 'hypodermic_syringe.n.01', 'name': 'hypodermic_syringe'}, {'id': 8524, 'synset': 'hypsometer.n.01', 'name': 'hypsometer'}, {'id': 8525, 'synset': 'hysterosalpingogram.n.01', 'name': 'hysterosalpingogram'}, {'id': 8526, 'synset': 'i-beam.n.01', 'name': 'I-beam'}, {'id': 8527, 'synset': 'ice_ax.n.01', 'name': 'ice_ax'}, {'id': 8528, 'synset': 'iceboat.n.02', 'name': 'iceboat'}, {'id': 8529, 'synset': 'icebreaker.n.01', 'name': 'icebreaker'}, {'id': 8530, 'synset': 'iced-tea_spoon.n.01', 'name': 'iced-tea_spoon'}, {'id': 8531, 'synset': 'ice_hockey_rink.n.01', 'name': 'ice_hockey_rink'}, {'id': 8532, 'synset': 'ice_machine.n.01', 'name': 'ice_machine'}, {'id': 8533, 'synset': 'icepick.n.01', 'name': 'icepick'}, {'id': 8534, 'synset': 'ice_rink.n.01', 'name': 'ice_rink'}, {'id': 8535, 'synset': 'ice_tongs.n.01', 'name': 'ice_tongs'}, {'id': 8536, 'synset': 'icetray.n.01', 'name': 'icetray'}, {'id': 8537, 'synset': 'iconoscope.n.01', 'name': 'iconoscope'}, {'id': 8538, 'synset': 'identikit.n.01', 'name': 'Identikit'}, {'id': 8539, 'synset': 'idle_pulley.n.01', 'name': 'idle_pulley'}, {'id': 8540, 'synset': 'igloo.n.01', 'name': 'igloo'}, {'id': 8541, 'synset': 'ignition_coil.n.01', 'name': 'ignition_coil'}, {'id': 8542, 'synset': 'ignition_key.n.01', 'name': 'ignition_key'}, {'id': 8543, 'synset': 'ignition_switch.n.01', 'name': 'ignition_switch'}, {'id': 8544, 'synset': 'imaret.n.01', 'name': 'imaret'}, {'id': 8545, 'synset': 'immovable_bandage.n.01', 'name': 'immovable_bandage'}, {'id': 8546, 'synset': 'impact_printer.n.01', 'name': 'impact_printer'}, {'id': 8547, 'synset': 'impeller.n.01', 'name': 'impeller'}, {'id': 8548, 'synset': 'implant.n.01', 'name': 'implant'}, {'id': 8549, 'synset': 'implement.n.01', 'name': 'implement'}, {'id': 8550, 'synset': 'impression.n.07', 'name': 'impression'}, {'id': 8551, 'synset': 'imprint.n.05', 'name': 'imprint'}, {'id': 8552, 'synset': 'improvised_explosive_device.n.01', 'name': 'improvised_explosive_device'}, {'id': 8553, 'synset': 'impulse_turbine.n.01', 'name': 'impulse_turbine'}, {'id': 8554, 'synset': 'in-basket.n.01', 'name': 'in-basket'}, {'id': 8555, 'synset': 'incendiary_bomb.n.01', 'name': 'incendiary_bomb'}, {'id': 8556, 'synset': 'incinerator.n.01', 'name': 'incinerator'}, {'id': 8557, 'synset': 'inclined_plane.n.01', 'name': 'inclined_plane'}, {'id': 8558, 'synset': 'inclinometer.n.02', 'name': 'inclinometer'}, {'id': 8559, 'synset': 'inclinometer.n.01', 'name': 'inclinometer'}, {'id': 8560, 'synset': 'incrustation.n.03', 'name': 'incrustation'}, {'id': 8561, 'synset': 'incubator.n.01', 'name': 'incubator'}, {'id': 8562, 'synset': 'index_register.n.01', 'name': 'index_register'}, {'id': 8563, 'synset': 'indiaman.n.01', 'name': 'Indiaman'}, {'id': 8564, 'synset': 'indian_club.n.01', 'name': 'Indian_club'}, {'id': 8565, 'synset': 'indicator.n.03', 'name': 'indicator'}, {'id': 8566, 'synset': 'induction_coil.n.01', 'name': 'induction_coil'}, {'id': 8567, 'synset': 'inductor.n.01', 'name': 'inductor'}, {'id': 8568, 'synset': 'industrial_watercourse.n.01', 'name': 'industrial_watercourse'}, {'id': 8569, 'synset': 'inertial_guidance_system.n.01', 'name': 'inertial_guidance_system'}, {'id': 8570, 'synset': 'inflater.n.01', 'name': 'inflater'}, {'id': 8571, 'synset': 'injector.n.01', 'name': 'injector'}, {'id': 8572, 'synset': 'ink_bottle.n.01', 'name': 'ink_bottle'}, {'id': 8573, 'synset': 'ink_eraser.n.01', 'name': 'ink_eraser'}, {'id': 8574, 'synset': 'ink-jet_printer.n.01', 'name': 'ink-jet_printer'}, {'id': 8575, 'synset': 'inkle.n.01', 'name': 'inkle'}, {'id': 8576, 'synset': 'inkstand.n.02', 'name': 'inkstand'}, {'id': 8577, 'synset': 'inkwell.n.01', 'name': 'inkwell'}, {'id': 8578, 'synset': 'inlay.n.01', 'name': 'inlay'}, {'id': 8579, 'synset': 'inside_caliper.n.01', 'name': 'inside_caliper'}, {'id': 8580, 'synset': 'insole.n.01', 'name': 'insole'}, {'id': 8581, 'synset': 'instep.n.02', 'name': 'instep'}, {'id': 8582, 'synset': 'instillator.n.01', 'name': 'instillator'}, {'id': 8583, 'synset': 'institution.n.02', 'name': 'institution'}, {'id': 8584, 'synset': 'instrument.n.01', 'name': 'instrument'}, {'id': 8585, 'synset': 'instrument_of_punishment.n.01', 'name': 'instrument_of_punishment'}, {'id': 8586, 'synset': 'instrument_of_torture.n.01', 'name': 'instrument_of_torture'}, {'id': 8587, 'synset': 'intaglio.n.02', 'name': 'intaglio'}, {'id': 8588, 'synset': 'intake_valve.n.01', 'name': 'intake_valve'}, {'id': 8589, 'synset': 'integrated_circuit.n.01', 'name': 'integrated_circuit'}, {'id': 8590, 'synset': 'integrator.n.01', 'name': 'integrator'}, {'id': 8591, 'synset': 'intelnet.n.01', 'name': 'Intelnet'}, {'id': 8592, 'synset': 'interceptor.n.01', 'name': 'interceptor'}, {'id': 8593, 'synset': 'interchange.n.01', 'name': 'interchange'}, {'id': 8594, 'synset': 'intercommunication_system.n.01', 'name': 'intercommunication_system'}, {'id': 8595, 'synset': 'intercontinental_ballistic_missile.n.01', 'name': 'intercontinental_ballistic_missile'}, {'id': 8596, 'synset': 'interface.n.04', 'name': 'interface'}, {'id': 8597, 'synset': 'interferometer.n.01', 'name': 'interferometer'}, {'id': 8598, 'synset': 'interior_door.n.01', 'name': 'interior_door'}, {'id': 8599, 'synset': 'internal-combustion_engine.n.01', 'name': 'internal-combustion_engine'}, {'id': 8600, 'synset': 'internal_drive.n.01', 'name': 'internal_drive'}, {'id': 8601, 'synset': 'internet.n.01', 'name': 'internet'}, {'id': 8602, 'synset': 'interphone.n.01', 'name': 'interphone'}, {'id': 8603, 'synset': 'interrupter.n.01', 'name': 'interrupter'}, {'id': 8604, 'synset': 'intersection.n.02', 'name': 'intersection'}, {'id': 8605, 'synset': 'interstice.n.02', 'name': 'interstice'}, {'id': 8606, 'synset': 'intraocular_lens.n.01', 'name': 'intraocular_lens'}, {'id': 8607, 'synset': 'intravenous_pyelogram.n.01', 'name': 'intravenous_pyelogram'}, {'id': 8608, 'synset': 'inverter.n.01', 'name': 'inverter'}, {'id': 8609, 'synset': 'ion_engine.n.01', 'name': 'ion_engine'}, {'id': 8610, 'synset': 'ionization_chamber.n.01', 'name': 'ionization_chamber'}, {'id': 8611, 'synset': 'video_ipod.n.01', 'name': 'video_iPod'}, {'id': 8612, 'synset': 'iron.n.02', 'name': 'iron'}, {'id': 8613, 'synset': 'iron.n.03', 'name': 'iron'}, {'id': 8614, 'synset': 'irons.n.01', 'name': 'irons'}, {'id': 8615, 'synset': 'ironclad.n.01', 'name': 'ironclad'}, {'id': 8616, 'synset': 'iron_foundry.n.01', 'name': 'iron_foundry'}, {'id': 8617, 'synset': 'iron_horse.n.01', 'name': 'iron_horse'}, {'id': 8618, 'synset': 'ironing.n.01', 'name': 'ironing'}, {'id': 8619, 'synset': 'iron_lung.n.01', 'name': 'iron_lung'}, {'id': 8620, 'synset': 'ironmongery.n.01', 'name': 'ironmongery'}, {'id': 8621, 'synset': 'ironworks.n.01', 'name': 'ironworks'}, {'id': 8622, 'synset': 'irrigation_ditch.n.01', 'name': 'irrigation_ditch'}, {'id': 8623, 'synset': 'izar.n.01', 'name': 'izar'}, {'id': 8624, 'synset': 'jabot.n.01', 'name': 'jabot'}, {'id': 8625, 'synset': 'jack.n.10', 'name': 'jack'}, {'id': 8626, 'synset': 'jack.n.07', 'name': 'jack'}, {'id': 8627, 'synset': 'jack.n.06', 'name': 'jack'}, {'id': 8628, 'synset': 'jack.n.05', 'name': 'jack'}, {'id': 8629, 'synset': 'jacket.n.02', 'name': 'jacket'}, {'id': 8630, 'synset': 'jacket.n.05', 'name': 'jacket'}, {'id': 8631, 'synset': 'jack-in-the-box.n.01', 'name': 'jack-in-the-box'}, {'id': 8632, 'synset': "jack-o'-lantern.n.02", 'name': "jack-o'-lantern"}, {'id': 8633, 'synset': 'jack_plane.n.01', 'name': 'jack_plane'}, {'id': 8634, 'synset': "jacob's_ladder.n.02", 'name': "Jacob's_ladder"}, {'id': 8635, 'synset': 'jaconet.n.01', 'name': 'jaconet'}, {'id': 8636, 'synset': 'jacquard_loom.n.01', 'name': 'Jacquard_loom'}, {'id': 8637, 'synset': 'jacquard.n.02', 'name': 'jacquard'}, {'id': 8638, 'synset': 'jag.n.03', 'name': 'jag'}, {'id': 8639, 'synset': 'jail.n.01', 'name': 'jail'}, {'id': 8640, 'synset': 'jalousie.n.02', 'name': 'jalousie'}, {'id': 8641, 'synset': 'jamb.n.01', 'name': 'jamb'}, {'id': 8642, 'synset': 'jammer.n.01', 'name': 'jammer'}, {'id': 8643, 'synset': 'jampot.n.01', 'name': 'jampot'}, {'id': 8644, 'synset': 'japan.n.04', 'name': 'japan'}, {'id': 8645, 'synset': 'jarvik_heart.n.01', 'name': 'Jarvik_heart'}, {'id': 8646, 'synset': 'jaunting_car.n.01', 'name': 'jaunting_car'}, {'id': 8647, 'synset': 'javelin.n.02', 'name': 'javelin'}, {'id': 8648, 'synset': 'jaw.n.03', 'name': 'jaw'}, {'id': 8649, 'synset': 'jaws_of_life.n.01', 'name': 'Jaws_of_Life'}, {'id': 8650, 'synset': 'jellaba.n.01', 'name': 'jellaba'}, {'id': 8651, 'synset': 'jerkin.n.01', 'name': 'jerkin'}, {'id': 8652, 'synset': 'jeroboam.n.02', 'name': 'jeroboam'}, {'id': 8653, 'synset': 'jersey.n.04', 'name': 'jersey'}, {'id': 8654, 'synset': 'jet_bridge.n.01', 'name': 'jet_bridge'}, {'id': 8655, 'synset': 'jet_engine.n.01', 'name': 'jet_engine'}, {'id': 8656, 'synset': 'jetliner.n.01', 'name': 'jetliner'}, {'id': 8657, 'synset': "jeweler's_glass.n.01", 'name': "jeweler's_glass"}, {'id': 8658, 'synset': 'jewelled_headdress.n.01', 'name': 'jewelled_headdress'}, {'id': 8659, 'synset': "jew's_harp.n.01", 'name': "jew's_harp"}, {'id': 8660, 'synset': 'jib.n.01', 'name': 'jib'}, {'id': 8661, 'synset': 'jibboom.n.01', 'name': 'jibboom'}, {'id': 8662, 'synset': 'jig.n.03', 'name': 'jig'}, {'id': 8663, 'synset': 'jig.n.02', 'name': 'jig'}, {'id': 8664, 'synset': 'jiggermast.n.01', 'name': 'jiggermast'}, {'id': 8665, 'synset': 'jigsaw.n.02', 'name': 'jigsaw'}, {'id': 8666, 'synset': 'jigsaw_puzzle.n.01', 'name': 'jigsaw_puzzle'}, {'id': 8667, 'synset': 'jinrikisha.n.01', 'name': 'jinrikisha'}, {'id': 8668, 'synset': 'jobcentre.n.01', 'name': 'jobcentre'}, {'id': 8669, 'synset': 'jodhpurs.n.01', 'name': 'jodhpurs'}, {'id': 8670, 'synset': 'jodhpur.n.01', 'name': 'jodhpur'}, {'id': 8671, 'synset': 'joinery.n.01', 'name': 'joinery'}, {'id': 8672, 'synset': 'joint.n.05', 'name': 'joint'}, {'id': 8673, 'synset': 'joint_direct_attack_munition.n.01', 'name': 'Joint_Direct_Attack_Munition'}, {'id': 8674, 'synset': 'jointer.n.01', 'name': 'jointer'}, {'id': 8675, 'synset': 'joist.n.01', 'name': 'joist'}, {'id': 8676, 'synset': 'jolly_boat.n.01', 'name': 'jolly_boat'}, {'id': 8677, 'synset': 'jorum.n.01', 'name': 'jorum'}, {'id': 8678, 'synset': 'joss_house.n.01', 'name': 'joss_house'}, {'id': 8679, 'synset': 'journal_bearing.n.01', 'name': 'journal_bearing'}, {'id': 8680, 'synset': 'journal_box.n.01', 'name': 'journal_box'}, {'id': 8681, 'synset': 'jungle_gym.n.01', 'name': 'jungle_gym'}, {'id': 8682, 'synset': 'junk.n.02', 'name': 'junk'}, {'id': 8683, 'synset': 'jug.n.01', 'name': 'jug'}, {'id': 8684, 'synset': 'jukebox.n.01', 'name': 'jukebox'}, {'id': 8685, 'synset': 'jumbojet.n.01', 'name': 'jumbojet'}, {'id': 8686, 'synset': 'jumper.n.07', 'name': 'jumper'}, {'id': 8687, 'synset': 'jumper.n.06', 'name': 'jumper'}, {'id': 8688, 'synset': 'jumper.n.05', 'name': 'jumper'}, {'id': 8689, 'synset': 'jumper.n.04', 'name': 'jumper'}, {'id': 8690, 'synset': 'jumper_cable.n.01', 'name': 'jumper_cable'}, {'id': 8691, 'synset': 'jump_seat.n.01', 'name': 'jump_seat'}, {'id': 8692, 'synset': 'jump_suit.n.02', 'name': 'jump_suit'}, {'id': 8693, 'synset': 'junction.n.01', 'name': 'junction'}, {'id': 8694, 'synset': 'junction.n.04', 'name': 'junction'}, {'id': 8695, 'synset': 'junction_barrier.n.01', 'name': 'junction_barrier'}, {'id': 8696, 'synset': 'junk_shop.n.01', 'name': 'junk_shop'}, {'id': 8697, 'synset': 'jury_box.n.01', 'name': 'jury_box'}, {'id': 8698, 'synset': 'jury_mast.n.01', 'name': 'jury_mast'}, {'id': 8699, 'synset': 'kachina.n.03', 'name': 'kachina'}, {'id': 8700, 'synset': 'kaffiyeh.n.01', 'name': 'kaffiyeh'}, {'id': 8701, 'synset': 'kalansuwa.n.01', 'name': 'kalansuwa'}, {'id': 8702, 'synset': 'kalashnikov.n.01', 'name': 'Kalashnikov'}, {'id': 8703, 'synset': 'kameez.n.01', 'name': 'kameez'}, {'id': 8704, 'synset': 'kanzu.n.01', 'name': 'kanzu'}, {'id': 8705, 'synset': 'katharometer.n.01', 'name': 'katharometer'}, {'id': 8706, 'synset': 'kazoo.n.01', 'name': 'kazoo'}, {'id': 8707, 'synset': 'keel.n.03', 'name': 'keel'}, {'id': 8708, 'synset': 'keelboat.n.01', 'name': 'keelboat'}, {'id': 8709, 'synset': 'keelson.n.01', 'name': 'keelson'}, {'id': 8710, 'synset': 'keep.n.02', 'name': 'keep'}, {'id': 8711, 'synset': 'kepi.n.01', 'name': 'kepi'}, {'id': 8712, 'synset': 'keratoscope.n.01', 'name': 'keratoscope'}, {'id': 8713, 'synset': 'kerchief.n.01', 'name': 'kerchief'}, {'id': 8714, 'synset': 'ketch.n.01', 'name': 'ketch'}, {'id': 8715, 'synset': 'kettle.n.04', 'name': 'kettle'}, {'id': 8716, 'synset': 'key.n.15', 'name': 'key'}, {'id': 8717, 'synset': 'keyboard.n.01', 'name': 'keyboard'}, {'id': 8718, 'synset': 'keyboard_buffer.n.01', 'name': 'keyboard_buffer'}, {'id': 8719, 'synset': 'keyboard_instrument.n.01', 'name': 'keyboard_instrument'}, {'id': 8720, 'synset': 'keyhole.n.01', 'name': 'keyhole'}, {'id': 8721, 'synset': 'keyhole_saw.n.01', 'name': 'keyhole_saw'}, {'id': 8722, 'synset': 'khadi.n.01', 'name': 'khadi'}, {'id': 8723, 'synset': 'khaki.n.01', 'name': 'khaki'}, {'id': 8724, 'synset': 'khakis.n.01', 'name': 'khakis'}, {'id': 8725, 'synset': 'khimar.n.01', 'name': 'khimar'}, {'id': 8726, 'synset': 'khukuri.n.01', 'name': 'khukuri'}, {'id': 8727, 'synset': 'kick_pleat.n.01', 'name': 'kick_pleat'}, {'id': 8728, 'synset': 'kicksorter.n.01', 'name': 'kicksorter'}, {'id': 8729, 'synset': 'kickstand.n.01', 'name': 'kickstand'}, {'id': 8730, 'synset': 'kick_starter.n.01', 'name': 'kick_starter'}, {'id': 8731, 'synset': 'kid_glove.n.01', 'name': 'kid_glove'}, {'id': 8732, 'synset': 'kiln.n.01', 'name': 'kiln'}, {'id': 8733, 'synset': 'kinescope.n.01', 'name': 'kinescope'}, {'id': 8734, 'synset': 'kinetoscope.n.01', 'name': 'Kinetoscope'}, {'id': 8735, 'synset': 'king.n.10', 'name': 'king'}, {'id': 8736, 'synset': 'king.n.08', 'name': 'king'}, {'id': 8737, 'synset': 'kingbolt.n.01', 'name': 'kingbolt'}, {'id': 8738, 'synset': 'king_post.n.01', 'name': 'king_post'}, {'id': 8739, 'synset': "kipp's_apparatus.n.01", 'name': "Kipp's_apparatus"}, {'id': 8740, 'synset': 'kirk.n.01', 'name': 'kirk'}, {'id': 8741, 'synset': 'kirpan.n.01', 'name': 'kirpan'}, {'id': 8742, 'synset': 'kirtle.n.02', 'name': 'kirtle'}, {'id': 8743, 'synset': 'kirtle.n.01', 'name': 'kirtle'}, {'id': 8744, 'synset': 'kit.n.02', 'name': 'kit'}, {'id': 8745, 'synset': 'kit.n.01', 'name': 'kit'}, {'id': 8746, 'synset': 'kitbag.n.01', 'name': 'kitbag'}, {'id': 8747, 'synset': 'kitchen.n.01', 'name': 'kitchen'}, {'id': 8748, 'synset': 'kitchen_appliance.n.01', 'name': 'kitchen_appliance'}, {'id': 8749, 'synset': 'kitchenette.n.01', 'name': 'kitchenette'}, {'id': 8750, 'synset': 'kitchen_utensil.n.01', 'name': 'kitchen_utensil'}, {'id': 8751, 'synset': 'kitchenware.n.01', 'name': 'kitchenware'}, {'id': 8752, 'synset': 'kite_balloon.n.01', 'name': 'kite_balloon'}, {'id': 8753, 'synset': 'klaxon.n.01', 'name': 'klaxon'}, {'id': 8754, 'synset': 'klieg_light.n.01', 'name': 'klieg_light'}, {'id': 8755, 'synset': 'klystron.n.01', 'name': 'klystron'}, {'id': 8756, 'synset': 'knee_brace.n.01', 'name': 'knee_brace'}, {'id': 8757, 'synset': 'knee-high.n.01', 'name': 'knee-high'}, {'id': 8758, 'synset': 'knee_piece.n.01', 'name': 'knee_piece'}, {'id': 8759, 'synset': 'knife.n.02', 'name': 'knife'}, {'id': 8760, 'synset': 'knife_blade.n.01', 'name': 'knife_blade'}, {'id': 8761, 'synset': 'knight.n.02', 'name': 'knight'}, {'id': 8762, 'synset': 'knit.n.01', 'name': 'knit'}, {'id': 8763, 'synset': 'knitting_machine.n.01', 'name': 'knitting_machine'}, {'id': 8764, 'synset': 'knitwear.n.01', 'name': 'knitwear'}, {'id': 8765, 'synset': 'knob.n.01', 'name': 'knob'}, {'id': 8766, 'synset': 'knob.n.04', 'name': 'knob'}, {'id': 8767, 'synset': 'knobble.n.01', 'name': 'knobble'}, {'id': 8768, 'synset': 'knobkerrie.n.01', 'name': 'knobkerrie'}, {'id': 8769, 'synset': 'knot.n.02', 'name': 'knot'}, {'id': 8770, 'synset': 'knuckle_joint.n.02', 'name': 'knuckle_joint'}, {'id': 8771, 'synset': 'kohl.n.01', 'name': 'kohl'}, {'id': 8772, 'synset': 'koto.n.01', 'name': 'koto'}, {'id': 8773, 'synset': 'kraal.n.02', 'name': 'kraal'}, {'id': 8774, 'synset': 'kremlin.n.02', 'name': 'kremlin'}, {'id': 8775, 'synset': 'kris.n.01', 'name': 'kris'}, {'id': 8776, 'synset': 'krummhorn.n.01', 'name': 'krummhorn'}, {'id': 8777, 'synset': "kundt's_tube.n.01", 'name': "Kundt's_tube"}, {'id': 8778, 'synset': 'kurdistan.n.02', 'name': 'Kurdistan'}, {'id': 8779, 'synset': 'kurta.n.01', 'name': 'kurta'}, {'id': 8780, 'synset': 'kylix.n.01', 'name': 'kylix'}, {'id': 8781, 'synset': 'kymograph.n.01', 'name': 'kymograph'}, {'id': 8782, 'synset': 'lab_bench.n.01', 'name': 'lab_bench'}, {'id': 8783, 'synset': 'lace.n.02', 'name': 'lace'}, {'id': 8784, 'synset': 'lacquer.n.02', 'name': 'lacquer'}, {'id': 8785, 'synset': 'lacquerware.n.01', 'name': 'lacquerware'}, {'id': 8786, 'synset': 'lacrosse_ball.n.01', 'name': 'lacrosse_ball'}, {'id': 8787, 'synset': 'ladder-back.n.02', 'name': 'ladder-back'}, {'id': 8788, 'synset': 'ladder-back.n.01', 'name': 'ladder-back'}, {'id': 8789, 'synset': 'ladder_truck.n.01', 'name': 'ladder_truck'}, {'id': 8790, 'synset': "ladies'_room.n.01", 'name': "ladies'_room"}, {'id': 8791, 'synset': 'lady_chapel.n.01', 'name': 'lady_chapel'}, {'id': 8792, 'synset': 'lagerphone.n.01', 'name': 'lagerphone'}, {'id': 8793, 'synset': 'lag_screw.n.01', 'name': 'lag_screw'}, {'id': 8794, 'synset': 'lake_dwelling.n.01', 'name': 'lake_dwelling'}, {'id': 8795, 'synset': 'lally.n.01', 'name': 'lally'}, {'id': 8796, 'synset': 'lamasery.n.01', 'name': 'lamasery'}, {'id': 8797, 'synset': 'lambrequin.n.02', 'name': 'lambrequin'}, {'id': 8798, 'synset': 'lame.n.02', 'name': 'lame'}, {'id': 8799, 'synset': 'laminar_flow_clean_room.n.01', 'name': 'laminar_flow_clean_room'}, {'id': 8800, 'synset': 'laminate.n.01', 'name': 'laminate'}, {'id': 8801, 'synset': 'lamination.n.01', 'name': 'lamination'}, {'id': 8802, 'synset': 'lamp.n.01', 'name': 'lamp'}, {'id': 8803, 'synset': 'lamp_house.n.01', 'name': 'lamp_house'}, {'id': 8804, 'synset': 'lanai.n.02', 'name': 'lanai'}, {'id': 8805, 'synset': 'lancet_arch.n.01', 'name': 'lancet_arch'}, {'id': 8806, 'synset': 'lancet_window.n.01', 'name': 'lancet_window'}, {'id': 8807, 'synset': 'landau.n.02', 'name': 'landau'}, {'id': 8808, 'synset': 'lander.n.02', 'name': 'lander'}, {'id': 8809, 'synset': 'landing_craft.n.01', 'name': 'landing_craft'}, {'id': 8810, 'synset': 'landing_flap.n.01', 'name': 'landing_flap'}, {'id': 8811, 'synset': 'landing_gear.n.01', 'name': 'landing_gear'}, {'id': 8812, 'synset': 'landing_net.n.01', 'name': 'landing_net'}, {'id': 8813, 'synset': 'landing_skid.n.01', 'name': 'landing_skid'}, {'id': 8814, 'synset': 'land_line.n.01', 'name': 'land_line'}, {'id': 8815, 'synset': 'land_mine.n.01', 'name': 'land_mine'}, {'id': 8816, 'synset': 'land_office.n.01', 'name': 'land_office'}, {'id': 8817, 'synset': 'lanolin.n.02', 'name': 'lanolin'}, {'id': 8818, 'synset': 'lanyard.n.01', 'name': 'lanyard'}, {'id': 8819, 'synset': 'lap.n.03', 'name': 'lap'}, {'id': 8820, 'synset': 'laparoscope.n.01', 'name': 'laparoscope'}, {'id': 8821, 'synset': 'lapboard.n.01', 'name': 'lapboard'}, {'id': 8822, 'synset': 'lapel.n.01', 'name': 'lapel'}, {'id': 8823, 'synset': 'lap_joint.n.01', 'name': 'lap_joint'}, {'id': 8824, 'synset': 'laryngoscope.n.01', 'name': 'laryngoscope'}, {'id': 8825, 'synset': 'laser.n.01', 'name': 'laser'}, {'id': 8826, 'synset': 'laser-guided_bomb.n.01', 'name': 'laser-guided_bomb'}, {'id': 8827, 'synset': 'laser_printer.n.01', 'name': 'laser_printer'}, {'id': 8828, 'synset': 'lash.n.02', 'name': 'lash'}, {'id': 8829, 'synset': 'lashing.n.02', 'name': 'lashing'}, {'id': 8830, 'synset': 'lasso.n.02', 'name': 'lasso'}, {'id': 8831, 'synset': 'latch.n.01', 'name': 'latch'}, {'id': 8832, 'synset': 'latchet.n.01', 'name': 'latchet'}, {'id': 8833, 'synset': 'latchkey.n.01', 'name': 'latchkey'}, {'id': 8834, 'synset': 'lateen.n.01', 'name': 'lateen'}, {'id': 8835, 'synset': 'latex_paint.n.01', 'name': 'latex_paint'}, {'id': 8836, 'synset': 'lath.n.01', 'name': 'lath'}, {'id': 8837, 'synset': 'lathe.n.01', 'name': 'lathe'}, {'id': 8838, 'synset': 'latrine.n.01', 'name': 'latrine'}, {'id': 8839, 'synset': 'lattice.n.03', 'name': 'lattice'}, {'id': 8840, 'synset': 'launch.n.01', 'name': 'launch'}, {'id': 8841, 'synset': 'launcher.n.01', 'name': 'launcher'}, {'id': 8842, 'synset': 'laundry.n.01', 'name': 'laundry'}, {'id': 8843, 'synset': 'laundry_cart.n.01', 'name': 'laundry_cart'}, {'id': 8844, 'synset': 'laundry_truck.n.01', 'name': 'laundry_truck'}, {'id': 8845, 'synset': 'lavalava.n.01', 'name': 'lavalava'}, {'id': 8846, 'synset': 'lavaliere.n.01', 'name': 'lavaliere'}, {'id': 8847, 'synset': 'laver.n.02', 'name': 'laver'}, {'id': 8848, 'synset': 'lawn_chair.n.01', 'name': 'lawn_chair'}, {'id': 8849, 'synset': 'lawn_furniture.n.01', 'name': 'lawn_furniture'}, {'id': 8850, 'synset': 'layette.n.01', 'name': 'layette'}, {'id': 8851, 'synset': 'lead-acid_battery.n.01', 'name': 'lead-acid_battery'}, {'id': 8852, 'synset': 'lead-in.n.02', 'name': 'lead-in'}, {'id': 8853, 'synset': 'leading_rein.n.01', 'name': 'leading_rein'}, {'id': 8854, 'synset': 'lead_pencil.n.01', 'name': 'lead_pencil'}, {'id': 8855, 'synset': 'leaf_spring.n.01', 'name': 'leaf_spring'}, {'id': 8856, 'synset': 'lean-to.n.01', 'name': 'lean-to'}, {'id': 8857, 'synset': 'lean-to_tent.n.01', 'name': 'lean-to_tent'}, {'id': 8858, 'synset': 'leash.n.01', 'name': 'leash'}, {'id': 8859, 'synset': 'leatherette.n.01', 'name': 'leatherette'}, {'id': 8860, 'synset': 'leather_strip.n.01', 'name': 'leather_strip'}, {'id': 8861, 'synset': 'leclanche_cell.n.01', 'name': 'Leclanche_cell'}, {'id': 8862, 'synset': 'lectern.n.01', 'name': 'lectern'}, {'id': 8863, 'synset': 'lecture_room.n.01', 'name': 'lecture_room'}, {'id': 8864, 'synset': 'lederhosen.n.01', 'name': 'lederhosen'}, {'id': 8865, 'synset': 'ledger_board.n.01', 'name': 'ledger_board'}, {'id': 8866, 'synset': 'leg.n.07', 'name': 'leg'}, {'id': 8867, 'synset': 'leg.n.03', 'name': 'leg'}, {'id': 8868, 'synset': 'leiden_jar.n.01', 'name': 'Leiden_jar'}, {'id': 8869, 'synset': 'leisure_wear.n.01', 'name': 'leisure_wear'}, {'id': 8870, 'synset': 'lens.n.01', 'name': 'lens'}, {'id': 8871, 'synset': 'lens.n.05', 'name': 'lens'}, {'id': 8872, 'synset': 'lens_cap.n.01', 'name': 'lens_cap'}, {'id': 8873, 'synset': 'lens_implant.n.01', 'name': 'lens_implant'}, {'id': 8874, 'synset': 'leotard.n.01', 'name': 'leotard'}, {'id': 8875, 'synset': 'letter_case.n.01', 'name': 'letter_case'}, {'id': 8876, 'synset': 'letter_opener.n.01', 'name': 'letter_opener'}, {'id': 8877, 'synset': 'levee.n.03', 'name': 'levee'}, {'id': 8878, 'synset': 'level.n.05', 'name': 'level'}, {'id': 8879, 'synset': 'lever.n.01', 'name': 'lever'}, {'id': 8880, 'synset': 'lever.n.03', 'name': 'lever'}, {'id': 8881, 'synset': 'lever.n.02', 'name': 'lever'}, {'id': 8882, 'synset': 'lever_lock.n.01', 'name': 'lever_lock'}, {'id': 8883, 'synset': "levi's.n.01", 'name': "Levi's"}, {'id': 8884, 'synset': 'liberty_ship.n.01', 'name': 'Liberty_ship'}, {'id': 8885, 'synset': 'library.n.01', 'name': 'library'}, {'id': 8886, 'synset': 'library.n.05', 'name': 'library'}, {'id': 8887, 'synset': 'lid.n.02', 'name': 'lid'}, {'id': 8888, 'synset': 'liebig_condenser.n.01', 'name': 'Liebig_condenser'}, {'id': 8889, 'synset': 'lie_detector.n.01', 'name': 'lie_detector'}, {'id': 8890, 'synset': 'lifeboat.n.01', 'name': 'lifeboat'}, {'id': 8891, 'synset': 'life_office.n.01', 'name': 'life_office'}, {'id': 8892, 'synset': 'life_preserver.n.01', 'name': 'life_preserver'}, {'id': 8893, 'synset': 'life-support_system.n.02', 'name': 'life-support_system'}, {'id': 8894, 'synset': 'life-support_system.n.01', 'name': 'life-support_system'}, {'id': 8895, 'synset': 'lifting_device.n.01', 'name': 'lifting_device'}, {'id': 8896, 'synset': 'lift_pump.n.01', 'name': 'lift_pump'}, {'id': 8897, 'synset': 'ligament.n.02', 'name': 'ligament'}, {'id': 8898, 'synset': 'ligature.n.03', 'name': 'ligature'}, {'id': 8899, 'synset': 'light.n.02', 'name': 'light'}, {'id': 8900, 'synset': 'light_arm.n.01', 'name': 'light_arm'}, {'id': 8901, 'synset': 'light_circuit.n.01', 'name': 'light_circuit'}, {'id': 8902, 'synset': 'light-emitting_diode.n.01', 'name': 'light-emitting_diode'}, {'id': 8903, 'synset': 'lighter.n.02', 'name': 'lighter'}, {'id': 8904, 'synset': 'lighter-than-air_craft.n.01', 'name': 'lighter-than-air_craft'}, {'id': 8905, 'synset': 'light_filter.n.01', 'name': 'light_filter'}, {'id': 8906, 'synset': 'lighting.n.02', 'name': 'lighting'}, {'id': 8907, 'synset': 'light_machine_gun.n.01', 'name': 'light_machine_gun'}, {'id': 8908, 'synset': 'light_meter.n.01', 'name': 'light_meter'}, {'id': 8909, 'synset': 'light_microscope.n.01', 'name': 'light_microscope'}, {'id': 8910, 'synset': 'light_pen.n.01', 'name': 'light_pen'}, {'id': 8911, 'synset': 'lightship.n.01', 'name': 'lightship'}, {'id': 8912, 'synset': 'lilo.n.01', 'name': 'Lilo'}, {'id': 8913, 'synset': 'limber.n.01', 'name': 'limber'}, {'id': 8914, 'synset': 'limekiln.n.01', 'name': 'limekiln'}, {'id': 8915, 'synset': 'limiter.n.01', 'name': 'limiter'}, {'id': 8916, 'synset': 'linear_accelerator.n.01', 'name': 'linear_accelerator'}, {'id': 8917, 'synset': 'linen.n.01', 'name': 'linen'}, {'id': 8918, 'synset': 'line_printer.n.01', 'name': 'line_printer'}, {'id': 8919, 'synset': 'liner.n.04', 'name': 'liner'}, {'id': 8920, 'synset': 'liner.n.03', 'name': 'liner'}, {'id': 8921, 'synset': 'lingerie.n.01', 'name': 'lingerie'}, {'id': 8922, 'synset': 'lining.n.01', 'name': 'lining'}, {'id': 8923, 'synset': 'link.n.09', 'name': 'link'}, {'id': 8924, 'synset': 'linkage.n.03', 'name': 'linkage'}, {'id': 8925, 'synset': 'link_trainer.n.01', 'name': 'Link_trainer'}, {'id': 8926, 'synset': 'linocut.n.02', 'name': 'linocut'}, {'id': 8927, 'synset': 'linoleum_knife.n.01', 'name': 'linoleum_knife'}, {'id': 8928, 'synset': 'linotype.n.01', 'name': 'Linotype'}, {'id': 8929, 'synset': 'linsey-woolsey.n.01', 'name': 'linsey-woolsey'}, {'id': 8930, 'synset': 'linstock.n.01', 'name': 'linstock'}, {'id': 8931, 'synset': 'lion-jaw_forceps.n.01', 'name': 'lion-jaw_forceps'}, {'id': 8932, 'synset': 'lip-gloss.n.01', 'name': 'lip-gloss'}, {'id': 8933, 'synset': 'lipstick.n.01', 'name': 'lipstick'}, {'id': 8934, 'synset': 'liqueur_glass.n.01', 'name': 'liqueur_glass'}, {'id': 8935, 'synset': 'liquid_crystal_display.n.01', 'name': 'liquid_crystal_display'}, {'id': 8936, 'synset': 'liquid_metal_reactor.n.01', 'name': 'liquid_metal_reactor'}, {'id': 8937, 'synset': 'lisle.n.01', 'name': 'lisle'}, {'id': 8938, 'synset': 'lister.n.03', 'name': 'lister'}, {'id': 8939, 'synset': 'litterbin.n.01', 'name': 'litterbin'}, {'id': 8940, 'synset': 'little_theater.n.01', 'name': 'little_theater'}, {'id': 8941, 'synset': 'live_axle.n.01', 'name': 'live_axle'}, {'id': 8942, 'synset': 'living_quarters.n.01', 'name': 'living_quarters'}, {'id': 8943, 'synset': 'living_room.n.01', 'name': 'living_room'}, {'id': 8944, 'synset': 'load.n.09', 'name': 'load'}, {'id': 8945, 'synset': 'loafer.n.02', 'name': 'Loafer'}, {'id': 8946, 'synset': 'loaner.n.02', 'name': 'loaner'}, {'id': 8947, 'synset': 'lobe.n.04', 'name': 'lobe'}, {'id': 8948, 'synset': 'lobster_pot.n.01', 'name': 'lobster_pot'}, {'id': 8949, 'synset': 'local.n.01', 'name': 'local'}, {'id': 8950, 'synset': 'local_area_network.n.01', 'name': 'local_area_network'}, {'id': 8951, 'synset': 'local_oscillator.n.01', 'name': 'local_oscillator'}, {'id': 8952, 'synset': 'lochaber_ax.n.01', 'name': 'Lochaber_ax'}, {'id': 8953, 'synset': 'lock.n.01', 'name': 'lock'}, {'id': 8954, 'synset': 'lock.n.05', 'name': 'lock'}, {'id': 8955, 'synset': 'lock.n.04', 'name': 'lock'}, {'id': 8956, 'synset': 'lock.n.03', 'name': 'lock'}, {'id': 8957, 'synset': 'lockage.n.02', 'name': 'lockage'}, {'id': 8958, 'synset': 'locker.n.02', 'name': 'locker'}, {'id': 8959, 'synset': 'locker_room.n.01', 'name': 'locker_room'}, {'id': 8960, 'synset': 'locket.n.01', 'name': 'locket'}, {'id': 8961, 'synset': 'lock-gate.n.01', 'name': 'lock-gate'}, {'id': 8962, 'synset': 'locking_pliers.n.01', 'name': 'locking_pliers'}, {'id': 8963, 'synset': 'lockring.n.01', 'name': 'lockring'}, {'id': 8964, 'synset': 'lockstitch.n.01', 'name': 'lockstitch'}, {'id': 8965, 'synset': 'lockup.n.01', 'name': 'lockup'}, {'id': 8966, 'synset': 'locomotive.n.01', 'name': 'locomotive'}, {'id': 8967, 'synset': 'lodge.n.05', 'name': 'lodge'}, {'id': 8968, 'synset': 'lodge.n.04', 'name': 'lodge'}, {'id': 8969, 'synset': 'lodge.n.03', 'name': 'lodge'}, {'id': 8970, 'synset': 'lodging_house.n.01', 'name': 'lodging_house'}, {'id': 8971, 'synset': 'loft.n.02', 'name': 'loft'}, {'id': 8972, 'synset': 'loft.n.04', 'name': 'loft'}, {'id': 8973, 'synset': 'loft.n.01', 'name': 'loft'}, {'id': 8974, 'synset': 'log_cabin.n.01', 'name': 'log_cabin'}, {'id': 8975, 'synset': 'loggia.n.01', 'name': 'loggia'}, {'id': 8976, 'synset': 'longbow.n.01', 'name': 'longbow'}, {'id': 8977, 'synset': 'long_iron.n.01', 'name': 'long_iron'}, {'id': 8978, 'synset': 'long_johns.n.01', 'name': 'long_johns'}, {'id': 8979, 'synset': 'long_sleeve.n.01', 'name': 'long_sleeve'}, {'id': 8980, 'synset': 'long_tom.n.01', 'name': 'long_tom'}, {'id': 8981, 'synset': 'long_trousers.n.01', 'name': 'long_trousers'}, {'id': 8982, 'synset': 'long_underwear.n.01', 'name': 'long_underwear'}, {'id': 8983, 'synset': 'looking_glass.n.01', 'name': 'looking_glass'}, {'id': 8984, 'synset': 'lookout.n.03', 'name': 'lookout'}, {'id': 8985, 'synset': 'loom.n.01', 'name': 'loom'}, {'id': 8986, 'synset': 'loop_knot.n.01', 'name': 'loop_knot'}, {'id': 8987, 'synset': 'lorgnette.n.01', 'name': 'lorgnette'}, {'id': 8988, 'synset': 'lorraine_cross.n.01', 'name': 'Lorraine_cross'}, {'id': 8989, 'synset': 'lorry.n.02', 'name': 'lorry'}, {'id': 8990, 'synset': 'lota.n.01', 'name': 'lota'}, {'id': 8991, 'synset': 'lotion.n.01', 'name': 'lotion'}, {'id': 8992, 'synset': 'lounge.n.02', 'name': 'lounge'}, {'id': 8993, 'synset': 'lounger.n.03', 'name': 'lounger'}, {'id': 8994, 'synset': 'lounging_jacket.n.01', 'name': 'lounging_jacket'}, {'id': 8995, 'synset': 'lounging_pajama.n.01', 'name': 'lounging_pajama'}, {'id': 8996, 'synset': 'loungewear.n.01', 'name': 'loungewear'}, {'id': 8997, 'synset': 'loupe.n.01', 'name': 'loupe'}, {'id': 8998, 'synset': 'louvered_window.n.01', 'name': 'louvered_window'}, {'id': 8999, 'synset': 'love_knot.n.01', 'name': 'love_knot'}, {'id': 9000, 'synset': 'loving_cup.n.01', 'name': 'loving_cup'}, {'id': 9001, 'synset': 'lowboy.n.01', 'name': 'lowboy'}, {'id': 9002, 'synset': 'low-pass_filter.n.01', 'name': 'low-pass_filter'}, {'id': 9003, 'synset': 'low-warp-loom.n.01', 'name': 'low-warp-loom'}, {'id': 9004, 'synset': 'lp.n.01', 'name': 'LP'}, {'id': 9005, 'synset': 'l-plate.n.01', 'name': 'L-plate'}, {'id': 9006, 'synset': "lubber's_hole.n.01", 'name': "lubber's_hole"}, {'id': 9007, 'synset': 'lubricating_system.n.01', 'name': 'lubricating_system'}, {'id': 9008, 'synset': 'luff.n.01', 'name': 'luff'}, {'id': 9009, 'synset': 'lug.n.03', 'name': 'lug'}, {'id': 9010, 'synset': 'luge.n.01', 'name': 'luge'}, {'id': 9011, 'synset': 'luger.n.01', 'name': 'Luger'}, {'id': 9012, 'synset': 'luggage_carrier.n.01', 'name': 'luggage_carrier'}, {'id': 9013, 'synset': 'luggage_compartment.n.01', 'name': 'luggage_compartment'}, {'id': 9014, 'synset': 'luggage_rack.n.01', 'name': 'luggage_rack'}, {'id': 9015, 'synset': 'lugger.n.01', 'name': 'lugger'}, {'id': 9016, 'synset': 'lugsail.n.01', 'name': 'lugsail'}, {'id': 9017, 'synset': 'lug_wrench.n.01', 'name': 'lug_wrench'}, {'id': 9018, 'synset': 'lumberjack.n.02', 'name': 'lumberjack'}, {'id': 9019, 'synset': 'lumbermill.n.01', 'name': 'lumbermill'}, {'id': 9020, 'synset': 'lunar_excursion_module.n.01', 'name': 'lunar_excursion_module'}, {'id': 9021, 'synset': 'lunchroom.n.01', 'name': 'lunchroom'}, {'id': 9022, 'synset': 'lunette.n.01', 'name': 'lunette'}, {'id': 9023, 'synset': 'lungi.n.01', 'name': 'lungi'}, {'id': 9024, 'synset': 'lunula.n.02', 'name': 'lunula'}, {'id': 9025, 'synset': 'lusterware.n.01', 'name': 'lusterware'}, {'id': 9026, 'synset': 'lute.n.02', 'name': 'lute'}, {'id': 9027, 'synset': 'luxury_liner.n.01', 'name': 'luxury_liner'}, {'id': 9028, 'synset': 'lyceum.n.02', 'name': 'lyceum'}, {'id': 9029, 'synset': 'lychgate.n.01', 'name': 'lychgate'}, {'id': 9030, 'synset': 'lyre.n.01', 'name': 'lyre'}, {'id': 9031, 'synset': 'machete.n.01', 'name': 'machete'}, {'id': 9032, 'synset': 'machicolation.n.01', 'name': 'machicolation'}, {'id': 9033, 'synset': 'machine.n.01', 'name': 'machine'}, {'id': 9034, 'synset': 'machine.n.04', 'name': 'machine'}, {'id': 9035, 'synset': 'machine_bolt.n.01', 'name': 'machine_bolt'}, {'id': 9036, 'synset': 'machinery.n.01', 'name': 'machinery'}, {'id': 9037, 'synset': 'machine_screw.n.01', 'name': 'machine_screw'}, {'id': 9038, 'synset': 'machine_tool.n.01', 'name': 'machine_tool'}, {'id': 9039, 'synset': "machinist's_vise.n.01", 'name': "machinist's_vise"}, {'id': 9040, 'synset': 'machmeter.n.01', 'name': 'machmeter'}, {'id': 9041, 'synset': 'mackinaw.n.04', 'name': 'mackinaw'}, {'id': 9042, 'synset': 'mackinaw.n.03', 'name': 'mackinaw'}, {'id': 9043, 'synset': 'mackinaw.n.01', 'name': 'mackinaw'}, {'id': 9044, 'synset': 'mackintosh.n.01', 'name': 'mackintosh'}, {'id': 9045, 'synset': 'macrame.n.01', 'name': 'macrame'}, {'id': 9046, 'synset': 'madras.n.03', 'name': 'madras'}, {'id': 9047, 'synset': 'mae_west.n.02', 'name': 'Mae_West'}, {'id': 9048, 'synset': 'magazine_rack.n.01', 'name': 'magazine_rack'}, {'id': 9049, 'synset': 'magic_lantern.n.01', 'name': 'magic_lantern'}, {'id': 9050, 'synset': 'magnetic_bottle.n.01', 'name': 'magnetic_bottle'}, {'id': 9051, 'synset': 'magnetic_compass.n.01', 'name': 'magnetic_compass'}, {'id': 9052, 'synset': 'magnetic_core_memory.n.01', 'name': 'magnetic_core_memory'}, {'id': 9053, 'synset': 'magnetic_disk.n.01', 'name': 'magnetic_disk'}, {'id': 9054, 'synset': 'magnetic_head.n.01', 'name': 'magnetic_head'}, {'id': 9055, 'synset': 'magnetic_mine.n.01', 'name': 'magnetic_mine'}, {'id': 9056, 'synset': 'magnetic_needle.n.01', 'name': 'magnetic_needle'}, {'id': 9057, 'synset': 'magnetic_recorder.n.01', 'name': 'magnetic_recorder'}, {'id': 9058, 'synset': 'magnetic_stripe.n.01', 'name': 'magnetic_stripe'}, {'id': 9059, 'synset': 'magnetic_tape.n.01', 'name': 'magnetic_tape'}, {'id': 9060, 'synset': 'magneto.n.01', 'name': 'magneto'}, {'id': 9061, 'synset': 'magnetometer.n.01', 'name': 'magnetometer'}, {'id': 9062, 'synset': 'magnetron.n.01', 'name': 'magnetron'}, {'id': 9063, 'synset': 'magnifier.n.01', 'name': 'magnifier'}, {'id': 9064, 'synset': 'magnum.n.01', 'name': 'magnum'}, {'id': 9065, 'synset': 'magnus_hitch.n.01', 'name': 'magnus_hitch'}, {'id': 9066, 'synset': 'mail.n.03', 'name': 'mail'}, {'id': 9067, 'synset': 'mailbag.n.02', 'name': 'mailbag'}, {'id': 9068, 'synset': 'mailbag.n.01', 'name': 'mailbag'}, {'id': 9069, 'synset': 'mailboat.n.01', 'name': 'mailboat'}, {'id': 9070, 'synset': 'mail_car.n.01', 'name': 'mail_car'}, {'id': 9071, 'synset': 'maildrop.n.01', 'name': 'maildrop'}, {'id': 9072, 'synset': 'mailer.n.04', 'name': 'mailer'}, {'id': 9073, 'synset': 'maillot.n.02', 'name': 'maillot'}, {'id': 9074, 'synset': 'maillot.n.01', 'name': 'maillot'}, {'id': 9075, 'synset': 'mailsorter.n.01', 'name': 'mailsorter'}, {'id': 9076, 'synset': 'mail_train.n.01', 'name': 'mail_train'}, {'id': 9077, 'synset': 'mainframe.n.01', 'name': 'mainframe'}, {'id': 9078, 'synset': 'mainmast.n.01', 'name': 'mainmast'}, {'id': 9079, 'synset': 'main_rotor.n.01', 'name': 'main_rotor'}, {'id': 9080, 'synset': 'mainsail.n.01', 'name': 'mainsail'}, {'id': 9081, 'synset': 'mainspring.n.01', 'name': 'mainspring'}, {'id': 9082, 'synset': 'main-topmast.n.01', 'name': 'main-topmast'}, {'id': 9083, 'synset': 'main-topsail.n.01', 'name': 'main-topsail'}, {'id': 9084, 'synset': 'main_yard.n.01', 'name': 'main_yard'}, {'id': 9085, 'synset': 'maisonette.n.02', 'name': 'maisonette'}, {'id': 9086, 'synset': 'majolica.n.01', 'name': 'majolica'}, {'id': 9087, 'synset': 'makeup.n.01', 'name': 'makeup'}, {'id': 9088, 'synset': 'maksutov_telescope.n.01', 'name': 'Maksutov_telescope'}, {'id': 9089, 'synset': 'malacca.n.02', 'name': 'malacca'}, {'id': 9090, 'synset': 'mallet.n.03', 'name': 'mallet'}, {'id': 9091, 'synset': 'mallet.n.02', 'name': 'mallet'}, {'id': 9092, 'synset': 'mammogram.n.01', 'name': 'mammogram'}, {'id': 9093, 'synset': 'mandola.n.01', 'name': 'mandola'}, {'id': 9094, 'synset': 'mandolin.n.01', 'name': 'mandolin'}, {'id': 9095, 'synset': 'mangle.n.01', 'name': 'mangle'}, {'id': 9096, 'synset': 'manhole_cover.n.01', 'name': 'manhole_cover'}, {'id': 9097, 'synset': 'man-of-war.n.01', 'name': 'man-of-war'}, {'id': 9098, 'synset': 'manometer.n.01', 'name': 'manometer'}, {'id': 9099, 'synset': 'manor.n.01', 'name': 'manor'}, {'id': 9100, 'synset': 'manor_hall.n.01', 'name': 'manor_hall'}, {'id': 9101, 'synset': 'manpad.n.01', 'name': 'MANPAD'}, {'id': 9102, 'synset': 'mansard.n.01', 'name': 'mansard'}, {'id': 9103, 'synset': 'manse.n.02', 'name': 'manse'}, {'id': 9104, 'synset': 'mansion.n.02', 'name': 'mansion'}, {'id': 9105, 'synset': 'mantel.n.01', 'name': 'mantel'}, {'id': 9106, 'synset': 'mantelet.n.02', 'name': 'mantelet'}, {'id': 9107, 'synset': 'mantilla.n.01', 'name': 'mantilla'}, {'id': 9108, 'synset': 'mao_jacket.n.01', 'name': 'Mao_jacket'}, {'id': 9109, 'synset': 'maquiladora.n.01', 'name': 'maquiladora'}, {'id': 9110, 'synset': 'maraca.n.01', 'name': 'maraca'}, {'id': 9111, 'synset': 'marble.n.02', 'name': 'marble'}, {'id': 9112, 'synset': 'marching_order.n.01', 'name': 'marching_order'}, {'id': 9113, 'synset': 'marimba.n.01', 'name': 'marimba'}, {'id': 9114, 'synset': 'marina.n.01', 'name': 'marina'}, {'id': 9115, 'synset': 'marketplace.n.02', 'name': 'marketplace'}, {'id': 9116, 'synset': 'marlinespike.n.01', 'name': 'marlinespike'}, {'id': 9117, 'synset': 'marocain.n.01', 'name': 'marocain'}, {'id': 9118, 'synset': 'marquee.n.02', 'name': 'marquee'}, {'id': 9119, 'synset': 'marquetry.n.01', 'name': 'marquetry'}, {'id': 9120, 'synset': 'marriage_bed.n.01', 'name': 'marriage_bed'}, {'id': 9121, 'synset': 'martello_tower.n.01', 'name': 'martello_tower'}, {'id': 9122, 'synset': 'martingale.n.01', 'name': 'martingale'}, {'id': 9123, 'synset': 'mascara.n.01', 'name': 'mascara'}, {'id': 9124, 'synset': 'maser.n.01', 'name': 'maser'}, {'id': 9125, 'synset': 'mashie.n.01', 'name': 'mashie'}, {'id': 9126, 'synset': 'mashie_niblick.n.01', 'name': 'mashie_niblick'}, {'id': 9127, 'synset': 'masjid.n.01', 'name': 'masjid'}, {'id': 9128, 'synset': 'mask.n.01', 'name': 'mask'}, {'id': 9129, 'synset': 'masonite.n.01', 'name': 'Masonite'}, {'id': 9130, 'synset': 'mason_jar.n.01', 'name': 'Mason_jar'}, {'id': 9131, 'synset': 'masonry.n.01', 'name': 'masonry'}, {'id': 9132, 'synset': "mason's_level.n.01", 'name': "mason's_level"}, {'id': 9133, 'synset': 'massage_parlor.n.02', 'name': 'massage_parlor'}, {'id': 9134, 'synset': 'massage_parlor.n.01', 'name': 'massage_parlor'}, {'id': 9135, 'synset': 'mass_spectrograph.n.01', 'name': 'mass_spectrograph'}, {'id': 9136, 'synset': 'mass_spectrometer.n.01', 'name': 'mass_spectrometer'}, {'id': 9137, 'synset': 'mast.n.04', 'name': 'mast'}, {'id': 9138, 'synset': 'mastaba.n.01', 'name': 'mastaba'}, {'id': 9139, 'synset': 'master_bedroom.n.01', 'name': 'master_bedroom'}, {'id': 9140, 'synset': 'masterpiece.n.01', 'name': 'masterpiece'}, {'id': 9141, 'synset': 'mat.n.01', 'name': 'mat'}, {'id': 9142, 'synset': 'match.n.01', 'name': 'match'}, {'id': 9143, 'synset': 'match.n.03', 'name': 'match'}, {'id': 9144, 'synset': 'matchboard.n.01', 'name': 'matchboard'}, {'id': 9145, 'synset': 'matchbook.n.01', 'name': 'matchbook'}, {'id': 9146, 'synset': 'matchlock.n.01', 'name': 'matchlock'}, {'id': 9147, 'synset': 'match_plane.n.01', 'name': 'match_plane'}, {'id': 9148, 'synset': 'matchstick.n.01', 'name': 'matchstick'}, {'id': 9149, 'synset': 'material.n.04', 'name': 'material'}, {'id': 9150, 'synset': 'materiel.n.01', 'name': 'materiel'}, {'id': 9151, 'synset': 'maternity_hospital.n.01', 'name': 'maternity_hospital'}, {'id': 9152, 'synset': 'maternity_ward.n.01', 'name': 'maternity_ward'}, {'id': 9153, 'synset': 'matrix.n.06', 'name': 'matrix'}, {'id': 9154, 'synset': 'matthew_walker.n.01', 'name': 'Matthew_Walker'}, {'id': 9155, 'synset': 'matting.n.01', 'name': 'matting'}, {'id': 9156, 'synset': 'mattock.n.01', 'name': 'mattock'}, {'id': 9157, 'synset': 'mattress_cover.n.01', 'name': 'mattress_cover'}, {'id': 9158, 'synset': 'maul.n.01', 'name': 'maul'}, {'id': 9159, 'synset': 'maulstick.n.01', 'name': 'maulstick'}, {'id': 9160, 'synset': 'mauser.n.02', 'name': 'Mauser'}, {'id': 9161, 'synset': 'mausoleum.n.01', 'name': 'mausoleum'}, {'id': 9162, 'synset': 'maxi.n.01', 'name': 'maxi'}, {'id': 9163, 'synset': 'maxim_gun.n.01', 'name': 'Maxim_gun'}, {'id': 9164, 'synset': 'maximum_and_minimum_thermometer.n.01', 'name': 'maximum_and_minimum_thermometer'}, {'id': 9165, 'synset': 'maypole.n.01', 'name': 'maypole'}, {'id': 9166, 'synset': 'maze.n.01', 'name': 'maze'}, {'id': 9167, 'synset': 'mazer.n.01', 'name': 'mazer'}, {'id': 9168, 'synset': 'means.n.02', 'name': 'means'}, {'id': 9169, 'synset': 'measure.n.09', 'name': 'measure'}, {'id': 9170, 'synset': 'measuring_instrument.n.01', 'name': 'measuring_instrument'}, {'id': 9171, 'synset': 'meat_counter.n.01', 'name': 'meat_counter'}, {'id': 9172, 'synset': 'meat_grinder.n.01', 'name': 'meat_grinder'}, {'id': 9173, 'synset': 'meat_hook.n.01', 'name': 'meat_hook'}, {'id': 9174, 'synset': 'meat_house.n.02', 'name': 'meat_house'}, {'id': 9175, 'synset': 'meat_safe.n.01', 'name': 'meat_safe'}, {'id': 9176, 'synset': 'meat_thermometer.n.01', 'name': 'meat_thermometer'}, {'id': 9177, 'synset': 'mechanical_device.n.01', 'name': 'mechanical_device'}, {'id': 9178, 'synset': 'mechanical_piano.n.01', 'name': 'mechanical_piano'}, {'id': 9179, 'synset': 'mechanical_system.n.01', 'name': 'mechanical_system'}, {'id': 9180, 'synset': 'mechanism.n.05', 'name': 'mechanism'}, {'id': 9181, 'synset': 'medical_building.n.01', 'name': 'medical_building'}, {'id': 9182, 'synset': 'medical_instrument.n.01', 'name': 'medical_instrument'}, {'id': 9183, 'synset': 'medicine_ball.n.01', 'name': 'medicine_ball'}, {'id': 9184, 'synset': 'medicine_chest.n.01', 'name': 'medicine_chest'}, {'id': 9185, 'synset': 'medline.n.01', 'name': 'MEDLINE'}, {'id': 9186, 'synset': 'megalith.n.01', 'name': 'megalith'}, {'id': 9187, 'synset': 'megaphone.n.01', 'name': 'megaphone'}, {'id': 9188, 'synset': 'memorial.n.03', 'name': 'memorial'}, {'id': 9189, 'synset': 'memory.n.04', 'name': 'memory'}, {'id': 9190, 'synset': 'memory_chip.n.01', 'name': 'memory_chip'}, {'id': 9191, 'synset': 'memory_device.n.01', 'name': 'memory_device'}, {'id': 9192, 'synset': 'menagerie.n.02', 'name': 'menagerie'}, {'id': 9193, 'synset': 'mending.n.01', 'name': 'mending'}, {'id': 9194, 'synset': 'menhir.n.01', 'name': 'menhir'}, {'id': 9195, 'synset': 'menorah.n.02', 'name': 'menorah'}, {'id': 9196, 'synset': 'menorah.n.01', 'name': 'Menorah'}, {'id': 9197, 'synset': "man's_clothing.n.01", 'name': "man's_clothing"}, {'id': 9198, 'synset': "men's_room.n.01", 'name': "men's_room"}, {'id': 9199, 'synset': 'mercantile_establishment.n.01', 'name': 'mercantile_establishment'}, {'id': 9200, 'synset': 'mercury_barometer.n.01', 'name': 'mercury_barometer'}, {'id': 9201, 'synset': 'mercury_cell.n.01', 'name': 'mercury_cell'}, {'id': 9202, 'synset': 'mercury_thermometer.n.01', 'name': 'mercury_thermometer'}, {'id': 9203, 'synset': 'mercury-vapor_lamp.n.01', 'name': 'mercury-vapor_lamp'}, {'id': 9204, 'synset': 'mercy_seat.n.02', 'name': 'mercy_seat'}, {'id': 9205, 'synset': 'merlon.n.01', 'name': 'merlon'}, {'id': 9206, 'synset': 'mess.n.05', 'name': 'mess'}, {'id': 9207, 'synset': 'mess_jacket.n.01', 'name': 'mess_jacket'}, {'id': 9208, 'synset': 'mess_kit.n.01', 'name': 'mess_kit'}, {'id': 9209, 'synset': 'messuage.n.01', 'name': 'messuage'}, {'id': 9210, 'synset': 'metal_detector.n.01', 'name': 'metal_detector'}, {'id': 9211, 'synset': 'metallic.n.01', 'name': 'metallic'}, {'id': 9212, 'synset': 'metal_screw.n.01', 'name': 'metal_screw'}, {'id': 9213, 'synset': 'metal_wood.n.01', 'name': 'metal_wood'}, {'id': 9214, 'synset': 'meteorological_balloon.n.01', 'name': 'meteorological_balloon'}, {'id': 9215, 'synset': 'meter.n.02', 'name': 'meter'}, {'id': 9216, 'synset': 'meterstick.n.01', 'name': 'meterstick'}, {'id': 9217, 'synset': 'metronome.n.01', 'name': 'metronome'}, {'id': 9218, 'synset': 'mezzanine.n.02', 'name': 'mezzanine'}, {'id': 9219, 'synset': 'mezzanine.n.01', 'name': 'mezzanine'}, {'id': 9220, 'synset': 'microbalance.n.01', 'name': 'microbalance'}, {'id': 9221, 'synset': 'microbrewery.n.01', 'name': 'microbrewery'}, {'id': 9222, 'synset': 'microfiche.n.01', 'name': 'microfiche'}, {'id': 9223, 'synset': 'microfilm.n.01', 'name': 'microfilm'}, {'id': 9224, 'synset': 'micrometer.n.02', 'name': 'micrometer'}, {'id': 9225, 'synset': 'microprocessor.n.01', 'name': 'microprocessor'}, {'id': 9226, 'synset': 'microtome.n.01', 'name': 'microtome'}, {'id': 9227, 'synset': 'microwave_diathermy_machine.n.01', 'name': 'microwave_diathermy_machine'}, {'id': 9228, 'synset': 'microwave_linear_accelerator.n.01', 'name': 'microwave_linear_accelerator'}, {'id': 9229, 'synset': 'middy.n.01', 'name': 'middy'}, {'id': 9230, 'synset': 'midiron.n.01', 'name': 'midiron'}, {'id': 9231, 'synset': 'mihrab.n.02', 'name': 'mihrab'}, {'id': 9232, 'synset': 'mihrab.n.01', 'name': 'mihrab'}, {'id': 9233, 'synset': 'military_hospital.n.01', 'name': 'military_hospital'}, {'id': 9234, 'synset': 'military_quarters.n.01', 'name': 'military_quarters'}, {'id': 9235, 'synset': 'military_uniform.n.01', 'name': 'military_uniform'}, {'id': 9236, 'synset': 'military_vehicle.n.01', 'name': 'military_vehicle'}, {'id': 9237, 'synset': 'milk_bar.n.01', 'name': 'milk_bar'}, {'id': 9238, 'synset': 'milk_float.n.01', 'name': 'milk_float'}, {'id': 9239, 'synset': 'milking_machine.n.01', 'name': 'milking_machine'}, {'id': 9240, 'synset': 'milking_stool.n.01', 'name': 'milking_stool'}, {'id': 9241, 'synset': 'milk_wagon.n.01', 'name': 'milk_wagon'}, {'id': 9242, 'synset': 'mill.n.04', 'name': 'mill'}, {'id': 9243, 'synset': 'milldam.n.01', 'name': 'milldam'}, {'id': 9244, 'synset': 'miller.n.05', 'name': 'miller'}, {'id': 9245, 'synset': 'milliammeter.n.01', 'name': 'milliammeter'}, {'id': 9246, 'synset': 'millinery.n.02', 'name': 'millinery'}, {'id': 9247, 'synset': 'millinery.n.01', 'name': 'millinery'}, {'id': 9248, 'synset': 'milling.n.01', 'name': 'milling'}, {'id': 9249, 'synset': 'millivoltmeter.n.01', 'name': 'millivoltmeter'}, {'id': 9250, 'synset': 'millstone.n.03', 'name': 'millstone'}, {'id': 9251, 'synset': 'millstone.n.02', 'name': 'millstone'}, {'id': 9252, 'synset': 'millwheel.n.01', 'name': 'millwheel'}, {'id': 9253, 'synset': 'mimeograph.n.01', 'name': 'mimeograph'}, {'id': 9254, 'synset': 'minaret.n.01', 'name': 'minaret'}, {'id': 9255, 'synset': 'mincer.n.01', 'name': 'mincer'}, {'id': 9256, 'synset': 'mine.n.02', 'name': 'mine'}, {'id': 9257, 'synset': 'mine_detector.n.01', 'name': 'mine_detector'}, {'id': 9258, 'synset': 'minelayer.n.01', 'name': 'minelayer'}, {'id': 9259, 'synset': 'mineshaft.n.01', 'name': 'mineshaft'}, {'id': 9260, 'synset': 'minibar.n.01', 'name': 'minibar'}, {'id': 9261, 'synset': 'minibike.n.01', 'name': 'minibike'}, {'id': 9262, 'synset': 'minibus.n.01', 'name': 'minibus'}, {'id': 9263, 'synset': 'minicar.n.01', 'name': 'minicar'}, {'id': 9264, 'synset': 'minicomputer.n.01', 'name': 'minicomputer'}, {'id': 9265, 'synset': 'ministry.n.02', 'name': 'ministry'}, {'id': 9266, 'synset': 'miniskirt.n.01', 'name': 'miniskirt'}, {'id': 9267, 'synset': 'minisub.n.01', 'name': 'minisub'}, {'id': 9268, 'synset': 'miniver.n.01', 'name': 'miniver'}, {'id': 9269, 'synset': 'mink.n.02', 'name': 'mink'}, {'id': 9270, 'synset': 'minster.n.01', 'name': 'minster'}, {'id': 9271, 'synset': 'mint.n.06', 'name': 'mint'}, {'id': 9272, 'synset': 'minute_hand.n.01', 'name': 'minute_hand'}, {'id': 9273, 'synset': 'minuteman.n.02', 'name': 'Minuteman'}, {'id': 9274, 'synset': 'missile.n.01', 'name': 'missile'}, {'id': 9275, 'synset': 'missile_defense_system.n.01', 'name': 'missile_defense_system'}, {'id': 9276, 'synset': 'miter_box.n.01', 'name': 'miter_box'}, {'id': 9277, 'synset': 'miter_joint.n.01', 'name': 'miter_joint'}, {'id': 9278, 'synset': 'mixer.n.03', 'name': 'mixer'}, {'id': 9279, 'synset': 'mixing_bowl.n.01', 'name': 'mixing_bowl'}, {'id': 9280, 'synset': 'mixing_faucet.n.01', 'name': 'mixing_faucet'}, {'id': 9281, 'synset': 'mizzen.n.02', 'name': 'mizzen'}, {'id': 9282, 'synset': 'mizzenmast.n.01', 'name': 'mizzenmast'}, {'id': 9283, 'synset': 'mobcap.n.01', 'name': 'mobcap'}, {'id': 9284, 'synset': 'mobile_home.n.01', 'name': 'mobile_home'}, {'id': 9285, 'synset': 'moccasin.n.01', 'name': 'moccasin'}, {'id': 9286, 'synset': 'mock-up.n.01', 'name': 'mock-up'}, {'id': 9287, 'synset': 'mod_con.n.01', 'name': 'mod_con'}, {'id': 9288, 'synset': 'model_t.n.01', 'name': 'Model_T'}, {'id': 9289, 'synset': 'modem.n.01', 'name': 'modem'}, {'id': 9290, 'synset': 'modillion.n.01', 'name': 'modillion'}, {'id': 9291, 'synset': 'module.n.03', 'name': 'module'}, {'id': 9292, 'synset': 'module.n.02', 'name': 'module'}, {'id': 9293, 'synset': 'mohair.n.01', 'name': 'mohair'}, {'id': 9294, 'synset': 'moire.n.01', 'name': 'moire'}, {'id': 9295, 'synset': 'mold.n.02', 'name': 'mold'}, {'id': 9296, 'synset': 'moldboard.n.01', 'name': 'moldboard'}, {'id': 9297, 'synset': 'moldboard_plow.n.01', 'name': 'moldboard_plow'}, {'id': 9298, 'synset': 'moleskin.n.01', 'name': 'moleskin'}, {'id': 9299, 'synset': 'molotov_cocktail.n.01', 'name': 'Molotov_cocktail'}, {'id': 9300, 'synset': 'monastery.n.01', 'name': 'monastery'}, {'id': 9301, 'synset': 'monastic_habit.n.01', 'name': 'monastic_habit'}, {'id': 9302, 'synset': 'moneybag.n.01', 'name': 'moneybag'}, {'id': 9303, 'synset': 'money_belt.n.01', 'name': 'money_belt'}, {'id': 9304, 'synset': 'monitor.n.06', 'name': 'monitor'}, {'id': 9305, 'synset': 'monitor.n.05', 'name': 'monitor'}, {'id': 9306, 'synset': 'monkey-wrench.n.01', 'name': 'monkey-wrench'}, {'id': 9307, 'synset': "monk's_cloth.n.01", 'name': "monk's_cloth"}, {'id': 9308, 'synset': 'monochrome.n.01', 'name': 'monochrome'}, {'id': 9309, 'synset': 'monocle.n.01', 'name': 'monocle'}, {'id': 9310, 'synset': 'monofocal_lens_implant.n.01', 'name': 'monofocal_lens_implant'}, {'id': 9311, 'synset': 'monoplane.n.01', 'name': 'monoplane'}, {'id': 9312, 'synset': 'monotype.n.02', 'name': 'monotype'}, {'id': 9313, 'synset': 'monstrance.n.02', 'name': 'monstrance'}, {'id': 9314, 'synset': 'mooring_tower.n.01', 'name': 'mooring_tower'}, {'id': 9315, 'synset': 'moorish_arch.n.01', 'name': 'Moorish_arch'}, {'id': 9316, 'synset': 'moped.n.01', 'name': 'moped'}, {'id': 9317, 'synset': 'mop_handle.n.01', 'name': 'mop_handle'}, {'id': 9318, 'synset': 'moquette.n.01', 'name': 'moquette'}, {'id': 9319, 'synset': 'morgue.n.01', 'name': 'morgue'}, {'id': 9320, 'synset': 'morion.n.01', 'name': 'morion'}, {'id': 9321, 'synset': 'morning_dress.n.02', 'name': 'morning_dress'}, {'id': 9322, 'synset': 'morning_dress.n.01', 'name': 'morning_dress'}, {'id': 9323, 'synset': 'morning_room.n.01', 'name': 'morning_room'}, {'id': 9324, 'synset': 'morris_chair.n.01', 'name': 'Morris_chair'}, {'id': 9325, 'synset': 'mortar.n.01', 'name': 'mortar'}, {'id': 9326, 'synset': 'mortar.n.03', 'name': 'mortar'}, {'id': 9327, 'synset': 'mortarboard.n.02', 'name': 'mortarboard'}, {'id': 9328, 'synset': 'mortise_joint.n.02', 'name': 'mortise_joint'}, {'id': 9329, 'synset': 'mosaic.n.05', 'name': 'mosaic'}, {'id': 9330, 'synset': 'mosque.n.01', 'name': 'mosque'}, {'id': 9331, 'synset': 'mosquito_net.n.01', 'name': 'mosquito_net'}, {'id': 9332, 'synset': 'motel.n.01', 'name': 'motel'}, {'id': 9333, 'synset': 'motel_room.n.01', 'name': 'motel_room'}, {'id': 9334, 'synset': 'mother_hubbard.n.01', 'name': 'Mother_Hubbard'}, {'id': 9335, 'synset': 'motion-picture_camera.n.01', 'name': 'motion-picture_camera'}, {'id': 9336, 'synset': 'motion-picture_film.n.01', 'name': 'motion-picture_film'}, {'id': 9337, 'synset': 'motley.n.03', 'name': 'motley'}, {'id': 9338, 'synset': 'motley.n.02', 'name': 'motley'}, {'id': 9339, 'synset': 'motorboat.n.01', 'name': 'motorboat'}, {'id': 9340, 'synset': 'motor_hotel.n.01', 'name': 'motor_hotel'}, {'id': 9341, 'synset': 'motorized_wheelchair.n.01', 'name': 'motorized_wheelchair'}, {'id': 9342, 'synset': 'mound.n.04', 'name': 'mound'}, {'id': 9343, 'synset': 'mount.n.04', 'name': 'mount'}, {'id': 9344, 'synset': 'mountain_bike.n.01', 'name': 'mountain_bike'}, {'id': 9345, 'synset': 'mountain_tent.n.01', 'name': 'mountain_tent'}, {'id': 9346, 'synset': 'mouse_button.n.01', 'name': 'mouse_button'}, {'id': 9347, 'synset': 'mousetrap.n.01', 'name': 'mousetrap'}, {'id': 9348, 'synset': 'mousse.n.03', 'name': 'mousse'}, {'id': 9349, 'synset': 'mouthpiece.n.06', 'name': 'mouthpiece'}, {'id': 9350, 'synset': 'mouthpiece.n.02', 'name': 'mouthpiece'}, {'id': 9351, 'synset': 'mouthpiece.n.04', 'name': 'mouthpiece'}, {'id': 9352, 'synset': 'movement.n.10', 'name': 'movement'}, {'id': 9353, 'synset': 'movie_projector.n.01', 'name': 'movie_projector'}, {'id': 9354, 'synset': 'moving-coil_galvanometer.n.01', 'name': 'moving-coil_galvanometer'}, {'id': 9355, 'synset': 'moving_van.n.01', 'name': 'moving_van'}, {'id': 9356, 'synset': 'mud_brick.n.01', 'name': 'mud_brick'}, {'id': 9357, 'synset': 'mudguard.n.01', 'name': 'mudguard'}, {'id': 9358, 'synset': 'mudhif.n.01', 'name': 'mudhif'}, {'id': 9359, 'synset': 'muff.n.01', 'name': 'muff'}, {'id': 9360, 'synset': 'muffle.n.01', 'name': 'muffle'}, {'id': 9361, 'synset': 'muffler.n.02', 'name': 'muffler'}, {'id': 9362, 'synset': 'mufti.n.02', 'name': 'mufti'}, {'id': 9363, 'synset': 'mulch.n.01', 'name': 'mulch'}, {'id': 9364, 'synset': 'mule.n.02', 'name': 'mule'}, {'id': 9365, 'synset': 'multichannel_recorder.n.01', 'name': 'multichannel_recorder'}, {'id': 9366, 'synset': 'multiengine_airplane.n.01', 'name': 'multiengine_airplane'}, {'id': 9367, 'synset': 'multiplex.n.02', 'name': 'multiplex'}, {'id': 9368, 'synset': 'multiplexer.n.01', 'name': 'multiplexer'}, {'id': 9369, 'synset': 'multiprocessor.n.01', 'name': 'multiprocessor'}, {'id': 9370, 'synset': 'multistage_rocket.n.01', 'name': 'multistage_rocket'}, {'id': 9371, 'synset': 'munition.n.02', 'name': 'munition'}, {'id': 9372, 'synset': 'murphy_bed.n.01', 'name': 'Murphy_bed'}, {'id': 9373, 'synset': 'musette.n.01', 'name': 'musette'}, {'id': 9374, 'synset': 'musette_pipe.n.01', 'name': 'musette_pipe'}, {'id': 9375, 'synset': 'museum.n.01', 'name': 'museum'}, {'id': 9376, 'synset': 'mushroom_anchor.n.01', 'name': 'mushroom_anchor'}, {'id': 9377, 'synset': 'music_box.n.01', 'name': 'music_box'}, {'id': 9378, 'synset': 'music_hall.n.01', 'name': 'music_hall'}, {'id': 9379, 'synset': 'music_school.n.02', 'name': 'music_school'}, {'id': 9380, 'synset': 'music_stand.n.01', 'name': 'music_stand'}, {'id': 9381, 'synset': 'musket.n.01', 'name': 'musket'}, {'id': 9382, 'synset': 'musket_ball.n.01', 'name': 'musket_ball'}, {'id': 9383, 'synset': 'muslin.n.01', 'name': 'muslin'}, {'id': 9384, 'synset': 'mustache_cup.n.01', 'name': 'mustache_cup'}, {'id': 9385, 'synset': 'mustard_plaster.n.01', 'name': 'mustard_plaster'}, {'id': 9386, 'synset': 'mute.n.02', 'name': 'mute'}, {'id': 9387, 'synset': 'muzzle_loader.n.01', 'name': 'muzzle_loader'}, {'id': 9388, 'synset': 'muzzle.n.03', 'name': 'muzzle'}, {'id': 9389, 'synset': 'myelogram.n.01', 'name': 'myelogram'}, {'id': 9390, 'synset': 'nacelle.n.01', 'name': 'nacelle'}, {'id': 9391, 'synset': 'nail.n.02', 'name': 'nail'}, {'id': 9392, 'synset': 'nailbrush.n.01', 'name': 'nailbrush'}, {'id': 9393, 'synset': 'nailhead.n.02', 'name': 'nailhead'}, {'id': 9394, 'synset': 'nailhead.n.01', 'name': 'nailhead'}, {'id': 9395, 'synset': 'nail_polish.n.01', 'name': 'nail_polish'}, {'id': 9396, 'synset': 'nainsook.n.01', 'name': 'nainsook'}, {'id': 9397, 'synset': "napier's_bones.n.01", 'name': "Napier's_bones"}, {'id': 9398, 'synset': 'nard.n.01', 'name': 'nard'}, {'id': 9399, 'synset': 'narrowbody_aircraft.n.01', 'name': 'narrowbody_aircraft'}, {'id': 9400, 'synset': 'narrow_wale.n.01', 'name': 'narrow_wale'}, {'id': 9401, 'synset': 'narthex.n.02', 'name': 'narthex'}, {'id': 9402, 'synset': 'narthex.n.01', 'name': 'narthex'}, {'id': 9403, 'synset': 'nasotracheal_tube.n.01', 'name': 'nasotracheal_tube'}, {'id': 9404, 'synset': 'national_monument.n.01', 'name': 'national_monument'}, {'id': 9405, 'synset': 'nautilus.n.01', 'name': 'nautilus'}, {'id': 9406, 'synset': 'navigational_system.n.01', 'name': 'navigational_system'}, {'id': 9407, 'synset': 'naval_equipment.n.01', 'name': 'naval_equipment'}, {'id': 9408, 'synset': 'naval_gun.n.01', 'name': 'naval_gun'}, {'id': 9409, 'synset': 'naval_missile.n.01', 'name': 'naval_missile'}, {'id': 9410, 'synset': 'naval_radar.n.01', 'name': 'naval_radar'}, {'id': 9411, 'synset': 'naval_tactical_data_system.n.01', 'name': 'naval_tactical_data_system'}, {'id': 9412, 'synset': 'naval_weaponry.n.01', 'name': 'naval_weaponry'}, {'id': 9413, 'synset': 'nave.n.01', 'name': 'nave'}, {'id': 9414, 'synset': 'navigational_instrument.n.01', 'name': 'navigational_instrument'}, {'id': 9415, 'synset': 'nebuchadnezzar.n.02', 'name': 'nebuchadnezzar'}, {'id': 9416, 'synset': 'neckband.n.01', 'name': 'neckband'}, {'id': 9417, 'synset': 'neck_brace.n.01', 'name': 'neck_brace'}, {'id': 9418, 'synset': 'neckcloth.n.01', 'name': 'neckcloth'}, {'id': 9419, 'synset': 'necklet.n.01', 'name': 'necklet'}, {'id': 9420, 'synset': 'neckline.n.01', 'name': 'neckline'}, {'id': 9421, 'synset': 'neckpiece.n.01', 'name': 'neckpiece'}, {'id': 9422, 'synset': 'neckwear.n.01', 'name': 'neckwear'}, {'id': 9423, 'synset': 'needle.n.02', 'name': 'needle'}, {'id': 9424, 'synset': 'needlenose_pliers.n.01', 'name': 'needlenose_pliers'}, {'id': 9425, 'synset': 'needlework.n.01', 'name': 'needlework'}, {'id': 9426, 'synset': 'negative.n.02', 'name': 'negative'}, {'id': 9427, 'synset': 'negative_magnetic_pole.n.01', 'name': 'negative_magnetic_pole'}, {'id': 9428, 'synset': 'negative_pole.n.01', 'name': 'negative_pole'}, {'id': 9429, 'synset': 'negligee.n.01', 'name': 'negligee'}, {'id': 9430, 'synset': 'neolith.n.01', 'name': 'neolith'}, {'id': 9431, 'synset': 'neon_lamp.n.01', 'name': 'neon_lamp'}, {'id': 9432, 'synset': 'nephoscope.n.01', 'name': 'nephoscope'}, {'id': 9433, 'synset': 'nest.n.05', 'name': 'nest'}, {'id': 9434, 'synset': 'nest_egg.n.02', 'name': 'nest_egg'}, {'id': 9435, 'synset': 'net.n.06', 'name': 'net'}, {'id': 9436, 'synset': 'net.n.02', 'name': 'net'}, {'id': 9437, 'synset': 'net.n.05', 'name': 'net'}, {'id': 9438, 'synset': 'net.n.04', 'name': 'net'}, {'id': 9439, 'synset': 'network.n.05', 'name': 'network'}, {'id': 9440, 'synset': 'network.n.04', 'name': 'network'}, {'id': 9441, 'synset': 'neutron_bomb.n.01', 'name': 'neutron_bomb'}, {'id': 9442, 'synset': 'newel.n.02', 'name': 'newel'}, {'id': 9443, 'synset': 'newel_post.n.01', 'name': 'newel_post'}, {'id': 9444, 'synset': 'newspaper.n.03', 'name': 'newspaper'}, {'id': 9445, 'synset': 'newsroom.n.03', 'name': 'newsroom'}, {'id': 9446, 'synset': 'newsroom.n.02', 'name': 'newsroom'}, {'id': 9447, 'synset': 'newtonian_telescope.n.01', 'name': 'Newtonian_telescope'}, {'id': 9448, 'synset': 'nib.n.01', 'name': 'nib'}, {'id': 9449, 'synset': 'niblick.n.01', 'name': 'niblick'}, {'id': 9450, 'synset': 'nicad.n.01', 'name': 'nicad'}, {'id': 9451, 'synset': 'nickel-iron_battery.n.01', 'name': 'nickel-iron_battery'}, {'id': 9452, 'synset': 'nicol_prism.n.01', 'name': 'Nicol_prism'}, {'id': 9453, 'synset': 'night_bell.n.01', 'name': 'night_bell'}, {'id': 9454, 'synset': 'nightcap.n.02', 'name': 'nightcap'}, {'id': 9455, 'synset': 'nightgown.n.01', 'name': 'nightgown'}, {'id': 9456, 'synset': 'night_latch.n.01', 'name': 'night_latch'}, {'id': 9457, 'synset': 'night-light.n.01', 'name': 'night-light'}, {'id': 9458, 'synset': 'nightshirt.n.01', 'name': 'nightshirt'}, {'id': 9459, 'synset': 'ninepin.n.01', 'name': 'ninepin'}, {'id': 9460, 'synset': 'ninepin_ball.n.01', 'name': 'ninepin_ball'}, {'id': 9461, 'synset': 'ninon.n.01', 'name': 'ninon'}, {'id': 9462, 'synset': 'nipple.n.02', 'name': 'nipple'}, {'id': 9463, 'synset': 'nipple_shield.n.01', 'name': 'nipple_shield'}, {'id': 9464, 'synset': 'niqab.n.01', 'name': 'niqab'}, {'id': 9465, 'synset': 'nissen_hut.n.01', 'name': 'Nissen_hut'}, {'id': 9466, 'synset': 'nogging.n.01', 'name': 'nogging'}, {'id': 9467, 'synset': 'noisemaker.n.01', 'name': 'noisemaker'}, {'id': 9468, 'synset': 'nonsmoker.n.02', 'name': 'nonsmoker'}, {'id': 9469, 'synset': 'non-volatile_storage.n.01', 'name': 'non-volatile_storage'}, {'id': 9470, 'synset': 'norfolk_jacket.n.01', 'name': 'Norfolk_jacket'}, {'id': 9471, 'synset': 'noria.n.01', 'name': 'noria'}, {'id': 9472, 'synset': 'nose_flute.n.01', 'name': 'nose_flute'}, {'id': 9473, 'synset': 'nosewheel.n.01', 'name': 'nosewheel'}, {'id': 9474, 'synset': 'notebook.n.02', 'name': 'notebook'}, {'id': 9475, 'synset': 'nuclear-powered_ship.n.01', 'name': 'nuclear-powered_ship'}, {'id': 9476, 'synset': 'nuclear_reactor.n.01', 'name': 'nuclear_reactor'}, {'id': 9477, 'synset': 'nuclear_rocket.n.01', 'name': 'nuclear_rocket'}, {'id': 9478, 'synset': 'nuclear_weapon.n.01', 'name': 'nuclear_weapon'}, {'id': 9479, 'synset': 'nude.n.01', 'name': 'nude'}, {'id': 9480, 'synset': 'numdah.n.01', 'name': 'numdah'}, {'id': 9481, 'synset': "nun's_habit.n.01", 'name': "nun's_habit"}, {'id': 9482, 'synset': 'nursery.n.01', 'name': 'nursery'}, {'id': 9483, 'synset': 'nut_and_bolt.n.01', 'name': 'nut_and_bolt'}, {'id': 9484, 'synset': 'nylon.n.02', 'name': 'nylon'}, {'id': 9485, 'synset': 'nylons.n.01', 'name': 'nylons'}, {'id': 9486, 'synset': 'oast.n.01', 'name': 'oast'}, {'id': 9487, 'synset': 'oast_house.n.01', 'name': 'oast_house'}, {'id': 9488, 'synset': 'obelisk.n.01', 'name': 'obelisk'}, {'id': 9489, 'synset': 'object_ball.n.01', 'name': 'object_ball'}, {'id': 9490, 'synset': 'objective.n.02', 'name': 'objective'}, {'id': 9491, 'synset': 'oblique_bandage.n.01', 'name': 'oblique_bandage'}, {'id': 9492, 'synset': 'oboe.n.01', 'name': 'oboe'}, {'id': 9493, 'synset': 'oboe_da_caccia.n.01', 'name': 'oboe_da_caccia'}, {'id': 9494, 'synset': "oboe_d'amore.n.01", 'name': "oboe_d'amore"}, {'id': 9495, 'synset': 'observation_dome.n.01', 'name': 'observation_dome'}, {'id': 9496, 'synset': 'observatory.n.01', 'name': 'observatory'}, {'id': 9497, 'synset': 'obstacle.n.02', 'name': 'obstacle'}, {'id': 9498, 'synset': 'obturator.n.01', 'name': 'obturator'}, {'id': 9499, 'synset': 'ocarina.n.01', 'name': 'ocarina'}, {'id': 9500, 'synset': 'octant.n.01', 'name': 'octant'}, {'id': 9501, 'synset': 'odd-leg_caliper.n.01', 'name': 'odd-leg_caliper'}, {'id': 9502, 'synset': 'odometer.n.01', 'name': 'odometer'}, {'id': 9503, 'synset': 'oeil_de_boeuf.n.01', 'name': 'oeil_de_boeuf'}, {'id': 9504, 'synset': 'office.n.01', 'name': 'office'}, {'id': 9505, 'synset': 'office_building.n.01', 'name': 'office_building'}, {'id': 9506, 'synset': 'office_furniture.n.01', 'name': 'office_furniture'}, {'id': 9507, 'synset': "officer's_mess.n.01", 'name': "officer's_mess"}, {'id': 9508, 'synset': 'off-line_equipment.n.01', 'name': 'off-line_equipment'}, {'id': 9509, 'synset': 'ogee.n.01', 'name': 'ogee'}, {'id': 9510, 'synset': 'ogee_arch.n.01', 'name': 'ogee_arch'}, {'id': 9511, 'synset': 'ohmmeter.n.01', 'name': 'ohmmeter'}, {'id': 9512, 'synset': 'oil.n.02', 'name': 'oil'}, {'id': 9513, 'synset': 'oilcan.n.01', 'name': 'oilcan'}, {'id': 9514, 'synset': 'oilcloth.n.01', 'name': 'oilcloth'}, {'id': 9515, 'synset': 'oil_filter.n.01', 'name': 'oil_filter'}, {'id': 9516, 'synset': 'oil_heater.n.01', 'name': 'oil_heater'}, {'id': 9517, 'synset': 'oil_paint.n.01', 'name': 'oil_paint'}, {'id': 9518, 'synset': 'oil_pump.n.01', 'name': 'oil_pump'}, {'id': 9519, 'synset': 'oil_refinery.n.01', 'name': 'oil_refinery'}, {'id': 9520, 'synset': 'oilskin.n.01', 'name': 'oilskin'}, {'id': 9521, 'synset': 'oil_slick.n.01', 'name': 'oil_slick'}, {'id': 9522, 'synset': 'oilstone.n.01', 'name': 'oilstone'}, {'id': 9523, 'synset': 'oil_tanker.n.01', 'name': 'oil_tanker'}, {'id': 9524, 'synset': 'old_school_tie.n.01', 'name': 'old_school_tie'}, {'id': 9525, 'synset': 'olive_drab.n.03', 'name': 'olive_drab'}, {'id': 9526, 'synset': 'olive_drab.n.02', 'name': 'olive_drab'}, {'id': 9527, 'synset': 'olympian_zeus.n.01', 'name': 'Olympian_Zeus'}, {'id': 9528, 'synset': 'omelet_pan.n.01', 'name': 'omelet_pan'}, {'id': 9529, 'synset': 'omnidirectional_antenna.n.01', 'name': 'omnidirectional_antenna'}, {'id': 9530, 'synset': 'omnirange.n.01', 'name': 'omnirange'}, {'id': 9531, 'synset': 'onion_dome.n.01', 'name': 'onion_dome'}, {'id': 9532, 'synset': 'open-air_market.n.01', 'name': 'open-air_market'}, {'id': 9533, 'synset': 'open_circuit.n.01', 'name': 'open_circuit'}, {'id': 9534, 'synset': 'open-end_wrench.n.01', 'name': 'open-end_wrench'}, {'id': 9535, 'synset': 'opener.n.03', 'name': 'opener'}, {'id': 9536, 'synset': 'open-hearth_furnace.n.01', 'name': 'open-hearth_furnace'}, {'id': 9537, 'synset': 'openside_plane.n.01', 'name': 'openside_plane'}, {'id': 9538, 'synset': 'open_sight.n.01', 'name': 'open_sight'}, {'id': 9539, 'synset': 'openwork.n.01', 'name': 'openwork'}, {'id': 9540, 'synset': 'opera.n.03', 'name': 'opera'}, {'id': 9541, 'synset': 'opera_cloak.n.01', 'name': 'opera_cloak'}, {'id': 9542, 'synset': 'operating_microscope.n.01', 'name': 'operating_microscope'}, {'id': 9543, 'synset': 'operating_room.n.01', 'name': 'operating_room'}, {'id': 9544, 'synset': 'operating_table.n.01', 'name': 'operating_table'}, {'id': 9545, 'synset': 'ophthalmoscope.n.01', 'name': 'ophthalmoscope'}, {'id': 9546, 'synset': 'optical_device.n.01', 'name': 'optical_device'}, {'id': 9547, 'synset': 'optical_disk.n.01', 'name': 'optical_disk'}, {'id': 9548, 'synset': 'optical_instrument.n.01', 'name': 'optical_instrument'}, {'id': 9549, 'synset': 'optical_pyrometer.n.01', 'name': 'optical_pyrometer'}, {'id': 9550, 'synset': 'optical_telescope.n.01', 'name': 'optical_telescope'}, {'id': 9551, 'synset': 'orchestra_pit.n.01', 'name': 'orchestra_pit'}, {'id': 9552, 'synset': 'ordinary.n.04', 'name': 'ordinary'}, {'id': 9553, 'synset': 'organ.n.05', 'name': 'organ'}, {'id': 9554, 'synset': 'organdy.n.01', 'name': 'organdy'}, {'id': 9555, 'synset': 'organic_light-emitting_diode.n.01', 'name': 'organic_light-emitting_diode'}, {'id': 9556, 'synset': 'organ_loft.n.01', 'name': 'organ_loft'}, {'id': 9557, 'synset': 'organ_pipe.n.01', 'name': 'organ_pipe'}, {'id': 9558, 'synset': 'organza.n.01', 'name': 'organza'}, {'id': 9559, 'synset': 'oriel.n.01', 'name': 'oriel'}, {'id': 9560, 'synset': 'oriflamme.n.02', 'name': 'oriflamme'}, {'id': 9561, 'synset': 'o_ring.n.01', 'name': 'O_ring'}, {'id': 9562, 'synset': 'orlon.n.01', 'name': 'Orlon'}, {'id': 9563, 'synset': 'orlop_deck.n.01', 'name': 'orlop_deck'}, {'id': 9564, 'synset': 'orphanage.n.02', 'name': 'orphanage'}, {'id': 9565, 'synset': 'orphrey.n.01', 'name': 'orphrey'}, {'id': 9566, 'synset': 'orrery.n.01', 'name': 'orrery'}, {'id': 9567, 'synset': 'orthicon.n.01', 'name': 'orthicon'}, {'id': 9568, 'synset': 'orthochromatic_film.n.01', 'name': 'orthochromatic_film'}, {'id': 9569, 'synset': 'orthopter.n.01', 'name': 'orthopter'}, {'id': 9570, 'synset': 'orthoscope.n.01', 'name': 'orthoscope'}, {'id': 9571, 'synset': 'oscillograph.n.01', 'name': 'oscillograph'}, {'id': 9572, 'synset': 'oscilloscope.n.01', 'name': 'oscilloscope'}, {'id': 9573, 'synset': 'ossuary.n.01', 'name': 'ossuary'}, {'id': 9574, 'synset': 'otoscope.n.01', 'name': 'otoscope'}, {'id': 9575, 'synset': 'oubliette.n.01', 'name': 'oubliette'}, {'id': 9576, 'synset': 'out-basket.n.01', 'name': 'out-basket'}, {'id': 9577, 'synset': 'outboard_motor.n.01', 'name': 'outboard_motor'}, {'id': 9578, 'synset': 'outboard_motorboat.n.01', 'name': 'outboard_motorboat'}, {'id': 9579, 'synset': 'outbuilding.n.01', 'name': 'outbuilding'}, {'id': 9580, 'synset': 'outerwear.n.01', 'name': 'outerwear'}, {'id': 9581, 'synset': 'outfall.n.01', 'name': 'outfall'}, {'id': 9582, 'synset': 'outfit.n.02', 'name': 'outfit'}, {'id': 9583, 'synset': 'outfitter.n.02', 'name': 'outfitter'}, {'id': 9584, 'synset': 'outhouse.n.01', 'name': 'outhouse'}, {'id': 9585, 'synset': 'output_device.n.01', 'name': 'output_device'}, {'id': 9586, 'synset': 'outrigger.n.01', 'name': 'outrigger'}, {'id': 9587, 'synset': 'outrigger_canoe.n.01', 'name': 'outrigger_canoe'}, {'id': 9588, 'synset': 'outside_caliper.n.01', 'name': 'outside_caliper'}, {'id': 9589, 'synset': 'outside_mirror.n.01', 'name': 'outside_mirror'}, {'id': 9590, 'synset': 'outwork.n.01', 'name': 'outwork'}, {'id': 9591, 'synset': 'oven_thermometer.n.01', 'name': 'oven_thermometer'}, {'id': 9592, 'synset': 'overall.n.02', 'name': 'overall'}, {'id': 9593, 'synset': 'overcoat.n.02', 'name': 'overcoat'}, {'id': 9594, 'synset': 'overdrive.n.02', 'name': 'overdrive'}, {'id': 9595, 'synset': 'overgarment.n.01', 'name': 'overgarment'}, {'id': 9596, 'synset': 'overhand_knot.n.01', 'name': 'overhand_knot'}, {'id': 9597, 'synset': 'overhang.n.01', 'name': 'overhang'}, {'id': 9598, 'synset': 'overhead_projector.n.01', 'name': 'overhead_projector'}, {'id': 9599, 'synset': 'overmantel.n.01', 'name': 'overmantel'}, {'id': 9600, 'synset': 'overnighter.n.02', 'name': 'overnighter'}, {'id': 9601, 'synset': 'overpass.n.01', 'name': 'overpass'}, {'id': 9602, 'synset': 'override.n.01', 'name': 'override'}, {'id': 9603, 'synset': 'overshoe.n.01', 'name': 'overshoe'}, {'id': 9604, 'synset': 'overskirt.n.01', 'name': 'overskirt'}, {'id': 9605, 'synset': 'oxbow.n.03', 'name': 'oxbow'}, {'id': 9606, 'synset': 'oxbridge.n.01', 'name': 'Oxbridge'}, {'id': 9607, 'synset': 'oxcart.n.01', 'name': 'oxcart'}, {'id': 9608, 'synset': 'oxeye.n.03', 'name': 'oxeye'}, {'id': 9609, 'synset': 'oxford.n.04', 'name': 'oxford'}, {'id': 9610, 'synset': 'oximeter.n.01', 'name': 'oximeter'}, {'id': 9611, 'synset': 'oxyacetylene_torch.n.01', 'name': 'oxyacetylene_torch'}, {'id': 9612, 'synset': 'oxygen_mask.n.01', 'name': 'oxygen_mask'}, {'id': 9613, 'synset': 'oyster_bar.n.01', 'name': 'oyster_bar'}, {'id': 9614, 'synset': 'oyster_bed.n.01', 'name': 'oyster_bed'}, {'id': 9615, 'synset': 'pace_car.n.01', 'name': 'pace_car'}, {'id': 9616, 'synset': 'pacemaker.n.03', 'name': 'pacemaker'}, {'id': 9617, 'synset': 'pack.n.03', 'name': 'pack'}, {'id': 9618, 'synset': 'pack.n.09', 'name': 'pack'}, {'id': 9619, 'synset': 'pack.n.07', 'name': 'pack'}, {'id': 9620, 'synset': 'package.n.02', 'name': 'package'}, {'id': 9621, 'synset': 'package_store.n.01', 'name': 'package_store'}, {'id': 9622, 'synset': 'packaging.n.03', 'name': 'packaging'}, {'id': 9623, 'synset': 'packing_box.n.02', 'name': 'packing_box'}, {'id': 9624, 'synset': 'packinghouse.n.02', 'name': 'packinghouse'}, {'id': 9625, 'synset': 'packinghouse.n.01', 'name': 'packinghouse'}, {'id': 9626, 'synset': 'packing_needle.n.01', 'name': 'packing_needle'}, {'id': 9627, 'synset': 'packsaddle.n.01', 'name': 'packsaddle'}, {'id': 9628, 'synset': 'paddle.n.02', 'name': 'paddle'}, {'id': 9629, 'synset': 'paddle.n.01', 'name': 'paddle'}, {'id': 9630, 'synset': 'paddle_box.n.01', 'name': 'paddle_box'}, {'id': 9631, 'synset': 'paddle_steamer.n.01', 'name': 'paddle_steamer'}, {'id': 9632, 'synset': 'paddlewheel.n.01', 'name': 'paddlewheel'}, {'id': 9633, 'synset': 'paddock.n.01', 'name': 'paddock'}, {'id': 9634, 'synset': 'page_printer.n.01', 'name': 'page_printer'}, {'id': 9635, 'synset': 'paint.n.01', 'name': 'paint'}, {'id': 9636, 'synset': 'paintball.n.01', 'name': 'paintball'}, {'id': 9637, 'synset': 'paintball_gun.n.01', 'name': 'paintball_gun'}, {'id': 9638, 'synset': 'paintbox.n.01', 'name': 'paintbox'}, {'id': 9639, 'synset': 'paisley.n.01', 'name': 'paisley'}, {'id': 9640, 'synset': 'pajama.n.01', 'name': 'pajama'}, {'id': 9641, 'synset': 'palace.n.04', 'name': 'palace'}, {'id': 9642, 'synset': 'palace.n.01', 'name': 'palace'}, {'id': 9643, 'synset': 'palace.n.03', 'name': 'palace'}, {'id': 9644, 'synset': 'palanquin.n.01', 'name': 'palanquin'}, {'id': 9645, 'synset': 'paleolith.n.01', 'name': 'paleolith'}, {'id': 9646, 'synset': 'palestra.n.01', 'name': 'palestra'}, {'id': 9647, 'synset': 'palette_knife.n.01', 'name': 'palette_knife'}, {'id': 9648, 'synset': 'palisade.n.01', 'name': 'palisade'}, {'id': 9649, 'synset': 'pallet.n.03', 'name': 'pallet'}, {'id': 9650, 'synset': 'pallette.n.01', 'name': 'pallette'}, {'id': 9651, 'synset': 'pallium.n.04', 'name': 'pallium'}, {'id': 9652, 'synset': 'pallium.n.03', 'name': 'pallium'}, {'id': 9653, 'synset': 'pancake_turner.n.01', 'name': 'pancake_turner'}, {'id': 9654, 'synset': 'panchromatic_film.n.01', 'name': 'panchromatic_film'}, {'id': 9655, 'synset': 'panda_car.n.01', 'name': 'panda_car'}, {'id': 9656, 'synset': 'paneling.n.01', 'name': 'paneling'}, {'id': 9657, 'synset': 'panhandle.n.02', 'name': 'panhandle'}, {'id': 9658, 'synset': 'panic_button.n.01', 'name': 'panic_button'}, {'id': 9659, 'synset': 'pannier.n.02', 'name': 'pannier'}, {'id': 9660, 'synset': 'pannier.n.01', 'name': 'pannier'}, {'id': 9661, 'synset': 'pannikin.n.01', 'name': 'pannikin'}, {'id': 9662, 'synset': 'panopticon.n.02', 'name': 'panopticon'}, {'id': 9663, 'synset': 'panopticon.n.01', 'name': 'panopticon'}, {'id': 9664, 'synset': 'panpipe.n.01', 'name': 'panpipe'}, {'id': 9665, 'synset': 'pantaloon.n.03', 'name': 'pantaloon'}, {'id': 9666, 'synset': 'pantechnicon.n.01', 'name': 'pantechnicon'}, {'id': 9667, 'synset': 'pantheon.n.03', 'name': 'pantheon'}, {'id': 9668, 'synset': 'pantheon.n.02', 'name': 'pantheon'}, {'id': 9669, 'synset': 'pantie.n.01', 'name': 'pantie'}, {'id': 9670, 'synset': 'panting.n.02', 'name': 'panting'}, {'id': 9671, 'synset': 'pant_leg.n.01', 'name': 'pant_leg'}, {'id': 9672, 'synset': 'pantograph.n.01', 'name': 'pantograph'}, {'id': 9673, 'synset': 'pantry.n.01', 'name': 'pantry'}, {'id': 9674, 'synset': 'pants_suit.n.01', 'name': 'pants_suit'}, {'id': 9675, 'synset': 'panty_girdle.n.01', 'name': 'panty_girdle'}, {'id': 9676, 'synset': 'panzer.n.01', 'name': 'panzer'}, {'id': 9677, 'synset': 'paper_chain.n.01', 'name': 'paper_chain'}, {'id': 9678, 'synset': 'paper_clip.n.01', 'name': 'paper_clip'}, {'id': 9679, 'synset': 'paper_cutter.n.01', 'name': 'paper_cutter'}, {'id': 9680, 'synset': 'paper_fastener.n.01', 'name': 'paper_fastener'}, {'id': 9681, 'synset': 'paper_feed.n.01', 'name': 'paper_feed'}, {'id': 9682, 'synset': 'paper_mill.n.01', 'name': 'paper_mill'}, {'id': 9683, 'synset': 'parabolic_mirror.n.01', 'name': 'parabolic_mirror'}, {'id': 9684, 'synset': 'parabolic_reflector.n.01', 'name': 'parabolic_reflector'}, {'id': 9685, 'synset': 'parallel_bars.n.01', 'name': 'parallel_bars'}, {'id': 9686, 'synset': 'parallel_circuit.n.01', 'name': 'parallel_circuit'}, {'id': 9687, 'synset': 'parallel_interface.n.01', 'name': 'parallel_interface'}, {'id': 9688, 'synset': 'parang.n.01', 'name': 'parang'}, {'id': 9689, 'synset': 'parapet.n.02', 'name': 'parapet'}, {'id': 9690, 'synset': 'parapet.n.01', 'name': 'parapet'}, {'id': 9691, 'synset': 'parer.n.02', 'name': 'parer'}, {'id': 9692, 'synset': 'parfait_glass.n.01', 'name': 'parfait_glass'}, {'id': 9693, 'synset': 'pargeting.n.02', 'name': 'pargeting'}, {'id': 9694, 'synset': 'pari-mutuel_machine.n.01', 'name': 'pari-mutuel_machine'}, {'id': 9695, 'synset': 'park_bench.n.01', 'name': 'park_bench'}, {'id': 9696, 'synset': 'parlor.n.01', 'name': 'parlor'}, {'id': 9697, 'synset': 'parquet.n.01', 'name': 'parquet'}, {'id': 9698, 'synset': 'parquetry.n.01', 'name': 'parquetry'}, {'id': 9699, 'synset': 'parsonage.n.01', 'name': 'parsonage'}, {'id': 9700, 'synset': 'parsons_table.n.01', 'name': 'Parsons_table'}, {'id': 9701, 'synset': 'partial_denture.n.01', 'name': 'partial_denture'}, {'id': 9702, 'synset': 'particle_detector.n.01', 'name': 'particle_detector'}, {'id': 9703, 'synset': 'partition.n.01', 'name': 'partition'}, {'id': 9704, 'synset': 'parts_bin.n.01', 'name': 'parts_bin'}, {'id': 9705, 'synset': 'party_line.n.02', 'name': 'party_line'}, {'id': 9706, 'synset': 'party_wall.n.01', 'name': 'party_wall'}, {'id': 9707, 'synset': 'parvis.n.01', 'name': 'parvis'}, {'id': 9708, 'synset': 'passenger_train.n.01', 'name': 'passenger_train'}, {'id': 9709, 'synset': 'passenger_van.n.01', 'name': 'passenger_van'}, {'id': 9710, 'synset': 'passe-partout.n.02', 'name': 'passe-partout'}, {'id': 9711, 'synset': 'passive_matrix_display.n.01', 'name': 'passive_matrix_display'}, {'id': 9712, 'synset': 'passkey.n.01', 'name': 'passkey'}, {'id': 9713, 'synset': 'pass-through.n.01', 'name': 'pass-through'}, {'id': 9714, 'synset': 'pastry_cart.n.01', 'name': 'pastry_cart'}, {'id': 9715, 'synset': 'patch.n.03', 'name': 'patch'}, {'id': 9716, 'synset': 'patchcord.n.01', 'name': 'patchcord'}, {'id': 9717, 'synset': 'patchouli.n.02', 'name': 'patchouli'}, {'id': 9718, 'synset': 'patch_pocket.n.01', 'name': 'patch_pocket'}, {'id': 9719, 'synset': 'patchwork.n.02', 'name': 'patchwork'}, {'id': 9720, 'synset': 'patent_log.n.01', 'name': 'patent_log'}, {'id': 9721, 'synset': 'paternoster.n.02', 'name': 'paternoster'}, {'id': 9722, 'synset': 'patina.n.01', 'name': 'patina'}, {'id': 9723, 'synset': 'patio.n.01', 'name': 'patio'}, {'id': 9724, 'synset': 'patisserie.n.01', 'name': 'patisserie'}, {'id': 9725, 'synset': 'patka.n.01', 'name': 'patka'}, {'id': 9726, 'synset': 'patrol_boat.n.01', 'name': 'patrol_boat'}, {'id': 9727, 'synset': 'patty-pan.n.01', 'name': 'patty-pan'}, {'id': 9728, 'synset': 'pave.n.01', 'name': 'pave'}, {'id': 9729, 'synset': 'pavilion.n.01', 'name': 'pavilion'}, {'id': 9730, 'synset': 'pavior.n.01', 'name': 'pavior'}, {'id': 9731, 'synset': 'pavis.n.01', 'name': 'pavis'}, {'id': 9732, 'synset': 'pawn.n.03', 'name': 'pawn'}, {'id': 9733, 'synset': "pawnbroker's_shop.n.01", 'name': "pawnbroker's_shop"}, {'id': 9734, 'synset': 'pay-phone.n.01', 'name': 'pay-phone'}, {'id': 9735, 'synset': 'pc_board.n.01', 'name': 'PC_board'}, {'id': 9736, 'synset': 'peach_orchard.n.01', 'name': 'peach_orchard'}, {'id': 9737, 'synset': 'pea_jacket.n.01', 'name': 'pea_jacket'}, {'id': 9738, 'synset': 'peavey.n.01', 'name': 'peavey'}, {'id': 9739, 'synset': 'pectoral.n.02', 'name': 'pectoral'}, {'id': 9740, 'synset': 'pedal.n.02', 'name': 'pedal'}, {'id': 9741, 'synset': 'pedal_pusher.n.01', 'name': 'pedal_pusher'}, {'id': 9742, 'synset': 'pedestal.n.03', 'name': 'pedestal'}, {'id': 9743, 'synset': 'pedestal_table.n.01', 'name': 'pedestal_table'}, {'id': 9744, 'synset': 'pedestrian_crossing.n.01', 'name': 'pedestrian_crossing'}, {'id': 9745, 'synset': 'pedicab.n.01', 'name': 'pedicab'}, {'id': 9746, 'synset': 'pediment.n.01', 'name': 'pediment'}, {'id': 9747, 'synset': 'pedometer.n.01', 'name': 'pedometer'}, {'id': 9748, 'synset': 'peep_sight.n.01', 'name': 'peep_sight'}, {'id': 9749, 'synset': 'peg.n.01', 'name': 'peg'}, {'id': 9750, 'synset': 'peg.n.06', 'name': 'peg'}, {'id': 9751, 'synset': 'peg.n.05', 'name': 'peg'}, {'id': 9752, 'synset': 'pelham.n.01', 'name': 'Pelham'}, {'id': 9753, 'synset': 'pelican_crossing.n.01', 'name': 'pelican_crossing'}, {'id': 9754, 'synset': 'pelisse.n.01', 'name': 'pelisse'}, {'id': 9755, 'synset': 'pelvimeter.n.01', 'name': 'pelvimeter'}, {'id': 9756, 'synset': 'penal_colony.n.01', 'name': 'penal_colony'}, {'id': 9757, 'synset': 'penal_institution.n.01', 'name': 'penal_institution'}, {'id': 9758, 'synset': 'penalty_box.n.01', 'name': 'penalty_box'}, {'id': 9759, 'synset': 'pen-and-ink.n.01', 'name': 'pen-and-ink'}, {'id': 9760, 'synset': 'pencil.n.04', 'name': 'pencil'}, {'id': 9761, 'synset': 'pendant_earring.n.01', 'name': 'pendant_earring'}, {'id': 9762, 'synset': 'pendulum_clock.n.01', 'name': 'pendulum_clock'}, {'id': 9763, 'synset': 'pendulum_watch.n.01', 'name': 'pendulum_watch'}, {'id': 9764, 'synset': 'penetration_bomb.n.01', 'name': 'penetration_bomb'}, {'id': 9765, 'synset': 'penile_implant.n.01', 'name': 'penile_implant'}, {'id': 9766, 'synset': 'penitentiary.n.01', 'name': 'penitentiary'}, {'id': 9767, 'synset': 'penknife.n.01', 'name': 'penknife'}, {'id': 9768, 'synset': 'penlight.n.01', 'name': 'penlight'}, {'id': 9769, 'synset': 'pennant.n.03', 'name': 'pennant'}, {'id': 9770, 'synset': 'pennywhistle.n.01', 'name': 'pennywhistle'}, {'id': 9771, 'synset': 'penthouse.n.01', 'name': 'penthouse'}, {'id': 9772, 'synset': 'pentode.n.01', 'name': 'pentode'}, {'id': 9773, 'synset': 'peplos.n.01', 'name': 'peplos'}, {'id': 9774, 'synset': 'peplum.n.01', 'name': 'peplum'}, {'id': 9775, 'synset': 'pepper_shaker.n.01', 'name': 'pepper_shaker'}, {'id': 9776, 'synset': 'pepper_spray.n.01', 'name': 'pepper_spray'}, {'id': 9777, 'synset': 'percale.n.01', 'name': 'percale'}, {'id': 9778, 'synset': 'percolator.n.01', 'name': 'percolator'}, {'id': 9779, 'synset': 'percussion_cap.n.01', 'name': 'percussion_cap'}, {'id': 9780, 'synset': 'percussion_instrument.n.01', 'name': 'percussion_instrument'}, {'id': 9781, 'synset': 'perforation.n.01', 'name': 'perforation'}, {'id': 9782, 'synset': 'perfumery.n.03', 'name': 'perfumery'}, {'id': 9783, 'synset': 'perfumery.n.02', 'name': 'perfumery'}, {'id': 9784, 'synset': 'perfumery.n.01', 'name': 'perfumery'}, {'id': 9785, 'synset': 'peripheral.n.01', 'name': 'peripheral'}, {'id': 9786, 'synset': 'periscope.n.01', 'name': 'periscope'}, {'id': 9787, 'synset': 'peristyle.n.01', 'name': 'peristyle'}, {'id': 9788, 'synset': 'periwig.n.01', 'name': 'periwig'}, {'id': 9789, 'synset': 'permanent_press.n.01', 'name': 'permanent_press'}, {'id': 9790, 'synset': 'perpetual_motion_machine.n.01', 'name': 'perpetual_motion_machine'}, {'id': 9791, 'synset': 'personal_computer.n.01', 'name': 'personal_computer'}, {'id': 9792, 'synset': 'personal_digital_assistant.n.01', 'name': 'personal_digital_assistant'}, {'id': 9793, 'synset': 'personnel_carrier.n.01', 'name': 'personnel_carrier'}, {'id': 9794, 'synset': 'pestle.n.03', 'name': 'pestle'}, {'id': 9795, 'synset': 'pestle.n.02', 'name': 'pestle'}, {'id': 9796, 'synset': 'petcock.n.01', 'name': 'petcock'}, {'id': 9797, 'synset': 'petri_dish.n.01', 'name': 'Petri_dish'}, {'id': 9798, 'synset': 'petrolatum_gauze.n.01', 'name': 'petrolatum_gauze'}, {'id': 9799, 'synset': 'pet_shop.n.01', 'name': 'pet_shop'}, {'id': 9800, 'synset': 'petticoat.n.01', 'name': 'petticoat'}, {'id': 9801, 'synset': 'phial.n.01', 'name': 'phial'}, {'id': 9802, 'synset': 'phillips_screw.n.01', 'name': 'Phillips_screw'}, {'id': 9803, 'synset': 'phillips_screwdriver.n.01', 'name': 'Phillips_screwdriver'}, {'id': 9804, 'synset': 'phonograph_needle.n.01', 'name': 'phonograph_needle'}, {'id': 9805, 'synset': 'photocathode.n.01', 'name': 'photocathode'}, {'id': 9806, 'synset': 'photocoagulator.n.01', 'name': 'photocoagulator'}, {'id': 9807, 'synset': 'photocopier.n.01', 'name': 'photocopier'}, {'id': 9808, 'synset': 'photographic_equipment.n.01', 'name': 'photographic_equipment'}, {'id': 9809, 'synset': 'photographic_paper.n.01', 'name': 'photographic_paper'}, {'id': 9810, 'synset': 'photometer.n.01', 'name': 'photometer'}, {'id': 9811, 'synset': 'photomicrograph.n.01', 'name': 'photomicrograph'}, {'id': 9812, 'synset': 'photostat.n.02', 'name': 'Photostat'}, {'id': 9813, 'synset': 'photostat.n.01', 'name': 'photostat'}, {'id': 9814, 'synset': 'physical_pendulum.n.01', 'name': 'physical_pendulum'}, {'id': 9815, 'synset': 'piano_action.n.01', 'name': 'piano_action'}, {'id': 9816, 'synset': 'piano_keyboard.n.01', 'name': 'piano_keyboard'}, {'id': 9817, 'synset': 'piano_wire.n.01', 'name': 'piano_wire'}, {'id': 9818, 'synset': 'piccolo.n.01', 'name': 'piccolo'}, {'id': 9819, 'synset': 'pick.n.07', 'name': 'pick'}, {'id': 9820, 'synset': 'pick.n.06', 'name': 'pick'}, {'id': 9821, 'synset': 'pick.n.05', 'name': 'pick'}, {'id': 9822, 'synset': 'pickelhaube.n.01', 'name': 'pickelhaube'}, {'id': 9823, 'synset': 'picket_boat.n.01', 'name': 'picket_boat'}, {'id': 9824, 'synset': 'picket_fence.n.01', 'name': 'picket_fence'}, {'id': 9825, 'synset': 'picket_ship.n.01', 'name': 'picket_ship'}, {'id': 9826, 'synset': 'pickle_barrel.n.01', 'name': 'pickle_barrel'}, {'id': 9827, 'synset': 'picture_frame.n.01', 'name': 'picture_frame'}, {'id': 9828, 'synset': 'picture_hat.n.01', 'name': 'picture_hat'}, {'id': 9829, 'synset': 'picture_rail.n.01', 'name': 'picture_rail'}, {'id': 9830, 'synset': 'picture_window.n.01', 'name': 'picture_window'}, {'id': 9831, 'synset': 'piece_of_cloth.n.01', 'name': 'piece_of_cloth'}, {'id': 9832, 'synset': 'pied-a-terre.n.01', 'name': 'pied-a-terre'}, {'id': 9833, 'synset': 'pier.n.03', 'name': 'pier'}, {'id': 9834, 'synset': 'pier.n.02', 'name': 'pier'}, {'id': 9835, 'synset': 'pier_arch.n.01', 'name': 'pier_arch'}, {'id': 9836, 'synset': 'pier_glass.n.01', 'name': 'pier_glass'}, {'id': 9837, 'synset': 'pier_table.n.01', 'name': 'pier_table'}, {'id': 9838, 'synset': 'pieta.n.01', 'name': 'pieta'}, {'id': 9839, 'synset': 'piezometer.n.01', 'name': 'piezometer'}, {'id': 9840, 'synset': 'pig_bed.n.01', 'name': 'pig_bed'}, {'id': 9841, 'synset': 'piggery.n.01', 'name': 'piggery'}, {'id': 9842, 'synset': 'pilaster.n.01', 'name': 'pilaster'}, {'id': 9843, 'synset': 'pile.n.06', 'name': 'pile'}, {'id': 9844, 'synset': 'pile_driver.n.01', 'name': 'pile_driver'}, {'id': 9845, 'synset': 'pill_bottle.n.01', 'name': 'pill_bottle'}, {'id': 9846, 'synset': 'pillbox.n.01', 'name': 'pillbox'}, {'id': 9847, 'synset': 'pillion.n.01', 'name': 'pillion'}, {'id': 9848, 'synset': 'pillory.n.01', 'name': 'pillory'}, {'id': 9849, 'synset': 'pillow_block.n.01', 'name': 'pillow_block'}, {'id': 9850, 'synset': 'pillow_lace.n.01', 'name': 'pillow_lace'}, {'id': 9851, 'synset': 'pillow_sham.n.01', 'name': 'pillow_sham'}, {'id': 9852, 'synset': 'pilot_bit.n.01', 'name': 'pilot_bit'}, {'id': 9853, 'synset': 'pilot_boat.n.01', 'name': 'pilot_boat'}, {'id': 9854, 'synset': 'pilot_burner.n.01', 'name': 'pilot_burner'}, {'id': 9855, 'synset': 'pilot_cloth.n.01', 'name': 'pilot_cloth'}, {'id': 9856, 'synset': 'pilot_engine.n.01', 'name': 'pilot_engine'}, {'id': 9857, 'synset': 'pilothouse.n.01', 'name': 'pilothouse'}, {'id': 9858, 'synset': 'pilot_light.n.02', 'name': 'pilot_light'}, {'id': 9859, 'synset': 'pin.n.08', 'name': 'pin'}, {'id': 9860, 'synset': 'pin.n.07', 'name': 'pin'}, {'id': 9861, 'synset': 'pinata.n.01', 'name': 'pinata'}, {'id': 9862, 'synset': 'pinball_machine.n.01', 'name': 'pinball_machine'}, {'id': 9863, 'synset': 'pince-nez.n.01', 'name': 'pince-nez'}, {'id': 9864, 'synset': 'pincer.n.01', 'name': 'pincer'}, {'id': 9865, 'synset': 'pinch_bar.n.01', 'name': 'pinch_bar'}, {'id': 9866, 'synset': 'pincurl_clip.n.01', 'name': 'pincurl_clip'}, {'id': 9867, 'synset': 'pinfold.n.01', 'name': 'pinfold'}, {'id': 9868, 'synset': 'pinhead.n.02', 'name': 'pinhead'}, {'id': 9869, 'synset': 'pinion.n.01', 'name': 'pinion'}, {'id': 9870, 'synset': 'pinnacle.n.01', 'name': 'pinnacle'}, {'id': 9871, 'synset': 'pinprick.n.02', 'name': 'pinprick'}, {'id': 9872, 'synset': 'pinstripe.n.03', 'name': 'pinstripe'}, {'id': 9873, 'synset': 'pinstripe.n.02', 'name': 'pinstripe'}, {'id': 9874, 'synset': 'pinstripe.n.01', 'name': 'pinstripe'}, {'id': 9875, 'synset': 'pintle.n.01', 'name': 'pintle'}, {'id': 9876, 'synset': 'pinwheel.n.02', 'name': 'pinwheel'}, {'id': 9877, 'synset': 'tabor_pipe.n.01', 'name': 'tabor_pipe'}, {'id': 9878, 'synset': 'pipe.n.04', 'name': 'pipe'}, {'id': 9879, 'synset': 'pipe_bomb.n.01', 'name': 'pipe_bomb'}, {'id': 9880, 'synset': 'pipe_cleaner.n.01', 'name': 'pipe_cleaner'}, {'id': 9881, 'synset': 'pipe_cutter.n.01', 'name': 'pipe_cutter'}, {'id': 9882, 'synset': 'pipefitting.n.01', 'name': 'pipefitting'}, {'id': 9883, 'synset': 'pipet.n.01', 'name': 'pipet'}, {'id': 9884, 'synset': 'pipe_vise.n.01', 'name': 'pipe_vise'}, {'id': 9885, 'synset': 'pipe_wrench.n.01', 'name': 'pipe_wrench'}, {'id': 9886, 'synset': 'pique.n.01', 'name': 'pique'}, {'id': 9887, 'synset': 'pirate.n.03', 'name': 'pirate'}, {'id': 9888, 'synset': 'piste.n.02', 'name': 'piste'}, {'id': 9889, 'synset': 'pistol_grip.n.01', 'name': 'pistol_grip'}, {'id': 9890, 'synset': 'piston.n.02', 'name': 'piston'}, {'id': 9891, 'synset': 'piston_ring.n.01', 'name': 'piston_ring'}, {'id': 9892, 'synset': 'piston_rod.n.01', 'name': 'piston_rod'}, {'id': 9893, 'synset': 'pit.n.07', 'name': 'pit'}, {'id': 9894, 'synset': 'pitching_wedge.n.01', 'name': 'pitching_wedge'}, {'id': 9895, 'synset': 'pitch_pipe.n.01', 'name': 'pitch_pipe'}, {'id': 9896, 'synset': 'pith_hat.n.01', 'name': 'pith_hat'}, {'id': 9897, 'synset': 'piton.n.01', 'name': 'piton'}, {'id': 9898, 'synset': 'pitot-static_tube.n.01', 'name': 'Pitot-static_tube'}, {'id': 9899, 'synset': 'pitot_tube.n.01', 'name': 'Pitot_tube'}, {'id': 9900, 'synset': 'pitsaw.n.01', 'name': 'pitsaw'}, {'id': 9901, 'synset': 'pivot.n.02', 'name': 'pivot'}, {'id': 9902, 'synset': 'pivoting_window.n.01', 'name': 'pivoting_window'}, {'id': 9903, 'synset': 'pizzeria.n.01', 'name': 'pizzeria'}, {'id': 9904, 'synset': 'place_of_business.n.01', 'name': 'place_of_business'}, {'id': 9905, 'synset': 'place_of_worship.n.01', 'name': 'place_of_worship'}, {'id': 9906, 'synset': 'placket.n.01', 'name': 'placket'}, {'id': 9907, 'synset': 'planchet.n.01', 'name': 'planchet'}, {'id': 9908, 'synset': 'plane.n.05', 'name': 'plane'}, {'id': 9909, 'synset': 'plane.n.04', 'name': 'plane'}, {'id': 9910, 'synset': 'plane_seat.n.01', 'name': 'plane_seat'}, {'id': 9911, 'synset': 'planetarium.n.03', 'name': 'planetarium'}, {'id': 9912, 'synset': 'planetarium.n.02', 'name': 'planetarium'}, {'id': 9913, 'synset': 'planetarium.n.01', 'name': 'planetarium'}, {'id': 9914, 'synset': 'planetary_gear.n.01', 'name': 'planetary_gear'}, {'id': 9915, 'synset': 'plank-bed.n.01', 'name': 'plank-bed'}, {'id': 9916, 'synset': 'planking.n.02', 'name': 'planking'}, {'id': 9917, 'synset': 'planner.n.02', 'name': 'planner'}, {'id': 9918, 'synset': 'plant.n.01', 'name': 'plant'}, {'id': 9919, 'synset': 'planter.n.03', 'name': 'planter'}, {'id': 9920, 'synset': 'plaster.n.05', 'name': 'plaster'}, {'id': 9921, 'synset': 'plasterboard.n.01', 'name': 'plasterboard'}, {'id': 9922, 'synset': 'plastering_trowel.n.01', 'name': 'plastering_trowel'}, {'id': 9923, 'synset': 'plastic_bag.n.01', 'name': 'plastic_bag'}, {'id': 9924, 'synset': 'plastic_bomb.n.01', 'name': 'plastic_bomb'}, {'id': 9925, 'synset': 'plastic_laminate.n.01', 'name': 'plastic_laminate'}, {'id': 9926, 'synset': 'plastic_wrap.n.01', 'name': 'plastic_wrap'}, {'id': 9927, 'synset': 'plastron.n.03', 'name': 'plastron'}, {'id': 9928, 'synset': 'plastron.n.02', 'name': 'plastron'}, {'id': 9929, 'synset': 'plastron.n.01', 'name': 'plastron'}, {'id': 9930, 'synset': 'plate.n.14', 'name': 'plate'}, {'id': 9931, 'synset': 'plate.n.13', 'name': 'plate'}, {'id': 9932, 'synset': 'plate.n.12', 'name': 'plate'}, {'id': 9933, 'synset': 'platen.n.03', 'name': 'platen'}, {'id': 9934, 'synset': 'platen.n.01', 'name': 'platen'}, {'id': 9935, 'synset': 'plate_rack.n.01', 'name': 'plate_rack'}, {'id': 9936, 'synset': 'plate_rail.n.01', 'name': 'plate_rail'}, {'id': 9937, 'synset': 'platform.n.01', 'name': 'platform'}, {'id': 9938, 'synset': 'platform.n.04', 'name': 'platform'}, {'id': 9939, 'synset': 'platform.n.03', 'name': 'platform'}, {'id': 9940, 'synset': 'platform_bed.n.01', 'name': 'platform_bed'}, {'id': 9941, 'synset': 'platform_rocker.n.01', 'name': 'platform_rocker'}, {'id': 9942, 'synset': 'plating.n.01', 'name': 'plating'}, {'id': 9943, 'synset': 'playback.n.02', 'name': 'playback'}, {'id': 9944, 'synset': 'playbox.n.01', 'name': 'playbox'}, {'id': 9945, 'synset': 'playground.n.02', 'name': 'playground'}, {'id': 9946, 'synset': 'playsuit.n.01', 'name': 'playsuit'}, {'id': 9947, 'synset': 'plaza.n.02', 'name': 'plaza'}, {'id': 9948, 'synset': 'pleat.n.01', 'name': 'pleat'}, {'id': 9949, 'synset': 'plenum.n.02', 'name': 'plenum'}, {'id': 9950, 'synset': 'plethysmograph.n.01', 'name': 'plethysmograph'}, {'id': 9951, 'synset': 'pleximeter.n.01', 'name': 'pleximeter'}, {'id': 9952, 'synset': 'plexor.n.01', 'name': 'plexor'}, {'id': 9953, 'synset': 'plimsoll.n.02', 'name': 'plimsoll'}, {'id': 9954, 'synset': 'plotter.n.04', 'name': 'plotter'}, {'id': 9955, 'synset': 'plug.n.01', 'name': 'plug'}, {'id': 9956, 'synset': 'plug.n.05', 'name': 'plug'}, {'id': 9957, 'synset': 'plug_fuse.n.01', 'name': 'plug_fuse'}, {'id': 9958, 'synset': 'plughole.n.01', 'name': 'plughole'}, {'id': 9959, 'synset': 'plumb_bob.n.01', 'name': 'plumb_bob'}, {'id': 9960, 'synset': 'plumb_level.n.01', 'name': 'plumb_level'}, {'id': 9961, 'synset': 'plunger.n.03', 'name': 'plunger'}, {'id': 9962, 'synset': 'plus_fours.n.01', 'name': 'plus_fours'}, {'id': 9963, 'synset': 'plush.n.01', 'name': 'plush'}, {'id': 9964, 'synset': 'plywood.n.01', 'name': 'plywood'}, {'id': 9965, 'synset': 'pneumatic_drill.n.01', 'name': 'pneumatic_drill'}, {'id': 9966, 'synset': 'p-n_junction.n.01', 'name': 'p-n_junction'}, {'id': 9967, 'synset': 'p-n-p_transistor.n.01', 'name': 'p-n-p_transistor'}, {'id': 9968, 'synset': 'poacher.n.02', 'name': 'poacher'}, {'id': 9969, 'synset': 'pocket.n.01', 'name': 'pocket'}, {'id': 9970, 'synset': 'pocket_battleship.n.01', 'name': 'pocket_battleship'}, {'id': 9971, 'synset': 'pocketcomb.n.01', 'name': 'pocketcomb'}, {'id': 9972, 'synset': 'pocket_flap.n.01', 'name': 'pocket_flap'}, {'id': 9973, 'synset': 'pocket-handkerchief.n.01', 'name': 'pocket-handkerchief'}, {'id': 9974, 'synset': 'pod.n.04', 'name': 'pod'}, {'id': 9975, 'synset': 'pogo_stick.n.01', 'name': 'pogo_stick'}, {'id': 9976, 'synset': 'point-and-shoot_camera.n.01', 'name': 'point-and-shoot_camera'}, {'id': 9977, 'synset': 'pointed_arch.n.01', 'name': 'pointed_arch'}, {'id': 9978, 'synset': 'pointing_trowel.n.01', 'name': 'pointing_trowel'}, {'id': 9979, 'synset': 'point_lace.n.01', 'name': 'point_lace'}, {'id': 9980, 'synset': 'polarimeter.n.01', 'name': 'polarimeter'}, {'id': 9981, 'synset': 'polaroid.n.01', 'name': 'Polaroid'}, {'id': 9982, 'synset': 'polaroid_camera.n.01', 'name': 'Polaroid_camera'}, {'id': 9983, 'synset': 'pole.n.09', 'name': 'pole'}, {'id': 9984, 'synset': 'poleax.n.02', 'name': 'poleax'}, {'id': 9985, 'synset': 'poleax.n.01', 'name': 'poleax'}, {'id': 9986, 'synset': 'police_boat.n.01', 'name': 'police_boat'}, {'id': 9987, 'synset': 'police_van.n.01', 'name': 'police_van'}, {'id': 9988, 'synset': 'polling_booth.n.01', 'name': 'polling_booth'}, {'id': 9989, 'synset': 'polo_ball.n.01', 'name': 'polo_ball'}, {'id': 9990, 'synset': 'polo_mallet.n.01', 'name': 'polo_mallet'}, {'id': 9991, 'synset': 'polonaise.n.01', 'name': 'polonaise'}, {'id': 9992, 'synset': 'polyester.n.03', 'name': 'polyester'}, {'id': 9993, 'synset': 'polygraph.n.01', 'name': 'polygraph'}, {'id': 9994, 'synset': 'pomade.n.01', 'name': 'pomade'}, {'id': 9995, 'synset': 'pommel_horse.n.01', 'name': 'pommel_horse'}, {'id': 9996, 'synset': 'pongee.n.01', 'name': 'pongee'}, {'id': 9997, 'synset': 'poniard.n.01', 'name': 'poniard'}, {'id': 9998, 'synset': 'pontifical.n.01', 'name': 'pontifical'}, {'id': 9999, 'synset': 'pontoon.n.01', 'name': 'pontoon'}, {'id': 10000, 'synset': 'pontoon_bridge.n.01', 'name': 'pontoon_bridge'}, {'id': 10001, 'synset': 'pony_cart.n.01', 'name': 'pony_cart'}, {'id': 10002, 'synset': 'pool_ball.n.01', 'name': 'pool_ball'}, {'id': 10003, 'synset': 'poolroom.n.01', 'name': 'poolroom'}, {'id': 10004, 'synset': 'poop_deck.n.01', 'name': 'poop_deck'}, {'id': 10005, 'synset': 'poor_box.n.01', 'name': 'poor_box'}, {'id': 10006, 'synset': 'poorhouse.n.01', 'name': 'poorhouse'}, {'id': 10007, 'synset': 'pop_bottle.n.01', 'name': 'pop_bottle'}, {'id': 10008, 'synset': 'popgun.n.01', 'name': 'popgun'}, {'id': 10009, 'synset': 'poplin.n.01', 'name': 'poplin'}, {'id': 10010, 'synset': 'popper.n.03', 'name': 'popper'}, {'id': 10011, 'synset': 'poppet.n.01', 'name': 'poppet'}, {'id': 10012, 'synset': 'pop_tent.n.01', 'name': 'pop_tent'}, {'id': 10013, 'synset': 'porcelain.n.01', 'name': 'porcelain'}, {'id': 10014, 'synset': 'porch.n.01', 'name': 'porch'}, {'id': 10015, 'synset': 'porkpie.n.01', 'name': 'porkpie'}, {'id': 10016, 'synset': 'porringer.n.01', 'name': 'porringer'}, {'id': 10017, 'synset': 'portable.n.01', 'name': 'portable'}, {'id': 10018, 'synset': 'portable_computer.n.01', 'name': 'portable_computer'}, {'id': 10019, 'synset': 'portable_circular_saw.n.01', 'name': 'portable_circular_saw'}, {'id': 10020, 'synset': 'portcullis.n.01', 'name': 'portcullis'}, {'id': 10021, 'synset': 'porte-cochere.n.02', 'name': 'porte-cochere'}, {'id': 10022, 'synset': 'porte-cochere.n.01', 'name': 'porte-cochere'}, {'id': 10023, 'synset': 'portfolio.n.01', 'name': 'portfolio'}, {'id': 10024, 'synset': 'porthole.n.01', 'name': 'porthole'}, {'id': 10025, 'synset': 'portico.n.01', 'name': 'portico'}, {'id': 10026, 'synset': 'portiere.n.01', 'name': 'portiere'}, {'id': 10027, 'synset': 'portmanteau.n.02', 'name': 'portmanteau'}, {'id': 10028, 'synset': 'portrait_camera.n.01', 'name': 'portrait_camera'}, {'id': 10029, 'synset': 'portrait_lens.n.01', 'name': 'portrait_lens'}, {'id': 10030, 'synset': 'positive_pole.n.02', 'name': 'positive_pole'}, {'id': 10031, 'synset': 'positive_pole.n.01', 'name': 'positive_pole'}, {'id': 10032, 'synset': 'positron_emission_tomography_scanner.n.01', 'name': 'positron_emission_tomography_scanner'}, {'id': 10033, 'synset': 'post.n.04', 'name': 'post'}, {'id': 10034, 'synset': 'postage_meter.n.01', 'name': 'postage_meter'}, {'id': 10035, 'synset': 'post_and_lintel.n.01', 'name': 'post_and_lintel'}, {'id': 10036, 'synset': 'post_chaise.n.01', 'name': 'post_chaise'}, {'id': 10037, 'synset': 'postern.n.01', 'name': 'postern'}, {'id': 10038, 'synset': 'post_exchange.n.01', 'name': 'post_exchange'}, {'id': 10039, 'synset': 'posthole_digger.n.01', 'name': 'posthole_digger'}, {'id': 10040, 'synset': 'post_horn.n.01', 'name': 'post_horn'}, {'id': 10041, 'synset': 'posthouse.n.01', 'name': 'posthouse'}, {'id': 10042, 'synset': 'potbelly.n.02', 'name': 'potbelly'}, {'id': 10043, 'synset': 'potemkin_village.n.01', 'name': 'Potemkin_village'}, {'id': 10044, 'synset': 'potential_divider.n.01', 'name': 'potential_divider'}, {'id': 10045, 'synset': 'potentiometer.n.02', 'name': 'potentiometer'}, {'id': 10046, 'synset': 'potentiometer.n.01', 'name': 'potentiometer'}, {'id': 10047, 'synset': 'potpourri.n.03', 'name': 'potpourri'}, {'id': 10048, 'synset': 'potsherd.n.01', 'name': 'potsherd'}, {'id': 10049, 'synset': "potter's_wheel.n.01", 'name': "potter's_wheel"}, {'id': 10050, 'synset': 'pottle.n.01', 'name': 'pottle'}, {'id': 10051, 'synset': 'potty_seat.n.01', 'name': 'potty_seat'}, {'id': 10052, 'synset': 'poultice.n.01', 'name': 'poultice'}, {'id': 10053, 'synset': 'pound.n.13', 'name': 'pound'}, {'id': 10054, 'synset': 'pound_net.n.01', 'name': 'pound_net'}, {'id': 10055, 'synset': 'powder.n.03', 'name': 'powder'}, {'id': 10056, 'synset': 'powder_and_shot.n.01', 'name': 'powder_and_shot'}, {'id': 10057, 'synset': 'powdered_mustard.n.01', 'name': 'powdered_mustard'}, {'id': 10058, 'synset': 'powder_horn.n.01', 'name': 'powder_horn'}, {'id': 10059, 'synset': 'powder_keg.n.02', 'name': 'powder_keg'}, {'id': 10060, 'synset': 'power_brake.n.01', 'name': 'power_brake'}, {'id': 10061, 'synset': 'power_cord.n.01', 'name': 'power_cord'}, {'id': 10062, 'synset': 'power_drill.n.01', 'name': 'power_drill'}, {'id': 10063, 'synset': 'power_line.n.01', 'name': 'power_line'}, {'id': 10064, 'synset': 'power_loom.n.01', 'name': 'power_loom'}, {'id': 10065, 'synset': 'power_mower.n.01', 'name': 'power_mower'}, {'id': 10066, 'synset': 'power_pack.n.01', 'name': 'power_pack'}, {'id': 10067, 'synset': 'power_saw.n.01', 'name': 'power_saw'}, {'id': 10068, 'synset': 'power_steering.n.01', 'name': 'power_steering'}, {'id': 10069, 'synset': 'power_takeoff.n.01', 'name': 'power_takeoff'}, {'id': 10070, 'synset': 'power_tool.n.01', 'name': 'power_tool'}, {'id': 10071, 'synset': 'praetorium.n.01', 'name': 'praetorium'}, {'id': 10072, 'synset': 'prayer_rug.n.01', 'name': 'prayer_rug'}, {'id': 10073, 'synset': 'prayer_shawl.n.01', 'name': 'prayer_shawl'}, {'id': 10074, 'synset': 'precipitator.n.01', 'name': 'precipitator'}, {'id': 10075, 'synset': 'prefab.n.01', 'name': 'prefab'}, {'id': 10076, 'synset': 'presbytery.n.01', 'name': 'presbytery'}, {'id': 10077, 'synset': 'presence_chamber.n.01', 'name': 'presence_chamber'}, {'id': 10078, 'synset': 'press.n.07', 'name': 'press'}, {'id': 10079, 'synset': 'press.n.03', 'name': 'press'}, {'id': 10080, 'synset': 'press.n.06', 'name': 'press'}, {'id': 10081, 'synset': 'press_box.n.01', 'name': 'press_box'}, {'id': 10082, 'synset': 'press_gallery.n.01', 'name': 'press_gallery'}, {'id': 10083, 'synset': 'press_of_sail.n.01', 'name': 'press_of_sail'}, {'id': 10084, 'synset': 'pressure_cabin.n.01', 'name': 'pressure_cabin'}, {'id': 10085, 'synset': 'pressure_cooker.n.01', 'name': 'pressure_cooker'}, {'id': 10086, 'synset': 'pressure_dome.n.01', 'name': 'pressure_dome'}, {'id': 10087, 'synset': 'pressure_gauge.n.01', 'name': 'pressure_gauge'}, {'id': 10088, 'synset': 'pressurized_water_reactor.n.01', 'name': 'pressurized_water_reactor'}, {'id': 10089, 'synset': 'pressure_suit.n.01', 'name': 'pressure_suit'}, {'id': 10090, 'synset': 'pricket.n.01', 'name': 'pricket'}, {'id': 10091, 'synset': 'prie-dieu.n.01', 'name': 'prie-dieu'}, {'id': 10092, 'synset': 'primary_coil.n.01', 'name': 'primary_coil'}, {'id': 10093, 'synset': 'primus_stove.n.01', 'name': 'Primus_stove'}, {'id': 10094, 'synset': 'prince_albert.n.02', 'name': 'Prince_Albert'}, {'id': 10095, 'synset': 'print.n.06', 'name': 'print'}, {'id': 10096, 'synset': 'print_buffer.n.01', 'name': 'print_buffer'}, {'id': 10097, 'synset': 'printed_circuit.n.01', 'name': 'printed_circuit'}, {'id': 10098, 'synset': 'printer.n.02', 'name': 'printer'}, {'id': 10099, 'synset': 'printer_cable.n.01', 'name': 'printer_cable'}, {'id': 10100, 'synset': 'priory.n.01', 'name': 'priory'}, {'id': 10101, 'synset': 'prison.n.01', 'name': 'prison'}, {'id': 10102, 'synset': 'prison_camp.n.01', 'name': 'prison_camp'}, {'id': 10103, 'synset': 'privateer.n.02', 'name': 'privateer'}, {'id': 10104, 'synset': 'private_line.n.01', 'name': 'private_line'}, {'id': 10105, 'synset': 'privet_hedge.n.01', 'name': 'privet_hedge'}, {'id': 10106, 'synset': 'probe.n.02', 'name': 'probe'}, {'id': 10107, 'synset': 'proctoscope.n.01', 'name': 'proctoscope'}, {'id': 10108, 'synset': 'prod.n.02', 'name': 'prod'}, {'id': 10109, 'synset': 'production_line.n.01', 'name': 'production_line'}, {'id': 10110, 'synset': 'projector.n.01', 'name': 'projector'}, {'id': 10111, 'synset': 'prolonge.n.01', 'name': 'prolonge'}, {'id': 10112, 'synset': 'prolonge_knot.n.01', 'name': 'prolonge_knot'}, {'id': 10113, 'synset': 'prompter.n.02', 'name': 'prompter'}, {'id': 10114, 'synset': 'prong.n.01', 'name': 'prong'}, {'id': 10115, 'synset': 'propeller_plane.n.01', 'name': 'propeller_plane'}, {'id': 10116, 'synset': 'propjet.n.01', 'name': 'propjet'}, {'id': 10117, 'synset': 'proportional_counter_tube.n.01', 'name': 'proportional_counter_tube'}, {'id': 10118, 'synset': 'propulsion_system.n.01', 'name': 'propulsion_system'}, {'id': 10119, 'synset': 'proscenium.n.02', 'name': 'proscenium'}, {'id': 10120, 'synset': 'proscenium_arch.n.01', 'name': 'proscenium_arch'}, {'id': 10121, 'synset': 'prosthesis.n.01', 'name': 'prosthesis'}, {'id': 10122, 'synset': 'protective_covering.n.01', 'name': 'protective_covering'}, {'id': 10123, 'synset': 'protective_garment.n.01', 'name': 'protective_garment'}, {'id': 10124, 'synset': 'proton_accelerator.n.01', 'name': 'proton_accelerator'}, {'id': 10125, 'synset': 'protractor.n.01', 'name': 'protractor'}, {'id': 10126, 'synset': 'pruner.n.02', 'name': 'pruner'}, {'id': 10127, 'synset': 'pruning_knife.n.01', 'name': 'pruning_knife'}, {'id': 10128, 'synset': 'pruning_saw.n.01', 'name': 'pruning_saw'}, {'id': 10129, 'synset': 'pruning_shears.n.01', 'name': 'pruning_shears'}, {'id': 10130, 'synset': 'psaltery.n.01', 'name': 'psaltery'}, {'id': 10131, 'synset': 'psychrometer.n.01', 'name': 'psychrometer'}, {'id': 10132, 'synset': 'pt_boat.n.01', 'name': 'PT_boat'}, {'id': 10133, 'synset': 'public_address_system.n.01', 'name': 'public_address_system'}, {'id': 10134, 'synset': 'public_house.n.01', 'name': 'public_house'}, {'id': 10135, 'synset': 'public_toilet.n.01', 'name': 'public_toilet'}, {'id': 10136, 'synset': 'public_transport.n.01', 'name': 'public_transport'}, {'id': 10137, 'synset': 'public_works.n.01', 'name': 'public_works'}, {'id': 10138, 'synset': 'puck.n.02', 'name': 'puck'}, {'id': 10139, 'synset': 'pull.n.04', 'name': 'pull'}, {'id': 10140, 'synset': 'pullback.n.01', 'name': 'pullback'}, {'id': 10141, 'synset': 'pull_chain.n.01', 'name': 'pull_chain'}, {'id': 10142, 'synset': 'pulley.n.01', 'name': 'pulley'}, {'id': 10143, 'synset': 'pull-off.n.01', 'name': 'pull-off'}, {'id': 10144, 'synset': 'pullman.n.01', 'name': 'Pullman'}, {'id': 10145, 'synset': 'pullover.n.01', 'name': 'pullover'}, {'id': 10146, 'synset': 'pull-through.n.01', 'name': 'pull-through'}, {'id': 10147, 'synset': 'pulse_counter.n.01', 'name': 'pulse_counter'}, {'id': 10148, 'synset': 'pulse_generator.n.01', 'name': 'pulse_generator'}, {'id': 10149, 'synset': 'pulse_timing_circuit.n.01', 'name': 'pulse_timing_circuit'}, {'id': 10150, 'synset': 'pump.n.01', 'name': 'pump'}, {'id': 10151, 'synset': 'pump.n.03', 'name': 'pump'}, {'id': 10152, 'synset': 'pump_action.n.01', 'name': 'pump_action'}, {'id': 10153, 'synset': 'pump_house.n.01', 'name': 'pump_house'}, {'id': 10154, 'synset': 'pump_room.n.01', 'name': 'pump_room'}, {'id': 10155, 'synset': 'pump-type_pliers.n.01', 'name': 'pump-type_pliers'}, {'id': 10156, 'synset': 'pump_well.n.01', 'name': 'pump_well'}, {'id': 10157, 'synset': 'punchboard.n.01', 'name': 'punchboard'}, {'id': 10158, 'synset': 'punch_bowl.n.01', 'name': 'punch_bowl'}, {'id': 10159, 'synset': 'punching_bag.n.02', 'name': 'punching_bag'}, {'id': 10160, 'synset': 'punch_pliers.n.01', 'name': 'punch_pliers'}, {'id': 10161, 'synset': 'punch_press.n.01', 'name': 'punch_press'}, {'id': 10162, 'synset': 'punnet.n.01', 'name': 'punnet'}, {'id': 10163, 'synset': 'punt.n.02', 'name': 'punt'}, {'id': 10164, 'synset': 'pup_tent.n.01', 'name': 'pup_tent'}, {'id': 10165, 'synset': 'purdah.n.03', 'name': 'purdah'}, {'id': 10166, 'synset': 'purifier.n.01', 'name': 'purifier'}, {'id': 10167, 'synset': 'purl.n.02', 'name': 'purl'}, {'id': 10168, 'synset': 'purse.n.03', 'name': 'purse'}, {'id': 10169, 'synset': 'push-bike.n.01', 'name': 'push-bike'}, {'id': 10170, 'synset': 'push_broom.n.01', 'name': 'push_broom'}, {'id': 10171, 'synset': 'push_button.n.01', 'name': 'push_button'}, {'id': 10172, 'synset': 'push-button_radio.n.01', 'name': 'push-button_radio'}, {'id': 10173, 'synset': 'pusher.n.04', 'name': 'pusher'}, {'id': 10174, 'synset': 'put-put.n.01', 'name': 'put-put'}, {'id': 10175, 'synset': 'puttee.n.01', 'name': 'puttee'}, {'id': 10176, 'synset': 'putter.n.02', 'name': 'putter'}, {'id': 10177, 'synset': 'putty_knife.n.01', 'name': 'putty_knife'}, {'id': 10178, 'synset': 'puzzle.n.02', 'name': 'puzzle'}, {'id': 10179, 'synset': 'pylon.n.02', 'name': 'pylon'}, {'id': 10180, 'synset': 'pylon.n.01', 'name': 'pylon'}, {'id': 10181, 'synset': 'pyramidal_tent.n.01', 'name': 'pyramidal_tent'}, {'id': 10182, 'synset': 'pyrograph.n.01', 'name': 'pyrograph'}, {'id': 10183, 'synset': 'pyrometer.n.01', 'name': 'pyrometer'}, {'id': 10184, 'synset': 'pyrometric_cone.n.01', 'name': 'pyrometric_cone'}, {'id': 10185, 'synset': 'pyrostat.n.01', 'name': 'pyrostat'}, {'id': 10186, 'synset': 'pyx.n.02', 'name': 'pyx'}, {'id': 10187, 'synset': 'pyx.n.01', 'name': 'pyx'}, {'id': 10188, 'synset': 'pyxis.n.03', 'name': 'pyxis'}, {'id': 10189, 'synset': 'quad.n.04', 'name': 'quad'}, {'id': 10190, 'synset': 'quadrant.n.04', 'name': 'quadrant'}, {'id': 10191, 'synset': 'quadraphony.n.01', 'name': 'quadraphony'}, {'id': 10192, 'synset': 'quartering.n.02', 'name': 'quartering'}, {'id': 10193, 'synset': 'quarterstaff.n.01', 'name': 'quarterstaff'}, {'id': 10194, 'synset': 'quartz_battery.n.01', 'name': 'quartz_battery'}, {'id': 10195, 'synset': 'quartz_lamp.n.01', 'name': 'quartz_lamp'}, {'id': 10196, 'synset': 'queen.n.08', 'name': 'queen'}, {'id': 10197, 'synset': 'queen.n.07', 'name': 'queen'}, {'id': 10198, 'synset': 'queen_post.n.01', 'name': 'queen_post'}, {'id': 10199, 'synset': 'quern.n.01', 'name': 'quern'}, {'id': 10200, 'synset': 'quill.n.01', 'name': 'quill'}, {'id': 10201, 'synset': 'quilted_bedspread.n.01', 'name': 'quilted_bedspread'}, {'id': 10202, 'synset': 'quilting.n.02', 'name': 'quilting'}, {'id': 10203, 'synset': 'quipu.n.01', 'name': 'quipu'}, {'id': 10204, 'synset': 'quirk_molding.n.01', 'name': 'quirk_molding'}, {'id': 10205, 'synset': 'quirt.n.01', 'name': 'quirt'}, {'id': 10206, 'synset': 'quiver.n.03', 'name': 'quiver'}, {'id': 10207, 'synset': 'quoin.n.02', 'name': 'quoin'}, {'id': 10208, 'synset': 'quoit.n.01', 'name': 'quoit'}, {'id': 10209, 'synset': 'qwerty_keyboard.n.01', 'name': 'QWERTY_keyboard'}, {'id': 10210, 'synset': 'rabbet.n.01', 'name': 'rabbet'}, {'id': 10211, 'synset': 'rabbet_joint.n.01', 'name': 'rabbet_joint'}, {'id': 10212, 'synset': 'rabbit_ears.n.01', 'name': 'rabbit_ears'}, {'id': 10213, 'synset': 'rabbit_hutch.n.01', 'name': 'rabbit_hutch'}, {'id': 10214, 'synset': 'raceabout.n.01', 'name': 'raceabout'}, {'id': 10215, 'synset': 'raceway.n.01', 'name': 'raceway'}, {'id': 10216, 'synset': 'racing_boat.n.01', 'name': 'racing_boat'}, {'id': 10217, 'synset': 'racing_gig.n.01', 'name': 'racing_gig'}, {'id': 10218, 'synset': 'racing_skiff.n.01', 'name': 'racing_skiff'}, {'id': 10219, 'synset': 'rack.n.05', 'name': 'rack'}, {'id': 10220, 'synset': 'rack.n.01', 'name': 'rack'}, {'id': 10221, 'synset': 'rack.n.04', 'name': 'rack'}, {'id': 10222, 'synset': 'rack_and_pinion.n.01', 'name': 'rack_and_pinion'}, {'id': 10223, 'synset': 'racquetball.n.01', 'name': 'racquetball'}, {'id': 10224, 'synset': 'radial.n.01', 'name': 'radial'}, {'id': 10225, 'synset': 'radial_engine.n.01', 'name': 'radial_engine'}, {'id': 10226, 'synset': 'radiation_pyrometer.n.01', 'name': 'radiation_pyrometer'}, {'id': 10227, 'synset': 'radiator.n.02', 'name': 'radiator'}, {'id': 10228, 'synset': 'radiator_cap.n.01', 'name': 'radiator_cap'}, {'id': 10229, 'synset': 'radiator_hose.n.01', 'name': 'radiator_hose'}, {'id': 10230, 'synset': 'radio.n.03', 'name': 'radio'}, {'id': 10231, 'synset': 'radio_antenna.n.01', 'name': 'radio_antenna'}, {'id': 10232, 'synset': 'radio_chassis.n.01', 'name': 'radio_chassis'}, {'id': 10233, 'synset': 'radio_compass.n.01', 'name': 'radio_compass'}, {'id': 10234, 'synset': 'radiogram.n.02', 'name': 'radiogram'}, {'id': 10235, 'synset': 'radio_interferometer.n.01', 'name': 'radio_interferometer'}, {'id': 10236, 'synset': 'radio_link.n.01', 'name': 'radio_link'}, {'id': 10237, 'synset': 'radiometer.n.01', 'name': 'radiometer'}, {'id': 10238, 'synset': 'radiomicrometer.n.01', 'name': 'radiomicrometer'}, {'id': 10239, 'synset': 'radio-phonograph.n.01', 'name': 'radio-phonograph'}, {'id': 10240, 'synset': 'radiotelegraph.n.02', 'name': 'radiotelegraph'}, {'id': 10241, 'synset': 'radiotelephone.n.02', 'name': 'radiotelephone'}, {'id': 10242, 'synset': 'radio_telescope.n.01', 'name': 'radio_telescope'}, {'id': 10243, 'synset': 'radiotherapy_equipment.n.01', 'name': 'radiotherapy_equipment'}, {'id': 10244, 'synset': 'radio_transmitter.n.01', 'name': 'radio_transmitter'}, {'id': 10245, 'synset': 'radome.n.01', 'name': 'radome'}, {'id': 10246, 'synset': 'rafter.n.01', 'name': 'rafter'}, {'id': 10247, 'synset': 'raft_foundation.n.01', 'name': 'raft_foundation'}, {'id': 10248, 'synset': 'rag.n.01', 'name': 'rag'}, {'id': 10249, 'synset': 'ragbag.n.02', 'name': 'ragbag'}, {'id': 10250, 'synset': 'raglan.n.01', 'name': 'raglan'}, {'id': 10251, 'synset': 'raglan_sleeve.n.01', 'name': 'raglan_sleeve'}, {'id': 10252, 'synset': 'rail.n.04', 'name': 'rail'}, {'id': 10253, 'synset': 'rail_fence.n.01', 'name': 'rail_fence'}, {'id': 10254, 'synset': 'railhead.n.01', 'name': 'railhead'}, {'id': 10255, 'synset': 'railing.n.01', 'name': 'railing'}, {'id': 10256, 'synset': 'railing.n.02', 'name': 'railing'}, {'id': 10257, 'synset': 'railroad_bed.n.01', 'name': 'railroad_bed'}, {'id': 10258, 'synset': 'railroad_tunnel.n.01', 'name': 'railroad_tunnel'}, {'id': 10259, 'synset': 'rain_barrel.n.01', 'name': 'rain_barrel'}, {'id': 10260, 'synset': 'rain_gauge.n.01', 'name': 'rain_gauge'}, {'id': 10261, 'synset': 'rain_stick.n.01', 'name': 'rain_stick'}, {'id': 10262, 'synset': 'rake.n.03', 'name': 'rake'}, {'id': 10263, 'synset': 'rake_handle.n.01', 'name': 'rake_handle'}, {'id': 10264, 'synset': 'ram_disk.n.01', 'name': 'RAM_disk'}, {'id': 10265, 'synset': 'ramekin.n.02', 'name': 'ramekin'}, {'id': 10266, 'synset': 'ramjet.n.01', 'name': 'ramjet'}, {'id': 10267, 'synset': 'rammer.n.01', 'name': 'rammer'}, {'id': 10268, 'synset': 'ramp.n.01', 'name': 'ramp'}, {'id': 10269, 'synset': 'rampant_arch.n.01', 'name': 'rampant_arch'}, {'id': 10270, 'synset': 'rampart.n.01', 'name': 'rampart'}, {'id': 10271, 'synset': 'ramrod.n.01', 'name': 'ramrod'}, {'id': 10272, 'synset': 'ramrod.n.03', 'name': 'ramrod'}, {'id': 10273, 'synset': 'ranch.n.01', 'name': 'ranch'}, {'id': 10274, 'synset': 'ranch_house.n.01', 'name': 'ranch_house'}, {'id': 10275, 'synset': 'random-access_memory.n.01', 'name': 'random-access_memory'}, {'id': 10276, 'synset': 'rangefinder.n.01', 'name': 'rangefinder'}, {'id': 10277, 'synset': 'range_hood.n.01', 'name': 'range_hood'}, {'id': 10278, 'synset': 'range_pole.n.01', 'name': 'range_pole'}, {'id': 10279, 'synset': 'rapier.n.01', 'name': 'rapier'}, {'id': 10280, 'synset': 'rariora.n.01', 'name': 'rariora'}, {'id': 10281, 'synset': 'rasp.n.02', 'name': 'rasp'}, {'id': 10282, 'synset': 'ratchet.n.01', 'name': 'ratchet'}, {'id': 10283, 'synset': 'ratchet_wheel.n.01', 'name': 'ratchet_wheel'}, {'id': 10284, 'synset': 'rathskeller.n.01', 'name': 'rathskeller'}, {'id': 10285, 'synset': 'ratline.n.01', 'name': 'ratline'}, {'id': 10286, 'synset': 'rat-tail_file.n.01', 'name': 'rat-tail_file'}, {'id': 10287, 'synset': 'rattan.n.03', 'name': 'rattan'}, {'id': 10288, 'synset': 'rattrap.n.03', 'name': 'rattrap'}, {'id': 10289, 'synset': 'rayon.n.01', 'name': 'rayon'}, {'id': 10290, 'synset': 'razor.n.01', 'name': 'razor'}, {'id': 10291, 'synset': 'reaction-propulsion_engine.n.01', 'name': 'reaction-propulsion_engine'}, {'id': 10292, 'synset': 'reaction_turbine.n.01', 'name': 'reaction_turbine'}, {'id': 10293, 'synset': 'reactor.n.01', 'name': 'reactor'}, {'id': 10294, 'synset': 'reading_lamp.n.01', 'name': 'reading_lamp'}, {'id': 10295, 'synset': 'reading_room.n.01', 'name': 'reading_room'}, {'id': 10296, 'synset': 'read-only_memory.n.01', 'name': 'read-only_memory'}, {'id': 10297, 'synset': 'read-only_memory_chip.n.01', 'name': 'read-only_memory_chip'}, {'id': 10298, 'synset': 'readout.n.03', 'name': 'readout'}, {'id': 10299, 'synset': 'read/write_head.n.01', 'name': 'read/write_head'}, {'id': 10300, 'synset': 'ready-to-wear.n.01', 'name': 'ready-to-wear'}, {'id': 10301, 'synset': 'real_storage.n.01', 'name': 'real_storage'}, {'id': 10302, 'synset': 'reamer.n.02', 'name': 'reamer'}, {'id': 10303, 'synset': 'reaumur_thermometer.n.01', 'name': 'Reaumur_thermometer'}, {'id': 10304, 'synset': 'rebozo.n.01', 'name': 'rebozo'}, {'id': 10305, 'synset': 'receiver.n.01', 'name': 'receiver'}, {'id': 10306, 'synset': 'receptacle.n.01', 'name': 'receptacle'}, {'id': 10307, 'synset': 'reception_desk.n.01', 'name': 'reception_desk'}, {'id': 10308, 'synset': 'reception_room.n.01', 'name': 'reception_room'}, {'id': 10309, 'synset': 'recess.n.04', 'name': 'recess'}, {'id': 10310, 'synset': 'reciprocating_engine.n.01', 'name': 'reciprocating_engine'}, {'id': 10311, 'synset': 'reconnaissance_plane.n.01', 'name': 'reconnaissance_plane'}, {'id': 10312, 'synset': 'reconnaissance_vehicle.n.01', 'name': 'reconnaissance_vehicle'}, {'id': 10313, 'synset': 'record_changer.n.01', 'name': 'record_changer'}, {'id': 10314, 'synset': 'recorder.n.01', 'name': 'recorder'}, {'id': 10315, 'synset': 'recording.n.03', 'name': 'recording'}, {'id': 10316, 'synset': 'recording_system.n.01', 'name': 'recording_system'}, {'id': 10317, 'synset': 'record_sleeve.n.01', 'name': 'record_sleeve'}, {'id': 10318, 'synset': 'recovery_room.n.01', 'name': 'recovery_room'}, {'id': 10319, 'synset': 'recreational_vehicle.n.01', 'name': 'recreational_vehicle'}, {'id': 10320, 'synset': 'recreation_room.n.01', 'name': 'recreation_room'}, {'id': 10321, 'synset': 'recycling_bin.n.01', 'name': 'recycling_bin'}, {'id': 10322, 'synset': 'recycling_plant.n.01', 'name': 'recycling_plant'}, {'id': 10323, 'synset': 'redbrick_university.n.01', 'name': 'redbrick_university'}, {'id': 10324, 'synset': 'red_carpet.n.01', 'name': 'red_carpet'}, {'id': 10325, 'synset': 'redoubt.n.02', 'name': 'redoubt'}, {'id': 10326, 'synset': 'redoubt.n.01', 'name': 'redoubt'}, {'id': 10327, 'synset': 'reduction_gear.n.01', 'name': 'reduction_gear'}, {'id': 10328, 'synset': 'reed_pipe.n.01', 'name': 'reed_pipe'}, {'id': 10329, 'synset': 'reed_stop.n.01', 'name': 'reed_stop'}, {'id': 10330, 'synset': 'reef_knot.n.01', 'name': 'reef_knot'}, {'id': 10331, 'synset': 'reel.n.03', 'name': 'reel'}, {'id': 10332, 'synset': 'reel.n.01', 'name': 'reel'}, {'id': 10333, 'synset': 'refectory.n.01', 'name': 'refectory'}, {'id': 10334, 'synset': 'refectory_table.n.01', 'name': 'refectory_table'}, {'id': 10335, 'synset': 'refinery.n.01', 'name': 'refinery'}, {'id': 10336, 'synset': 'reflecting_telescope.n.01', 'name': 'reflecting_telescope'}, {'id': 10337, 'synset': 'reflectometer.n.01', 'name': 'reflectometer'}, {'id': 10338, 'synset': 'reflex_camera.n.01', 'name': 'reflex_camera'}, {'id': 10339, 'synset': 'reflux_condenser.n.01', 'name': 'reflux_condenser'}, {'id': 10340, 'synset': 'reformatory.n.01', 'name': 'reformatory'}, {'id': 10341, 'synset': 'reformer.n.02', 'name': 'reformer'}, {'id': 10342, 'synset': 'refracting_telescope.n.01', 'name': 'refracting_telescope'}, {'id': 10343, 'synset': 'refractometer.n.01', 'name': 'refractometer'}, {'id': 10344, 'synset': 'refrigeration_system.n.01', 'name': 'refrigeration_system'}, {'id': 10345, 'synset': 'refrigerator.n.01', 'name': 'refrigerator'}, {'id': 10346, 'synset': 'refrigerator_car.n.01', 'name': 'refrigerator_car'}, {'id': 10347, 'synset': 'refuge.n.03', 'name': 'refuge'}, {'id': 10348, 'synset': 'regalia.n.01', 'name': 'regalia'}, {'id': 10349, 'synset': 'regimentals.n.01', 'name': 'regimentals'}, {'id': 10350, 'synset': 'regulator.n.01', 'name': 'regulator'}, {'id': 10351, 'synset': 'rein.n.01', 'name': 'rein'}, {'id': 10352, 'synset': 'relay.n.05', 'name': 'relay'}, {'id': 10353, 'synset': 'release.n.08', 'name': 'release'}, {'id': 10354, 'synset': 'religious_residence.n.01', 'name': 'religious_residence'}, {'id': 10355, 'synset': 'reliquary.n.01', 'name': 'reliquary'}, {'id': 10356, 'synset': 'remote_terminal.n.01', 'name': 'remote_terminal'}, {'id': 10357, 'synset': 'removable_disk.n.01', 'name': 'removable_disk'}, {'id': 10358, 'synset': 'rendering.n.05', 'name': 'rendering'}, {'id': 10359, 'synset': 'rep.n.02', 'name': 'rep'}, {'id': 10360, 'synset': 'repair_shop.n.01', 'name': 'repair_shop'}, {'id': 10361, 'synset': 'repeater.n.04', 'name': 'repeater'}, {'id': 10362, 'synset': 'repeating_firearm.n.01', 'name': 'repeating_firearm'}, {'id': 10363, 'synset': 'repository.n.03', 'name': 'repository'}, {'id': 10364, 'synset': 'reproducer.n.01', 'name': 'reproducer'}, {'id': 10365, 'synset': 'rerebrace.n.01', 'name': 'rerebrace'}, {'id': 10366, 'synset': 'rescue_equipment.n.01', 'name': 'rescue_equipment'}, {'id': 10367, 'synset': 'research_center.n.01', 'name': 'research_center'}, {'id': 10368, 'synset': 'reseau.n.02', 'name': 'reseau'}, {'id': 10369, 'synset': 'reservoir.n.03', 'name': 'reservoir'}, {'id': 10370, 'synset': 'reset.n.01', 'name': 'reset'}, {'id': 10371, 'synset': 'reset_button.n.01', 'name': 'reset_button'}, {'id': 10372, 'synset': 'residence.n.02', 'name': 'residence'}, {'id': 10373, 'synset': 'resistance_pyrometer.n.01', 'name': 'resistance_pyrometer'}, {'id': 10374, 'synset': 'resistor.n.01', 'name': 'resistor'}, {'id': 10375, 'synset': 'resonator.n.03', 'name': 'resonator'}, {'id': 10376, 'synset': 'resonator.n.01', 'name': 'resonator'}, {'id': 10377, 'synset': 'resort_hotel.n.02', 'name': 'resort_hotel'}, {'id': 10378, 'synset': 'respirator.n.01', 'name': 'respirator'}, {'id': 10379, 'synset': 'restaurant.n.01', 'name': 'restaurant'}, {'id': 10380, 'synset': 'rest_house.n.01', 'name': 'rest_house'}, {'id': 10381, 'synset': 'restraint.n.06', 'name': 'restraint'}, {'id': 10382, 'synset': 'resuscitator.n.01', 'name': 'resuscitator'}, {'id': 10383, 'synset': 'retainer.n.03', 'name': 'retainer'}, {'id': 10384, 'synset': 'retaining_wall.n.01', 'name': 'retaining_wall'}, {'id': 10385, 'synset': 'reticle.n.01', 'name': 'reticle'}, {'id': 10386, 'synset': 'reticulation.n.02', 'name': 'reticulation'}, {'id': 10387, 'synset': 'reticule.n.01', 'name': 'reticule'}, {'id': 10388, 'synset': 'retort.n.02', 'name': 'retort'}, {'id': 10389, 'synset': 'retractor.n.01', 'name': 'retractor'}, {'id': 10390, 'synset': 'return_key.n.01', 'name': 'return_key'}, {'id': 10391, 'synset': 'reverberatory_furnace.n.01', 'name': 'reverberatory_furnace'}, {'id': 10392, 'synset': 'revers.n.01', 'name': 'revers'}, {'id': 10393, 'synset': 'reverse.n.02', 'name': 'reverse'}, {'id': 10394, 'synset': 'reversible.n.01', 'name': 'reversible'}, {'id': 10395, 'synset': 'revetment.n.02', 'name': 'revetment'}, {'id': 10396, 'synset': 'revetment.n.01', 'name': 'revetment'}, {'id': 10397, 'synset': 'revolver.n.01', 'name': 'revolver'}, {'id': 10398, 'synset': 'revolving_door.n.02', 'name': 'revolving_door'}, {'id': 10399, 'synset': 'rheometer.n.01', 'name': 'rheometer'}, {'id': 10400, 'synset': 'rheostat.n.01', 'name': 'rheostat'}, {'id': 10401, 'synset': 'rhinoscope.n.01', 'name': 'rhinoscope'}, {'id': 10402, 'synset': 'rib.n.01', 'name': 'rib'}, {'id': 10403, 'synset': 'riband.n.01', 'name': 'riband'}, {'id': 10404, 'synset': 'ribbed_vault.n.01', 'name': 'ribbed_vault'}, {'id': 10405, 'synset': 'ribbing.n.01', 'name': 'ribbing'}, {'id': 10406, 'synset': 'ribbon_development.n.01', 'name': 'ribbon_development'}, {'id': 10407, 'synset': 'rib_joint_pliers.n.01', 'name': 'rib_joint_pliers'}, {'id': 10408, 'synset': 'ricer.n.01', 'name': 'ricer'}, {'id': 10409, 'synset': 'riddle.n.02', 'name': 'riddle'}, {'id': 10410, 'synset': 'ride.n.02', 'name': 'ride'}, {'id': 10411, 'synset': 'ridge.n.06', 'name': 'ridge'}, {'id': 10412, 'synset': 'ridge_rope.n.01', 'name': 'ridge_rope'}, {'id': 10413, 'synset': 'riding_boot.n.01', 'name': 'riding_boot'}, {'id': 10414, 'synset': 'riding_crop.n.01', 'name': 'riding_crop'}, {'id': 10415, 'synset': 'riding_mower.n.01', 'name': 'riding_mower'}, {'id': 10416, 'synset': 'rifle_ball.n.01', 'name': 'rifle_ball'}, {'id': 10417, 'synset': 'rifle_grenade.n.01', 'name': 'rifle_grenade'}, {'id': 10418, 'synset': 'rig.n.01', 'name': 'rig'}, {'id': 10419, 'synset': 'rigger.n.02', 'name': 'rigger'}, {'id': 10420, 'synset': 'rigger.n.04', 'name': 'rigger'}, {'id': 10421, 'synset': 'rigging.n.01', 'name': 'rigging'}, {'id': 10422, 'synset': 'rigout.n.01', 'name': 'rigout'}, {'id': 10423, 'synset': 'ringlet.n.03', 'name': 'ringlet'}, {'id': 10424, 'synset': 'rings.n.01', 'name': 'rings'}, {'id': 10425, 'synset': 'rink.n.01', 'name': 'rink'}, {'id': 10426, 'synset': 'riot_gun.n.01', 'name': 'riot_gun'}, {'id': 10427, 'synset': 'ripcord.n.02', 'name': 'ripcord'}, {'id': 10428, 'synset': 'ripcord.n.01', 'name': 'ripcord'}, {'id': 10429, 'synset': 'ripping_bar.n.01', 'name': 'ripping_bar'}, {'id': 10430, 'synset': 'ripping_chisel.n.01', 'name': 'ripping_chisel'}, {'id': 10431, 'synset': 'ripsaw.n.01', 'name': 'ripsaw'}, {'id': 10432, 'synset': 'riser.n.03', 'name': 'riser'}, {'id': 10433, 'synset': 'riser.n.02', 'name': 'riser'}, {'id': 10434, 'synset': 'ritz.n.03', 'name': 'Ritz'}, {'id': 10435, 'synset': 'rivet.n.02', 'name': 'rivet'}, {'id': 10436, 'synset': 'riveting_machine.n.01', 'name': 'riveting_machine'}, {'id': 10437, 'synset': 'roach_clip.n.01', 'name': 'roach_clip'}, {'id': 10438, 'synset': 'road.n.01', 'name': 'road'}, {'id': 10439, 'synset': 'roadbed.n.01', 'name': 'roadbed'}, {'id': 10440, 'synset': 'roadblock.n.02', 'name': 'roadblock'}, {'id': 10441, 'synset': 'roadhouse.n.01', 'name': 'roadhouse'}, {'id': 10442, 'synset': 'roadster.n.01', 'name': 'roadster'}, {'id': 10443, 'synset': 'roadway.n.01', 'name': 'roadway'}, {'id': 10444, 'synset': 'roaster.n.04', 'name': 'roaster'}, {'id': 10445, 'synset': 'robotics_equipment.n.01', 'name': 'robotics_equipment'}, {'id': 10446, 'synset': 'rochon_prism.n.01', 'name': 'Rochon_prism'}, {'id': 10447, 'synset': 'rock_bit.n.01', 'name': 'rock_bit'}, {'id': 10448, 'synset': 'rocker.n.07', 'name': 'rocker'}, {'id': 10449, 'synset': 'rocker.n.05', 'name': 'rocker'}, {'id': 10450, 'synset': 'rocker_arm.n.01', 'name': 'rocker_arm'}, {'id': 10451, 'synset': 'rocket.n.02', 'name': 'rocket'}, {'id': 10452, 'synset': 'rocket.n.01', 'name': 'rocket'}, {'id': 10453, 'synset': 'rod.n.01', 'name': 'rod'}, {'id': 10454, 'synset': 'rodeo.n.02', 'name': 'rodeo'}, {'id': 10455, 'synset': 'roll.n.04', 'name': 'roll'}, {'id': 10456, 'synset': 'roller.n.04', 'name': 'roller'}, {'id': 10457, 'synset': 'roller.n.03', 'name': 'roller'}, {'id': 10458, 'synset': 'roller_bandage.n.01', 'name': 'roller_bandage'}, {'id': 10459, 'synset': 'in-line_skate.n.01', 'name': 'in-line_skate'}, {'id': 10460, 'synset': 'roller_blind.n.01', 'name': 'roller_blind'}, {'id': 10461, 'synset': 'roller_coaster.n.02', 'name': 'roller_coaster'}, {'id': 10462, 'synset': 'roller_towel.n.01', 'name': 'roller_towel'}, {'id': 10463, 'synset': 'roll_film.n.01', 'name': 'roll_film'}, {'id': 10464, 'synset': 'rolling_hitch.n.01', 'name': 'rolling_hitch'}, {'id': 10465, 'synset': 'rolling_mill.n.01', 'name': 'rolling_mill'}, {'id': 10466, 'synset': 'rolling_stock.n.01', 'name': 'rolling_stock'}, {'id': 10467, 'synset': 'roll-on.n.02', 'name': 'roll-on'}, {'id': 10468, 'synset': 'roll-on.n.01', 'name': 'roll-on'}, {'id': 10469, 'synset': 'roll-on_roll-off.n.01', 'name': 'roll-on_roll-off'}, {'id': 10470, 'synset': 'rolodex.n.01', 'name': 'Rolodex'}, {'id': 10471, 'synset': 'roman_arch.n.01', 'name': 'Roman_arch'}, {'id': 10472, 'synset': 'roman_building.n.01', 'name': 'Roman_building'}, {'id': 10473, 'synset': 'romper.n.02', 'name': 'romper'}, {'id': 10474, 'synset': 'rood_screen.n.01', 'name': 'rood_screen'}, {'id': 10475, 'synset': 'roof.n.01', 'name': 'roof'}, {'id': 10476, 'synset': 'roof.n.02', 'name': 'roof'}, {'id': 10477, 'synset': 'roofing.n.01', 'name': 'roofing'}, {'id': 10478, 'synset': 'room.n.01', 'name': 'room'}, {'id': 10479, 'synset': 'roomette.n.01', 'name': 'roomette'}, {'id': 10480, 'synset': 'room_light.n.01', 'name': 'room_light'}, {'id': 10481, 'synset': 'roost.n.01', 'name': 'roost'}, {'id': 10482, 'synset': 'rope.n.01', 'name': 'rope'}, {'id': 10483, 'synset': 'rope_bridge.n.01', 'name': 'rope_bridge'}, {'id': 10484, 'synset': 'rope_tow.n.01', 'name': 'rope_tow'}, {'id': 10485, 'synset': 'rose_water.n.01', 'name': 'rose_water'}, {'id': 10486, 'synset': 'rose_window.n.01', 'name': 'rose_window'}, {'id': 10487, 'synset': 'rosin_bag.n.01', 'name': 'rosin_bag'}, {'id': 10488, 'synset': 'rotary_actuator.n.01', 'name': 'rotary_actuator'}, {'id': 10489, 'synset': 'rotary_engine.n.01', 'name': 'rotary_engine'}, {'id': 10490, 'synset': 'rotary_press.n.01', 'name': 'rotary_press'}, {'id': 10491, 'synset': 'rotating_mechanism.n.01', 'name': 'rotating_mechanism'}, {'id': 10492, 'synset': 'rotating_shaft.n.01', 'name': 'rotating_shaft'}, {'id': 10493, 'synset': 'rotisserie.n.02', 'name': 'rotisserie'}, {'id': 10494, 'synset': 'rotisserie.n.01', 'name': 'rotisserie'}, {'id': 10495, 'synset': 'rotor.n.03', 'name': 'rotor'}, {'id': 10496, 'synset': 'rotor.n.01', 'name': 'rotor'}, {'id': 10497, 'synset': 'rotor.n.02', 'name': 'rotor'}, {'id': 10498, 'synset': 'rotor_blade.n.01', 'name': 'rotor_blade'}, {'id': 10499, 'synset': 'rotor_head.n.01', 'name': 'rotor_head'}, {'id': 10500, 'synset': 'rotunda.n.02', 'name': 'rotunda'}, {'id': 10501, 'synset': 'rotunda.n.01', 'name': 'rotunda'}, {'id': 10502, 'synset': 'rouge.n.01', 'name': 'rouge'}, {'id': 10503, 'synset': 'roughcast.n.02', 'name': 'roughcast'}, {'id': 10504, 'synset': 'rouleau.n.02', 'name': 'rouleau'}, {'id': 10505, 'synset': 'roulette.n.02', 'name': 'roulette'}, {'id': 10506, 'synset': 'roulette_ball.n.01', 'name': 'roulette_ball'}, {'id': 10507, 'synset': 'roulette_wheel.n.01', 'name': 'roulette_wheel'}, {'id': 10508, 'synset': 'round.n.01', 'name': 'round'}, {'id': 10509, 'synset': 'round_arch.n.01', 'name': 'round_arch'}, {'id': 10510, 'synset': 'round-bottom_flask.n.01', 'name': 'round-bottom_flask'}, {'id': 10511, 'synset': 'roundel.n.02', 'name': 'roundel'}, {'id': 10512, 'synset': 'round_file.n.01', 'name': 'round_file'}, {'id': 10513, 'synset': 'roundhouse.n.01', 'name': 'roundhouse'}, {'id': 10514, 'synset': 'router.n.03', 'name': 'router'}, {'id': 10515, 'synset': 'router_plane.n.01', 'name': 'router_plane'}, {'id': 10516, 'synset': 'rowel.n.01', 'name': 'rowel'}, {'id': 10517, 'synset': 'row_house.n.01', 'name': 'row_house'}, {'id': 10518, 'synset': 'rowing_boat.n.01', 'name': 'rowing_boat'}, {'id': 10519, 'synset': 'rowlock_arch.n.01', 'name': 'rowlock_arch'}, {'id': 10520, 'synset': 'royal.n.01', 'name': 'royal'}, {'id': 10521, 'synset': 'royal_mast.n.01', 'name': 'royal_mast'}, {'id': 10522, 'synset': 'rubber_boot.n.01', 'name': 'rubber_boot'}, {'id': 10523, 'synset': 'rubber_bullet.n.01', 'name': 'rubber_bullet'}, {'id': 10524, 'synset': 'rubber_eraser.n.01', 'name': 'rubber_eraser'}, {'id': 10525, 'synset': 'rudder.n.02', 'name': 'rudder'}, {'id': 10526, 'synset': 'rudder.n.01', 'name': 'rudder'}, {'id': 10527, 'synset': 'rudder_blade.n.01', 'name': 'rudder_blade'}, {'id': 10528, 'synset': 'rug.n.01', 'name': 'rug'}, {'id': 10529, 'synset': 'rugby_ball.n.01', 'name': 'rugby_ball'}, {'id': 10530, 'synset': 'ruin.n.02', 'name': 'ruin'}, {'id': 10531, 'synset': 'rule.n.12', 'name': 'rule'}, {'id': 10532, 'synset': 'rumble.n.02', 'name': 'rumble'}, {'id': 10533, 'synset': 'rumble_seat.n.01', 'name': 'rumble_seat'}, {'id': 10534, 'synset': 'rummer.n.01', 'name': 'rummer'}, {'id': 10535, 'synset': 'rumpus_room.n.01', 'name': 'rumpus_room'}, {'id': 10536, 'synset': 'runcible_spoon.n.01', 'name': 'runcible_spoon'}, {'id': 10537, 'synset': 'rundle.n.01', 'name': 'rundle'}, {'id': 10538, 'synset': 'running_shoe.n.01', 'name': 'running_shoe'}, {'id': 10539, 'synset': 'running_suit.n.01', 'name': 'running_suit'}, {'id': 10540, 'synset': 'runway.n.04', 'name': 'runway'}, {'id': 10541, 'synset': 'rushlight.n.01', 'name': 'rushlight'}, {'id': 10542, 'synset': 'russet.n.01', 'name': 'russet'}, {'id': 10543, 'synset': 'rya.n.01', 'name': 'rya'}, {'id': 10544, 'synset': 'saber.n.01', 'name': 'saber'}, {'id': 10545, 'synset': 'saber_saw.n.01', 'name': 'saber_saw'}, {'id': 10546, 'synset': 'sable.n.04', 'name': 'sable'}, {'id': 10547, 'synset': 'sable.n.01', 'name': 'sable'}, {'id': 10548, 'synset': 'sable_coat.n.01', 'name': 'sable_coat'}, {'id': 10549, 'synset': 'sabot.n.01', 'name': 'sabot'}, {'id': 10550, 'synset': 'sachet.n.01', 'name': 'sachet'}, {'id': 10551, 'synset': 'sack.n.05', 'name': 'sack'}, {'id': 10552, 'synset': 'sackbut.n.01', 'name': 'sackbut'}, {'id': 10553, 'synset': 'sackcloth.n.02', 'name': 'sackcloth'}, {'id': 10554, 'synset': 'sackcloth.n.01', 'name': 'sackcloth'}, {'id': 10555, 'synset': 'sack_coat.n.01', 'name': 'sack_coat'}, {'id': 10556, 'synset': 'sacking.n.01', 'name': 'sacking'}, {'id': 10557, 'synset': 'saddle_oxford.n.01', 'name': 'saddle_oxford'}, {'id': 10558, 'synset': 'saddlery.n.02', 'name': 'saddlery'}, {'id': 10559, 'synset': 'saddle_seat.n.01', 'name': 'saddle_seat'}, {'id': 10560, 'synset': 'saddle_stitch.n.01', 'name': 'saddle_stitch'}, {'id': 10561, 'synset': 'safe.n.01', 'name': 'safe'}, {'id': 10562, 'synset': 'safe.n.02', 'name': 'safe'}, {'id': 10563, 'synset': 'safe-deposit.n.01', 'name': 'safe-deposit'}, {'id': 10564, 'synset': 'safe_house.n.01', 'name': 'safe_house'}, {'id': 10565, 'synset': 'safety_arch.n.01', 'name': 'safety_arch'}, {'id': 10566, 'synset': 'safety_belt.n.01', 'name': 'safety_belt'}, {'id': 10567, 'synset': 'safety_bicycle.n.01', 'name': 'safety_bicycle'}, {'id': 10568, 'synset': 'safety_bolt.n.01', 'name': 'safety_bolt'}, {'id': 10569, 'synset': 'safety_curtain.n.01', 'name': 'safety_curtain'}, {'id': 10570, 'synset': 'safety_fuse.n.01', 'name': 'safety_fuse'}, {'id': 10571, 'synset': 'safety_lamp.n.01', 'name': 'safety_lamp'}, {'id': 10572, 'synset': 'safety_match.n.01', 'name': 'safety_match'}, {'id': 10573, 'synset': 'safety_net.n.02', 'name': 'safety_net'}, {'id': 10574, 'synset': 'safety_rail.n.01', 'name': 'safety_rail'}, {'id': 10575, 'synset': 'safety_razor.n.01', 'name': 'safety_razor'}, {'id': 10576, 'synset': 'safety_valve.n.01', 'name': 'safety_valve'}, {'id': 10577, 'synset': 'sail.n.03', 'name': 'sail'}, {'id': 10578, 'synset': 'sailboat.n.01', 'name': 'sailboat'}, {'id': 10579, 'synset': 'sailcloth.n.01', 'name': 'sailcloth'}, {'id': 10580, 'synset': 'sailing_vessel.n.01', 'name': 'sailing_vessel'}, {'id': 10581, 'synset': 'sailing_warship.n.01', 'name': 'sailing_warship'}, {'id': 10582, 'synset': 'sailor_cap.n.01', 'name': 'sailor_cap'}, {'id': 10583, 'synset': 'sailor_suit.n.01', 'name': 'sailor_suit'}, {'id': 10584, 'synset': 'salad_bar.n.01', 'name': 'salad_bar'}, {'id': 10585, 'synset': 'salad_bowl.n.02', 'name': 'salad_bowl'}, {'id': 10586, 'synset': 'salinometer.n.01', 'name': 'salinometer'}, {'id': 10587, 'synset': 'sallet.n.01', 'name': 'sallet'}, {'id': 10588, 'synset': 'salon.n.03', 'name': 'salon'}, {'id': 10589, 'synset': 'salon.n.01', 'name': 'salon'}, {'id': 10590, 'synset': 'salon.n.02', 'name': 'salon'}, {'id': 10591, 'synset': 'saltbox.n.01', 'name': 'saltbox'}, {'id': 10592, 'synset': 'saltcellar.n.01', 'name': 'saltcellar'}, {'id': 10593, 'synset': 'saltworks.n.01', 'name': 'saltworks'}, {'id': 10594, 'synset': 'salver.n.01', 'name': 'salver'}, {'id': 10595, 'synset': 'salwar.n.01', 'name': 'salwar'}, {'id': 10596, 'synset': 'sam_browne_belt.n.01', 'name': 'Sam_Browne_belt'}, {'id': 10597, 'synset': 'samisen.n.01', 'name': 'samisen'}, {'id': 10598, 'synset': 'samite.n.01', 'name': 'samite'}, {'id': 10599, 'synset': 'samovar.n.01', 'name': 'samovar'}, {'id': 10600, 'synset': 'sampan.n.01', 'name': 'sampan'}, {'id': 10601, 'synset': 'sandbag.n.01', 'name': 'sandbag'}, {'id': 10602, 'synset': 'sandblaster.n.01', 'name': 'sandblaster'}, {'id': 10603, 'synset': 'sandbox.n.01', 'name': 'sandbox'}, {'id': 10604, 'synset': 'sandglass.n.01', 'name': 'sandglass'}, {'id': 10605, 'synset': 'sand_wedge.n.01', 'name': 'sand_wedge'}, {'id': 10606, 'synset': 'sandwich_board.n.01', 'name': 'sandwich_board'}, {'id': 10607, 'synset': 'sanitary_napkin.n.01', 'name': 'sanitary_napkin'}, {'id': 10608, 'synset': 'cling_film.n.01', 'name': 'cling_film'}, {'id': 10609, 'synset': 'sarcenet.n.01', 'name': 'sarcenet'}, {'id': 10610, 'synset': 'sarcophagus.n.01', 'name': 'sarcophagus'}, {'id': 10611, 'synset': 'sari.n.01', 'name': 'sari'}, {'id': 10612, 'synset': 'sarong.n.01', 'name': 'sarong'}, {'id': 10613, 'synset': 'sash.n.01', 'name': 'sash'}, {'id': 10614, 'synset': 'sash_fastener.n.01', 'name': 'sash_fastener'}, {'id': 10615, 'synset': 'sash_window.n.01', 'name': 'sash_window'}, {'id': 10616, 'synset': 'sateen.n.01', 'name': 'sateen'}, {'id': 10617, 'synset': 'satellite.n.01', 'name': 'satellite'}, {'id': 10618, 'synset': 'satellite_receiver.n.01', 'name': 'satellite_receiver'}, {'id': 10619, 'synset': 'satellite_television.n.01', 'name': 'satellite_television'}, {'id': 10620, 'synset': 'satellite_transmitter.n.01', 'name': 'satellite_transmitter'}, {'id': 10621, 'synset': 'satin.n.01', 'name': 'satin'}, {'id': 10622, 'synset': 'saturday_night_special.n.01', 'name': 'Saturday_night_special'}, {'id': 10623, 'synset': 'saucepot.n.01', 'name': 'saucepot'}, {'id': 10624, 'synset': 'sauna.n.01', 'name': 'sauna'}, {'id': 10625, 'synset': 'savings_bank.n.02', 'name': 'savings_bank'}, {'id': 10626, 'synset': 'saw.n.02', 'name': 'saw'}, {'id': 10627, 'synset': 'sawed-off_shotgun.n.01', 'name': 'sawed-off_shotgun'}, {'id': 10628, 'synset': 'sawmill.n.01', 'name': 'sawmill'}, {'id': 10629, 'synset': 'saw_set.n.01', 'name': 'saw_set'}, {'id': 10630, 'synset': 'saxhorn.n.01', 'name': 'saxhorn'}, {'id': 10631, 'synset': 'scabbard.n.01', 'name': 'scabbard'}, {'id': 10632, 'synset': 'scaffolding.n.01', 'name': 'scaffolding'}, {'id': 10633, 'synset': 'scale.n.08', 'name': 'scale'}, {'id': 10634, 'synset': 'scaler.n.01', 'name': 'scaler'}, {'id': 10635, 'synset': 'scaling_ladder.n.01', 'name': 'scaling_ladder'}, {'id': 10636, 'synset': 'scalpel.n.01', 'name': 'scalpel'}, {'id': 10637, 'synset': 'scanner.n.04', 'name': 'scanner'}, {'id': 10638, 'synset': 'scanner.n.03', 'name': 'scanner'}, {'id': 10639, 'synset': 'scanner.n.02', 'name': 'scanner'}, {'id': 10640, 'synset': 'scantling.n.01', 'name': 'scantling'}, {'id': 10641, 'synset': 'scarf_joint.n.01', 'name': 'scarf_joint'}, {'id': 10642, 'synset': 'scatter_rug.n.01', 'name': 'scatter_rug'}, {'id': 10643, 'synset': 'scauper.n.01', 'name': 'scauper'}, {'id': 10644, 'synset': 'schmidt_telescope.n.01', 'name': 'Schmidt_telescope'}, {'id': 10645, 'synset': 'school.n.02', 'name': 'school'}, {'id': 10646, 'synset': 'schoolbag.n.01', 'name': 'schoolbag'}, {'id': 10647, 'synset': 'school_bell.n.01', 'name': 'school_bell'}, {'id': 10648, 'synset': 'school_ship.n.01', 'name': 'school_ship'}, {'id': 10649, 'synset': 'school_system.n.01', 'name': 'school_system'}, {'id': 10650, 'synset': 'schooner.n.02', 'name': 'schooner'}, {'id': 10651, 'synset': 'schooner.n.01', 'name': 'schooner'}, {'id': 10652, 'synset': 'scientific_instrument.n.01', 'name': 'scientific_instrument'}, {'id': 10653, 'synset': 'scimitar.n.01', 'name': 'scimitar'}, {'id': 10654, 'synset': 'scintillation_counter.n.01', 'name': 'scintillation_counter'}, {'id': 10655, 'synset': 'sclerometer.n.01', 'name': 'sclerometer'}, {'id': 10656, 'synset': 'scoinson_arch.n.01', 'name': 'scoinson_arch'}, {'id': 10657, 'synset': 'sconce.n.04', 'name': 'sconce'}, {'id': 10658, 'synset': 'sconce.n.03', 'name': 'sconce'}, {'id': 10659, 'synset': 'scoop.n.06', 'name': 'scoop'}, {'id': 10660, 'synset': 'scooter.n.02', 'name': 'scooter'}, {'id': 10661, 'synset': 'scouring_pad.n.01', 'name': 'scouring_pad'}, {'id': 10662, 'synset': 'scow.n.02', 'name': 'scow'}, {'id': 10663, 'synset': 'scow.n.01', 'name': 'scow'}, {'id': 10664, 'synset': 'scratcher.n.03', 'name': 'scratcher'}, {'id': 10665, 'synset': 'screen.n.05', 'name': 'screen'}, {'id': 10666, 'synset': 'screen.n.04', 'name': 'screen'}, {'id': 10667, 'synset': 'screen.n.09', 'name': 'screen'}, {'id': 10668, 'synset': 'screen.n.03', 'name': 'screen'}, {'id': 10669, 'synset': 'screen_door.n.01', 'name': 'screen_door'}, {'id': 10670, 'synset': 'screening.n.02', 'name': 'screening'}, {'id': 10671, 'synset': 'screw.n.04', 'name': 'screw'}, {'id': 10672, 'synset': 'screw.n.03', 'name': 'screw'}, {'id': 10673, 'synset': 'screw.n.02', 'name': 'screw'}, {'id': 10674, 'synset': 'screw_eye.n.01', 'name': 'screw_eye'}, {'id': 10675, 'synset': 'screw_key.n.01', 'name': 'screw_key'}, {'id': 10676, 'synset': 'screw_thread.n.01', 'name': 'screw_thread'}, {'id': 10677, 'synset': 'screwtop.n.01', 'name': 'screwtop'}, {'id': 10678, 'synset': 'screw_wrench.n.01', 'name': 'screw_wrench'}, {'id': 10679, 'synset': 'scriber.n.01', 'name': 'scriber'}, {'id': 10680, 'synset': 'scrim.n.01', 'name': 'scrim'}, {'id': 10681, 'synset': 'scrimshaw.n.01', 'name': 'scrimshaw'}, {'id': 10682, 'synset': 'scriptorium.n.01', 'name': 'scriptorium'}, {'id': 10683, 'synset': 'scrubber.n.03', 'name': 'scrubber'}, {'id': 10684, 'synset': 'scrub_plane.n.01', 'name': 'scrub_plane'}, {'id': 10685, 'synset': 'scuffer.n.01', 'name': 'scuffer'}, {'id': 10686, 'synset': 'scuffle.n.02', 'name': 'scuffle'}, {'id': 10687, 'synset': 'scull.n.02', 'name': 'scull'}, {'id': 10688, 'synset': 'scull.n.01', 'name': 'scull'}, {'id': 10689, 'synset': 'scullery.n.01', 'name': 'scullery'}, {'id': 10690, 'synset': 'scuttle.n.01', 'name': 'scuttle'}, {'id': 10691, 'synset': 'scyphus.n.01', 'name': 'scyphus'}, {'id': 10692, 'synset': 'scythe.n.01', 'name': 'scythe'}, {'id': 10693, 'synset': 'seabag.n.01', 'name': 'seabag'}, {'id': 10694, 'synset': 'sea_boat.n.01', 'name': 'sea_boat'}, {'id': 10695, 'synset': 'sea_chest.n.01', 'name': 'sea_chest'}, {'id': 10696, 'synset': 'sealing_wax.n.01', 'name': 'sealing_wax'}, {'id': 10697, 'synset': 'sealskin.n.02', 'name': 'sealskin'}, {'id': 10698, 'synset': 'seam.n.01', 'name': 'seam'}, {'id': 10699, 'synset': 'searchlight.n.01', 'name': 'searchlight'}, {'id': 10700, 'synset': 'searing_iron.n.01', 'name': 'searing_iron'}, {'id': 10701, 'synset': 'seat.n.04', 'name': 'seat'}, {'id': 10702, 'synset': 'seat.n.03', 'name': 'seat'}, {'id': 10703, 'synset': 'seat.n.09', 'name': 'seat'}, {'id': 10704, 'synset': 'seat_belt.n.01', 'name': 'seat_belt'}, {'id': 10705, 'synset': 'secateurs.n.01', 'name': 'secateurs'}, {'id': 10706, 'synset': 'secondary_coil.n.01', 'name': 'secondary_coil'}, {'id': 10707, 'synset': 'second_balcony.n.01', 'name': 'second_balcony'}, {'id': 10708, 'synset': 'second_base.n.01', 'name': 'second_base'}, {'id': 10709, 'synset': 'second_hand.n.02', 'name': 'second_hand'}, {'id': 10710, 'synset': 'secretary.n.04', 'name': 'secretary'}, {'id': 10711, 'synset': 'sectional.n.01', 'name': 'sectional'}, {'id': 10712, 'synset': 'security_blanket.n.02', 'name': 'security_blanket'}, {'id': 10713, 'synset': 'security_system.n.02', 'name': 'security_system'}, {'id': 10714, 'synset': 'security_system.n.01', 'name': 'security_system'}, {'id': 10715, 'synset': 'sedan.n.01', 'name': 'sedan'}, {'id': 10716, 'synset': 'sedan.n.02', 'name': 'sedan'}, {'id': 10717, 'synset': 'seeder.n.02', 'name': 'seeder'}, {'id': 10718, 'synset': 'seeker.n.02', 'name': 'seeker'}, {'id': 10719, 'synset': 'seersucker.n.01', 'name': 'seersucker'}, {'id': 10720, 'synset': 'segmental_arch.n.01', 'name': 'segmental_arch'}, {'id': 10721, 'synset': 'segway.n.01', 'name': 'Segway'}, {'id': 10722, 'synset': 'seidel.n.01', 'name': 'seidel'}, {'id': 10723, 'synset': 'seine.n.02', 'name': 'seine'}, {'id': 10724, 'synset': 'seismograph.n.01', 'name': 'seismograph'}, {'id': 10725, 'synset': 'selector.n.02', 'name': 'selector'}, {'id': 10726, 'synset': 'selenium_cell.n.01', 'name': 'selenium_cell'}, {'id': 10727, 'synset': 'self-propelled_vehicle.n.01', 'name': 'self-propelled_vehicle'}, {'id': 10728, 'synset': 'self-registering_thermometer.n.01', 'name': 'self-registering_thermometer'}, {'id': 10729, 'synset': 'self-starter.n.02', 'name': 'self-starter'}, {'id': 10730, 'synset': 'selsyn.n.01', 'name': 'selsyn'}, {'id': 10731, 'synset': 'selvage.n.02', 'name': 'selvage'}, {'id': 10732, 'synset': 'semaphore.n.01', 'name': 'semaphore'}, {'id': 10733, 'synset': 'semiautomatic_firearm.n.01', 'name': 'semiautomatic_firearm'}, {'id': 10734, 'synset': 'semiautomatic_pistol.n.01', 'name': 'semiautomatic_pistol'}, {'id': 10735, 'synset': 'semiconductor_device.n.01', 'name': 'semiconductor_device'}, {'id': 10736, 'synset': 'semi-detached_house.n.01', 'name': 'semi-detached_house'}, {'id': 10737, 'synset': 'semigloss.n.01', 'name': 'semigloss'}, {'id': 10738, 'synset': 'semitrailer.n.01', 'name': 'semitrailer'}, {'id': 10739, 'synset': 'sennit.n.01', 'name': 'sennit'}, {'id': 10740, 'synset': 'sensitometer.n.01', 'name': 'sensitometer'}, {'id': 10741, 'synset': 'sentry_box.n.01', 'name': 'sentry_box'}, {'id': 10742, 'synset': 'separate.n.02', 'name': 'separate'}, {'id': 10743, 'synset': 'septic_tank.n.01', 'name': 'septic_tank'}, {'id': 10744, 'synset': 'sequence.n.03', 'name': 'sequence'}, {'id': 10745, 'synset': 'sequencer.n.01', 'name': 'sequencer'}, {'id': 10746, 'synset': 'serape.n.01', 'name': 'serape'}, {'id': 10747, 'synset': 'serge.n.01', 'name': 'serge'}, {'id': 10748, 'synset': 'serger.n.01', 'name': 'serger'}, {'id': 10749, 'synset': 'serial_port.n.01', 'name': 'serial_port'}, {'id': 10750, 'synset': 'serpent.n.03', 'name': 'serpent'}, {'id': 10751, 'synset': 'serration.n.03', 'name': 'serration'}, {'id': 10752, 'synset': 'server.n.04', 'name': 'server'}, {'id': 10753, 'synset': 'server.n.03', 'name': 'server'}, {'id': 10754, 'synset': 'service_club.n.02', 'name': 'service_club'}, {'id': 10755, 'synset': 'serving_cart.n.01', 'name': 'serving_cart'}, {'id': 10756, 'synset': 'serving_dish.n.01', 'name': 'serving_dish'}, {'id': 10757, 'synset': 'servo.n.01', 'name': 'servo'}, {'id': 10758, 'synset': 'set.n.13', 'name': 'set'}, {'id': 10759, 'synset': 'set_gun.n.01', 'name': 'set_gun'}, {'id': 10760, 'synset': 'setscrew.n.02', 'name': 'setscrew'}, {'id': 10761, 'synset': 'setscrew.n.01', 'name': 'setscrew'}, {'id': 10762, 'synset': 'set_square.n.01', 'name': 'set_square'}, {'id': 10763, 'synset': 'settee.n.02', 'name': 'settee'}, {'id': 10764, 'synset': 'settle.n.01', 'name': 'settle'}, {'id': 10765, 'synset': 'settlement_house.n.01', 'name': 'settlement_house'}, {'id': 10766, 'synset': 'seventy-eight.n.02', 'name': 'seventy-eight'}, {'id': 10767, 'synset': 'seven_wonders_of_the_ancient_world.n.01', 'name': 'Seven_Wonders_of_the_Ancient_World'}, {'id': 10768, 'synset': 'sewage_disposal_plant.n.01', 'name': 'sewage_disposal_plant'}, {'id': 10769, 'synset': 'sewer.n.01', 'name': 'sewer'}, {'id': 10770, 'synset': 'sewing_basket.n.01', 'name': 'sewing_basket'}, {'id': 10771, 'synset': 'sewing_kit.n.01', 'name': 'sewing_kit'}, {'id': 10772, 'synset': 'sewing_needle.n.01', 'name': 'sewing_needle'}, {'id': 10773, 'synset': 'sewing_room.n.01', 'name': 'sewing_room'}, {'id': 10774, 'synset': 'sextant.n.02', 'name': 'sextant'}, {'id': 10775, 'synset': 'sgraffito.n.01', 'name': 'sgraffito'}, {'id': 10776, 'synset': 'shackle.n.01', 'name': 'shackle'}, {'id': 10777, 'synset': 'shackle.n.02', 'name': 'shackle'}, {'id': 10778, 'synset': 'shade.n.03', 'name': 'shade'}, {'id': 10779, 'synset': 'shadow_box.n.01', 'name': 'shadow_box'}, {'id': 10780, 'synset': 'shaft.n.03', 'name': 'shaft'}, {'id': 10781, 'synset': 'shag_rug.n.01', 'name': 'shag_rug'}, {'id': 10782, 'synset': 'shank.n.04', 'name': 'shank'}, {'id': 10783, 'synset': 'shank.n.03', 'name': 'shank'}, {'id': 10784, 'synset': 'shantung.n.01', 'name': 'shantung'}, {'id': 10785, 'synset': 'shaper.n.02', 'name': 'shaper'}, {'id': 10786, 'synset': 'shaping_tool.n.01', 'name': 'shaping_tool'}, {'id': 10787, 'synset': 'sharkskin.n.01', 'name': 'sharkskin'}, {'id': 10788, 'synset': 'shaving_brush.n.01', 'name': 'shaving_brush'}, {'id': 10789, 'synset': 'shaving_foam.n.01', 'name': 'shaving_foam'}, {'id': 10790, 'synset': 'shawm.n.01', 'name': 'shawm'}, {'id': 10791, 'synset': 'sheath.n.01', 'name': 'sheath'}, {'id': 10792, 'synset': 'sheathing.n.01', 'name': 'sheathing'}, {'id': 10793, 'synset': 'shed.n.01', 'name': 'shed'}, {'id': 10794, 'synset': 'sheep_bell.n.01', 'name': 'sheep_bell'}, {'id': 10795, 'synset': 'sheepshank.n.01', 'name': 'sheepshank'}, {'id': 10796, 'synset': 'sheepskin_coat.n.01', 'name': 'sheepskin_coat'}, {'id': 10797, 'synset': 'sheepwalk.n.01', 'name': 'sheepwalk'}, {'id': 10798, 'synset': 'sheet.n.03', 'name': 'sheet'}, {'id': 10799, 'synset': 'sheet_bend.n.01', 'name': 'sheet_bend'}, {'id': 10800, 'synset': 'sheeting.n.01', 'name': 'sheeting'}, {'id': 10801, 'synset': 'sheet_pile.n.01', 'name': 'sheet_pile'}, {'id': 10802, 'synset': 'sheetrock.n.01', 'name': 'Sheetrock'}, {'id': 10803, 'synset': 'shelf.n.01', 'name': 'shelf'}, {'id': 10804, 'synset': 'shelf_bracket.n.01', 'name': 'shelf_bracket'}, {'id': 10805, 'synset': 'shell.n.01', 'name': 'shell'}, {'id': 10806, 'synset': 'shell.n.08', 'name': 'shell'}, {'id': 10807, 'synset': 'shell.n.07', 'name': 'shell'}, {'id': 10808, 'synset': 'shellac.n.02', 'name': 'shellac'}, {'id': 10809, 'synset': 'shelter.n.01', 'name': 'shelter'}, {'id': 10810, 'synset': 'shelter.n.02', 'name': 'shelter'}, {'id': 10811, 'synset': 'shelter.n.05', 'name': 'shelter'}, {'id': 10812, 'synset': 'sheltered_workshop.n.01', 'name': 'sheltered_workshop'}, {'id': 10813, 'synset': 'sheraton.n.01', 'name': 'Sheraton'}, {'id': 10814, 'synset': 'shield.n.01', 'name': 'shield'}, {'id': 10815, 'synset': 'shielding.n.03', 'name': 'shielding'}, {'id': 10816, 'synset': 'shift_key.n.01', 'name': 'shift_key'}, {'id': 10817, 'synset': 'shillelagh.n.01', 'name': 'shillelagh'}, {'id': 10818, 'synset': 'shim.n.01', 'name': 'shim'}, {'id': 10819, 'synset': 'shingle.n.03', 'name': 'shingle'}, {'id': 10820, 'synset': 'shin_guard.n.01', 'name': 'shin_guard'}, {'id': 10821, 'synset': 'ship.n.01', 'name': 'ship'}, {'id': 10822, 'synset': 'shipboard_system.n.01', 'name': 'shipboard_system'}, {'id': 10823, 'synset': 'shipping.n.02', 'name': 'shipping'}, {'id': 10824, 'synset': 'shipping_room.n.01', 'name': 'shipping_room'}, {'id': 10825, 'synset': 'ship-towed_long-range_acoustic_detection_system.n.01', 'name': 'ship-towed_long-range_acoustic_detection_system'}, {'id': 10826, 'synset': 'shipwreck.n.01', 'name': 'shipwreck'}, {'id': 10827, 'synset': 'shirt_button.n.01', 'name': 'shirt_button'}, {'id': 10828, 'synset': 'shirtdress.n.01', 'name': 'shirtdress'}, {'id': 10829, 'synset': 'shirtfront.n.01', 'name': 'shirtfront'}, {'id': 10830, 'synset': 'shirting.n.01', 'name': 'shirting'}, {'id': 10831, 'synset': 'shirtsleeve.n.01', 'name': 'shirtsleeve'}, {'id': 10832, 'synset': 'shirttail.n.02', 'name': 'shirttail'}, {'id': 10833, 'synset': 'shirtwaist.n.01', 'name': 'shirtwaist'}, {'id': 10834, 'synset': 'shiv.n.01', 'name': 'shiv'}, {'id': 10835, 'synset': 'shock_absorber.n.01', 'name': 'shock_absorber'}, {'id': 10836, 'synset': 'shoe.n.02', 'name': 'shoe'}, {'id': 10837, 'synset': 'shoebox.n.02', 'name': 'shoebox'}, {'id': 10838, 'synset': 'shoehorn.n.01', 'name': 'shoehorn'}, {'id': 10839, 'synset': 'shoe_shop.n.01', 'name': 'shoe_shop'}, {'id': 10840, 'synset': 'shoetree.n.01', 'name': 'shoetree'}, {'id': 10841, 'synset': 'shofar.n.01', 'name': 'shofar'}, {'id': 10842, 'synset': 'shoji.n.01', 'name': 'shoji'}, {'id': 10843, 'synset': 'shooting_brake.n.01', 'name': 'shooting_brake'}, {'id': 10844, 'synset': 'shooting_lodge.n.01', 'name': 'shooting_lodge'}, {'id': 10845, 'synset': 'shooting_stick.n.01', 'name': 'shooting_stick'}, {'id': 10846, 'synset': 'shop.n.01', 'name': 'shop'}, {'id': 10847, 'synset': 'shop_bell.n.01', 'name': 'shop_bell'}, {'id': 10848, 'synset': 'shopping_basket.n.01', 'name': 'shopping_basket'}, {'id': 10849, 'synset': 'short_circuit.n.01', 'name': 'short_circuit'}, {'id': 10850, 'synset': 'short_iron.n.01', 'name': 'short_iron'}, {'id': 10851, 'synset': 'short_sleeve.n.01', 'name': 'short_sleeve'}, {'id': 10852, 'synset': 'shortwave_diathermy_machine.n.01', 'name': 'shortwave_diathermy_machine'}, {'id': 10853, 'synset': 'shot.n.12', 'name': 'shot'}, {'id': 10854, 'synset': 'shotgun.n.01', 'name': 'shotgun'}, {'id': 10855, 'synset': 'shotgun_shell.n.01', 'name': 'shotgun_shell'}, {'id': 10856, 'synset': 'shot_tower.n.01', 'name': 'shot_tower'}, {'id': 10857, 'synset': 'shoulder.n.04', 'name': 'shoulder'}, {'id': 10858, 'synset': 'shouldered_arch.n.01', 'name': 'shouldered_arch'}, {'id': 10859, 'synset': 'shoulder_holster.n.01', 'name': 'shoulder_holster'}, {'id': 10860, 'synset': 'shoulder_pad.n.01', 'name': 'shoulder_pad'}, {'id': 10861, 'synset': 'shoulder_patch.n.01', 'name': 'shoulder_patch'}, {'id': 10862, 'synset': 'shovel.n.03', 'name': 'shovel'}, {'id': 10863, 'synset': 'shovel_hat.n.01', 'name': 'shovel_hat'}, {'id': 10864, 'synset': 'showboat.n.01', 'name': 'showboat'}, {'id': 10865, 'synset': 'shower_room.n.01', 'name': 'shower_room'}, {'id': 10866, 'synset': 'shower_stall.n.01', 'name': 'shower_stall'}, {'id': 10867, 'synset': 'showroom.n.01', 'name': 'showroom'}, {'id': 10868, 'synset': 'shrapnel.n.01', 'name': 'shrapnel'}, {'id': 10869, 'synset': 'shrimper.n.01', 'name': 'shrimper'}, {'id': 10870, 'synset': 'shrine.n.01', 'name': 'shrine'}, {'id': 10871, 'synset': 'shrink-wrap.n.01', 'name': 'shrink-wrap'}, {'id': 10872, 'synset': 'shunt.n.03', 'name': 'shunt'}, {'id': 10873, 'synset': 'shunt.n.02', 'name': 'shunt'}, {'id': 10874, 'synset': 'shunter.n.01', 'name': 'shunter'}, {'id': 10875, 'synset': 'shutter.n.02', 'name': 'shutter'}, {'id': 10876, 'synset': 'shutter.n.01', 'name': 'shutter'}, {'id': 10877, 'synset': 'shuttle.n.03', 'name': 'shuttle'}, {'id': 10878, 'synset': 'shuttle.n.02', 'name': 'shuttle'}, {'id': 10879, 'synset': 'shuttle_bus.n.01', 'name': 'shuttle_bus'}, {'id': 10880, 'synset': 'shuttlecock.n.01', 'name': 'shuttlecock'}, {'id': 10881, 'synset': 'shuttle_helicopter.n.01', 'name': 'shuttle_helicopter'}, {'id': 10882, 'synset': 'sibley_tent.n.01', 'name': 'Sibley_tent'}, {'id': 10883, 'synset': 'sickbay.n.01', 'name': 'sickbay'}, {'id': 10884, 'synset': 'sickbed.n.01', 'name': 'sickbed'}, {'id': 10885, 'synset': 'sickle.n.01', 'name': 'sickle'}, {'id': 10886, 'synset': 'sickroom.n.01', 'name': 'sickroom'}, {'id': 10887, 'synset': 'sideboard.n.02', 'name': 'sideboard'}, {'id': 10888, 'synset': 'sidecar.n.02', 'name': 'sidecar'}, {'id': 10889, 'synset': 'side_chapel.n.01', 'name': 'side_chapel'}, {'id': 10890, 'synset': 'sidelight.n.01', 'name': 'sidelight'}, {'id': 10891, 'synset': 'sidesaddle.n.01', 'name': 'sidesaddle'}, {'id': 10892, 'synset': 'sidewalk.n.01', 'name': 'sidewalk'}, {'id': 10893, 'synset': 'sidewall.n.02', 'name': 'sidewall'}, {'id': 10894, 'synset': 'side-wheeler.n.01', 'name': 'side-wheeler'}, {'id': 10895, 'synset': 'sidewinder.n.02', 'name': 'sidewinder'}, {'id': 10896, 'synset': 'sieve.n.01', 'name': 'sieve'}, {'id': 10897, 'synset': 'sifter.n.01', 'name': 'sifter'}, {'id': 10898, 'synset': 'sights.n.01', 'name': 'sights'}, {'id': 10899, 'synset': 'sigmoidoscope.n.01', 'name': 'sigmoidoscope'}, {'id': 10900, 'synset': 'signal_box.n.01', 'name': 'signal_box'}, {'id': 10901, 'synset': 'signaling_device.n.01', 'name': 'signaling_device'}, {'id': 10902, 'synset': 'silencer.n.02', 'name': 'silencer'}, {'id': 10903, 'synset': 'silent_butler.n.01', 'name': 'silent_butler'}, {'id': 10904, 'synset': 'silex.n.02', 'name': 'Silex'}, {'id': 10905, 'synset': 'silk.n.01', 'name': 'silk'}, {'id': 10906, 'synset': 'silks.n.01', 'name': 'silks'}, {'id': 10907, 'synset': 'silver_plate.n.02', 'name': 'silver_plate'}, {'id': 10908, 'synset': 'silverpoint.n.01', 'name': 'silverpoint'}, {'id': 10909, 'synset': 'simple_pendulum.n.01', 'name': 'simple_pendulum'}, {'id': 10910, 'synset': 'simulator.n.01', 'name': 'simulator'}, {'id': 10911, 'synset': 'single_bed.n.01', 'name': 'single_bed'}, {'id': 10912, 'synset': 'single-breasted_jacket.n.01', 'name': 'single-breasted_jacket'}, {'id': 10913, 'synset': 'single-breasted_suit.n.01', 'name': 'single-breasted_suit'}, {'id': 10914, 'synset': 'single_prop.n.01', 'name': 'single_prop'}, {'id': 10915, 'synset': 'single-reed_instrument.n.01', 'name': 'single-reed_instrument'}, {'id': 10916, 'synset': 'single-rotor_helicopter.n.01', 'name': 'single-rotor_helicopter'}, {'id': 10917, 'synset': 'singlestick.n.01', 'name': 'singlestick'}, {'id': 10918, 'synset': 'singlet.n.01', 'name': 'singlet'}, {'id': 10919, 'synset': 'siren.n.04', 'name': 'siren'}, {'id': 10920, 'synset': 'sister_ship.n.01', 'name': 'sister_ship'}, {'id': 10921, 'synset': 'sitar.n.01', 'name': 'sitar'}, {'id': 10922, 'synset': 'sitz_bath.n.01', 'name': 'sitz_bath'}, {'id': 10923, 'synset': 'six-pack.n.01', 'name': 'six-pack'}, {'id': 10924, 'synset': 'skate.n.01', 'name': 'skate'}, {'id': 10925, 'synset': 'skeg.n.01', 'name': 'skeg'}, {'id': 10926, 'synset': 'skein.n.01', 'name': 'skein'}, {'id': 10927, 'synset': 'skeleton.n.04', 'name': 'skeleton'}, {'id': 10928, 'synset': 'skeleton_key.n.01', 'name': 'skeleton_key'}, {'id': 10929, 'synset': 'skep.n.02', 'name': 'skep'}, {'id': 10930, 'synset': 'skep.n.01', 'name': 'skep'}, {'id': 10931, 'synset': 'sketch.n.01', 'name': 'sketch'}, {'id': 10932, 'synset': 'sketcher.n.02', 'name': 'sketcher'}, {'id': 10933, 'synset': 'skew_arch.n.01', 'name': 'skew_arch'}, {'id': 10934, 'synset': 'ski_binding.n.01', 'name': 'ski_binding'}, {'id': 10935, 'synset': 'skibob.n.01', 'name': 'skibob'}, {'id': 10936, 'synset': 'ski_cap.n.01', 'name': 'ski_cap'}, {'id': 10937, 'synset': 'skidder.n.03', 'name': 'skidder'}, {'id': 10938, 'synset': 'skid_lid.n.01', 'name': 'skid_lid'}, {'id': 10939, 'synset': 'skiff.n.01', 'name': 'skiff'}, {'id': 10940, 'synset': 'ski_jump.n.01', 'name': 'ski_jump'}, {'id': 10941, 'synset': 'ski_lodge.n.01', 'name': 'ski_lodge'}, {'id': 10942, 'synset': 'ski_mask.n.01', 'name': 'ski_mask'}, {'id': 10943, 'synset': 'skimmer.n.02', 'name': 'skimmer'}, {'id': 10944, 'synset': 'ski-plane.n.01', 'name': 'ski-plane'}, {'id': 10945, 'synset': 'ski_rack.n.01', 'name': 'ski_rack'}, {'id': 10946, 'synset': 'skirt.n.01', 'name': 'skirt'}, {'id': 10947, 'synset': 'ski_tow.n.01', 'name': 'ski_tow'}, {'id': 10948, 'synset': 'skivvies.n.01', 'name': 'Skivvies'}, {'id': 10949, 'synset': 'skybox.n.01', 'name': 'skybox'}, {'id': 10950, 'synset': 'skyhook.n.02', 'name': 'skyhook'}, {'id': 10951, 'synset': 'skylight.n.01', 'name': 'skylight'}, {'id': 10952, 'synset': 'skysail.n.01', 'name': 'skysail'}, {'id': 10953, 'synset': 'skyscraper.n.01', 'name': 'skyscraper'}, {'id': 10954, 'synset': 'skywalk.n.01', 'name': 'skywalk'}, {'id': 10955, 'synset': 'slacks.n.01', 'name': 'slacks'}, {'id': 10956, 'synset': 'slack_suit.n.01', 'name': 'slack_suit'}, {'id': 10957, 'synset': 'slasher.n.02', 'name': 'slasher'}, {'id': 10958, 'synset': 'slash_pocket.n.01', 'name': 'slash_pocket'}, {'id': 10959, 'synset': 'slat.n.01', 'name': 'slat'}, {'id': 10960, 'synset': 'slate.n.01', 'name': 'slate'}, {'id': 10961, 'synset': 'slate_pencil.n.01', 'name': 'slate_pencil'}, {'id': 10962, 'synset': 'slate_roof.n.01', 'name': 'slate_roof'}, {'id': 10963, 'synset': 'sleeper.n.07', 'name': 'sleeper'}, {'id': 10964, 'synset': 'sleeper.n.06', 'name': 'sleeper'}, {'id': 10965, 'synset': 'sleeping_car.n.01', 'name': 'sleeping_car'}, {'id': 10966, 'synset': 'sleeve.n.01', 'name': 'sleeve'}, {'id': 10967, 'synset': 'sleeve.n.02', 'name': 'sleeve'}, {'id': 10968, 'synset': 'sleigh_bed.n.01', 'name': 'sleigh_bed'}, {'id': 10969, 'synset': 'sleigh_bell.n.01', 'name': 'sleigh_bell'}, {'id': 10970, 'synset': 'slice_bar.n.01', 'name': 'slice_bar'}, {'id': 10971, 'synset': 'slicer.n.03', 'name': 'slicer'}, {'id': 10972, 'synset': 'slicer.n.02', 'name': 'slicer'}, {'id': 10973, 'synset': 'slide.n.04', 'name': 'slide'}, {'id': 10974, 'synset': 'slide_fastener.n.01', 'name': 'slide_fastener'}, {'id': 10975, 'synset': 'slide_projector.n.01', 'name': 'slide_projector'}, {'id': 10976, 'synset': 'slide_rule.n.01', 'name': 'slide_rule'}, {'id': 10977, 'synset': 'slide_valve.n.01', 'name': 'slide_valve'}, {'id': 10978, 'synset': 'sliding_door.n.01', 'name': 'sliding_door'}, {'id': 10979, 'synset': 'sliding_seat.n.01', 'name': 'sliding_seat'}, {'id': 10980, 'synset': 'sliding_window.n.01', 'name': 'sliding_window'}, {'id': 10981, 'synset': 'sling.n.04', 'name': 'sling'}, {'id': 10982, 'synset': 'slingback.n.01', 'name': 'slingback'}, {'id': 10983, 'synset': 'slinger_ring.n.01', 'name': 'slinger_ring'}, {'id': 10984, 'synset': 'slip_clutch.n.01', 'name': 'slip_clutch'}, {'id': 10985, 'synset': 'slipcover.n.01', 'name': 'slipcover'}, {'id': 10986, 'synset': 'slip-joint_pliers.n.01', 'name': 'slip-joint_pliers'}, {'id': 10987, 'synset': 'slipknot.n.01', 'name': 'slipknot'}, {'id': 10988, 'synset': 'slip-on.n.01', 'name': 'slip-on'}, {'id': 10989, 'synset': 'slip_ring.n.01', 'name': 'slip_ring'}, {'id': 10990, 'synset': 'slit_lamp.n.01', 'name': 'slit_lamp'}, {'id': 10991, 'synset': 'slit_trench.n.01', 'name': 'slit_trench'}, {'id': 10992, 'synset': 'sloop.n.01', 'name': 'sloop'}, {'id': 10993, 'synset': 'sloop_of_war.n.01', 'name': 'sloop_of_war'}, {'id': 10994, 'synset': 'slop_basin.n.01', 'name': 'slop_basin'}, {'id': 10995, 'synset': 'slop_pail.n.01', 'name': 'slop_pail'}, {'id': 10996, 'synset': 'slops.n.02', 'name': 'slops'}, {'id': 10997, 'synset': 'slopshop.n.01', 'name': 'slopshop'}, {'id': 10998, 'synset': 'slot.n.07', 'name': 'slot'}, {'id': 10999, 'synset': 'slot_machine.n.01', 'name': 'slot_machine'}, {'id': 11000, 'synset': 'sluice.n.01', 'name': 'sluice'}, {'id': 11001, 'synset': 'smack.n.03', 'name': 'smack'}, {'id': 11002, 'synset': 'small_boat.n.01', 'name': 'small_boat'}, {'id': 11003, 'synset': 'small_computer_system_interface.n.01', 'name': 'small_computer_system_interface'}, {'id': 11004, 'synset': 'small_ship.n.01', 'name': 'small_ship'}, {'id': 11005, 'synset': 'small_stores.n.01', 'name': 'small_stores'}, {'id': 11006, 'synset': 'smart_bomb.n.01', 'name': 'smart_bomb'}, {'id': 11007, 'synset': 'smelling_bottle.n.01', 'name': 'smelling_bottle'}, {'id': 11008, 'synset': 'smocking.n.01', 'name': 'smocking'}, {'id': 11009, 'synset': 'smoke_bomb.n.01', 'name': 'smoke_bomb'}, {'id': 11010, 'synset': 'smokehouse.n.01', 'name': 'smokehouse'}, {'id': 11011, 'synset': 'smoker.n.03', 'name': 'smoker'}, {'id': 11012, 'synset': 'smoke_screen.n.01', 'name': 'smoke_screen'}, {'id': 11013, 'synset': 'smoking_room.n.01', 'name': 'smoking_room'}, {'id': 11014, 'synset': 'smoothbore.n.01', 'name': 'smoothbore'}, {'id': 11015, 'synset': 'smooth_plane.n.01', 'name': 'smooth_plane'}, {'id': 11016, 'synset': 'snack_bar.n.01', 'name': 'snack_bar'}, {'id': 11017, 'synset': 'snaffle.n.01', 'name': 'snaffle'}, {'id': 11018, 'synset': 'snap.n.10', 'name': 'snap'}, {'id': 11019, 'synset': 'snap_brim.n.01', 'name': 'snap_brim'}, {'id': 11020, 'synset': 'snap-brim_hat.n.01', 'name': 'snap-brim_hat'}, {'id': 11021, 'synset': 'snare.n.05', 'name': 'snare'}, {'id': 11022, 'synset': 'snare_drum.n.01', 'name': 'snare_drum'}, {'id': 11023, 'synset': 'snatch_block.n.01', 'name': 'snatch_block'}, {'id': 11024, 'synset': 'snifter.n.01', 'name': 'snifter'}, {'id': 11025, 'synset': 'sniper_rifle.n.01', 'name': 'sniper_rifle'}, {'id': 11026, 'synset': 'snips.n.01', 'name': 'snips'}, {'id': 11027, 'synset': 'sno-cat.n.01', 'name': 'Sno-cat'}, {'id': 11028, 'synset': 'snood.n.01', 'name': 'snood'}, {'id': 11029, 'synset': 'snorkel.n.02', 'name': 'snorkel'}, {'id': 11030, 'synset': 'snorkel.n.01', 'name': 'snorkel'}, {'id': 11031, 'synset': 'snowbank.n.01', 'name': 'snowbank'}, {'id': 11032, 'synset': 'snowplow.n.01', 'name': 'snowplow'}, {'id': 11033, 'synset': 'snowshoe.n.01', 'name': 'snowshoe'}, {'id': 11034, 'synset': 'snowsuit.n.01', 'name': 'snowsuit'}, {'id': 11035, 'synset': 'snow_thrower.n.01', 'name': 'snow_thrower'}, {'id': 11036, 'synset': 'snuffbox.n.01', 'name': 'snuffbox'}, {'id': 11037, 'synset': 'snuffer.n.01', 'name': 'snuffer'}, {'id': 11038, 'synset': 'snuffers.n.01', 'name': 'snuffers'}, {'id': 11039, 'synset': 'soapbox.n.01', 'name': 'soapbox'}, {'id': 11040, 'synset': 'soap_dish.n.01', 'name': 'soap_dish'}, {'id': 11041, 'synset': 'soap_dispenser.n.01', 'name': 'soap_dispenser'}, {'id': 11042, 'synset': 'soap_pad.n.01', 'name': 'soap_pad'}, {'id': 11043, 'synset': 'socket.n.02', 'name': 'socket'}, {'id': 11044, 'synset': 'socket_wrench.n.01', 'name': 'socket_wrench'}, {'id': 11045, 'synset': 'socle.n.01', 'name': 'socle'}, {'id': 11046, 'synset': 'soda_can.n.01', 'name': 'soda_can'}, {'id': 11047, 'synset': 'soda_fountain.n.02', 'name': 'soda_fountain'}, {'id': 11048, 'synset': 'soda_fountain.n.01', 'name': 'soda_fountain'}, {'id': 11049, 'synset': 'sod_house.n.01', 'name': 'sod_house'}, {'id': 11050, 'synset': 'sodium-vapor_lamp.n.01', 'name': 'sodium-vapor_lamp'}, {'id': 11051, 'synset': 'soffit.n.01', 'name': 'soffit'}, {'id': 11052, 'synset': 'soft_pedal.n.01', 'name': 'soft_pedal'}, {'id': 11053, 'synset': 'soil_pipe.n.01', 'name': 'soil_pipe'}, {'id': 11054, 'synset': 'solar_cell.n.01', 'name': 'solar_cell'}, {'id': 11055, 'synset': 'solar_dish.n.01', 'name': 'solar_dish'}, {'id': 11056, 'synset': 'solar_heater.n.01', 'name': 'solar_heater'}, {'id': 11057, 'synset': 'solar_house.n.01', 'name': 'solar_house'}, {'id': 11058, 'synset': 'solar_telescope.n.01', 'name': 'solar_telescope'}, {'id': 11059, 'synset': 'solar_thermal_system.n.01', 'name': 'solar_thermal_system'}, {'id': 11060, 'synset': 'soldering_iron.n.01', 'name': 'soldering_iron'}, {'id': 11061, 'synset': 'solenoid.n.01', 'name': 'solenoid'}, {'id': 11062, 'synset': 'solleret.n.01', 'name': 'solleret'}, {'id': 11063, 'synset': 'sonic_depth_finder.n.01', 'name': 'sonic_depth_finder'}, {'id': 11064, 'synset': 'sonogram.n.01', 'name': 'sonogram'}, {'id': 11065, 'synset': 'sonograph.n.01', 'name': 'sonograph'}, {'id': 11066, 'synset': 'sorter.n.02', 'name': 'sorter'}, {'id': 11067, 'synset': 'souk.n.01', 'name': 'souk'}, {'id': 11068, 'synset': 'sound_bow.n.01', 'name': 'sound_bow'}, {'id': 11069, 'synset': 'soundbox.n.01', 'name': 'soundbox'}, {'id': 11070, 'synset': 'sound_camera.n.01', 'name': 'sound_camera'}, {'id': 11071, 'synset': 'sounder.n.01', 'name': 'sounder'}, {'id': 11072, 'synset': 'sound_film.n.01', 'name': 'sound_film'}, {'id': 11073, 'synset': 'sounding_board.n.02', 'name': 'sounding_board'}, {'id': 11074, 'synset': 'sounding_rocket.n.01', 'name': 'sounding_rocket'}, {'id': 11075, 'synset': 'sound_recording.n.01', 'name': 'sound_recording'}, {'id': 11076, 'synset': 'sound_spectrograph.n.01', 'name': 'sound_spectrograph'}, {'id': 11077, 'synset': 'soup_ladle.n.01', 'name': 'soup_ladle'}, {'id': 11078, 'synset': 'source_of_illumination.n.01', 'name': 'source_of_illumination'}, {'id': 11079, 'synset': 'sourdine.n.02', 'name': 'sourdine'}, {'id': 11080, 'synset': 'soutache.n.01', 'name': 'soutache'}, {'id': 11081, 'synset': 'soutane.n.01', 'name': 'soutane'}, {'id': 11082, 'synset': "sou'wester.n.02", 'name': "sou'wester"}, {'id': 11083, 'synset': 'soybean_future.n.01', 'name': 'soybean_future'}, {'id': 11084, 'synset': 'space_bar.n.01', 'name': 'space_bar'}, {'id': 11085, 'synset': 'space_capsule.n.01', 'name': 'space_capsule'}, {'id': 11086, 'synset': 'spacecraft.n.01', 'name': 'spacecraft'}, {'id': 11087, 'synset': 'space_heater.n.01', 'name': 'space_heater'}, {'id': 11088, 'synset': 'space_helmet.n.01', 'name': 'space_helmet'}, {'id': 11089, 'synset': 'space_rocket.n.01', 'name': 'space_rocket'}, {'id': 11090, 'synset': 'space_station.n.01', 'name': 'space_station'}, {'id': 11091, 'synset': 'spacesuit.n.01', 'name': 'spacesuit'}, {'id': 11092, 'synset': 'spade.n.02', 'name': 'spade'}, {'id': 11093, 'synset': 'spade_bit.n.01', 'name': 'spade_bit'}, {'id': 11094, 'synset': 'spaghetti_junction.n.01', 'name': 'spaghetti_junction'}, {'id': 11095, 'synset': 'spandau.n.01', 'name': 'Spandau'}, {'id': 11096, 'synset': 'spandex.n.01', 'name': 'spandex'}, {'id': 11097, 'synset': 'spandrel.n.01', 'name': 'spandrel'}, {'id': 11098, 'synset': 'spanker.n.02', 'name': 'spanker'}, {'id': 11099, 'synset': 'spar.n.02', 'name': 'spar'}, {'id': 11100, 'synset': 'sparge_pipe.n.01', 'name': 'sparge_pipe'}, {'id': 11101, 'synset': 'spark_arrester.n.02', 'name': 'spark_arrester'}, {'id': 11102, 'synset': 'spark_arrester.n.01', 'name': 'spark_arrester'}, {'id': 11103, 'synset': 'spark_chamber.n.01', 'name': 'spark_chamber'}, {'id': 11104, 'synset': 'spark_coil.n.01', 'name': 'spark_coil'}, {'id': 11105, 'synset': 'spark_gap.n.01', 'name': 'spark_gap'}, {'id': 11106, 'synset': 'spark_lever.n.01', 'name': 'spark_lever'}, {'id': 11107, 'synset': 'spark_plug.n.01', 'name': 'spark_plug'}, {'id': 11108, 'synset': 'sparkplug_wrench.n.01', 'name': 'sparkplug_wrench'}, {'id': 11109, 'synset': 'spark_transmitter.n.01', 'name': 'spark_transmitter'}, {'id': 11110, 'synset': 'spat.n.02', 'name': 'spat'}, {'id': 11111, 'synset': 'spatula.n.01', 'name': 'spatula'}, {'id': 11112, 'synset': 'speakerphone.n.01', 'name': 'speakerphone'}, {'id': 11113, 'synset': 'speaking_trumpet.n.01', 'name': 'speaking_trumpet'}, {'id': 11114, 'synset': 'spear.n.02', 'name': 'spear'}, {'id': 11115, 'synset': 'specialty_store.n.01', 'name': 'specialty_store'}, {'id': 11116, 'synset': 'specimen_bottle.n.01', 'name': 'specimen_bottle'}, {'id': 11117, 'synset': 'spectacle.n.02', 'name': 'spectacle'}, {'id': 11118, 'synset': 'spectator_pump.n.01', 'name': 'spectator_pump'}, {'id': 11119, 'synset': 'spectrograph.n.01', 'name': 'spectrograph'}, {'id': 11120, 'synset': 'spectrophotometer.n.01', 'name': 'spectrophotometer'}, {'id': 11121, 'synset': 'spectroscope.n.01', 'name': 'spectroscope'}, {'id': 11122, 'synset': 'speculum.n.02', 'name': 'speculum'}, {'id': 11123, 'synset': 'speedboat.n.01', 'name': 'speedboat'}, {'id': 11124, 'synset': 'speed_bump.n.01', 'name': 'speed_bump'}, {'id': 11125, 'synset': 'speedometer.n.01', 'name': 'speedometer'}, {'id': 11126, 'synset': 'speed_skate.n.01', 'name': 'speed_skate'}, {'id': 11127, 'synset': 'spherometer.n.01', 'name': 'spherometer'}, {'id': 11128, 'synset': 'sphygmomanometer.n.01', 'name': 'sphygmomanometer'}, {'id': 11129, 'synset': 'spicemill.n.01', 'name': 'spicemill'}, {'id': 11130, 'synset': 'spider.n.03', 'name': 'spider'}, {'id': 11131, 'synset': 'spider_web.n.01', 'name': 'spider_web'}, {'id': 11132, 'synset': 'spike.n.02', 'name': 'spike'}, {'id': 11133, 'synset': 'spike.n.11', 'name': 'spike'}, {'id': 11134, 'synset': 'spindle.n.04', 'name': 'spindle'}, {'id': 11135, 'synset': 'spindle.n.03', 'name': 'spindle'}, {'id': 11136, 'synset': 'spindle.n.02', 'name': 'spindle'}, {'id': 11137, 'synset': 'spin_dryer.n.01', 'name': 'spin_dryer'}, {'id': 11138, 'synset': 'spinet.n.02', 'name': 'spinet'}, {'id': 11139, 'synset': 'spinet.n.01', 'name': 'spinet'}, {'id': 11140, 'synset': 'spinnaker.n.01', 'name': 'spinnaker'}, {'id': 11141, 'synset': 'spinner.n.03', 'name': 'spinner'}, {'id': 11142, 'synset': 'spinning_frame.n.01', 'name': 'spinning_frame'}, {'id': 11143, 'synset': 'spinning_jenny.n.01', 'name': 'spinning_jenny'}, {'id': 11144, 'synset': 'spinning_machine.n.01', 'name': 'spinning_machine'}, {'id': 11145, 'synset': 'spinning_rod.n.01', 'name': 'spinning_rod'}, {'id': 11146, 'synset': 'spinning_wheel.n.01', 'name': 'spinning_wheel'}, {'id': 11147, 'synset': 'spiral_bandage.n.01', 'name': 'spiral_bandage'}, {'id': 11148, 'synset': 'spiral_ratchet_screwdriver.n.01', 'name': 'spiral_ratchet_screwdriver'}, {'id': 11149, 'synset': 'spiral_spring.n.01', 'name': 'spiral_spring'}, {'id': 11150, 'synset': 'spirit_lamp.n.01', 'name': 'spirit_lamp'}, {'id': 11151, 'synset': 'spirit_stove.n.01', 'name': 'spirit_stove'}, {'id': 11152, 'synset': 'spirometer.n.01', 'name': 'spirometer'}, {'id': 11153, 'synset': 'spit.n.03', 'name': 'spit'}, {'id': 11154, 'synset': 'spittoon.n.01', 'name': 'spittoon'}, {'id': 11155, 'synset': 'splashboard.n.02', 'name': 'splashboard'}, {'id': 11156, 'synset': 'splasher.n.01', 'name': 'splasher'}, {'id': 11157, 'synset': 'splice.n.01', 'name': 'splice'}, {'id': 11158, 'synset': 'splicer.n.03', 'name': 'splicer'}, {'id': 11159, 'synset': 'splint.n.02', 'name': 'splint'}, {'id': 11160, 'synset': 'split_rail.n.01', 'name': 'split_rail'}, {'id': 11161, 'synset': 'spode.n.02', 'name': 'Spode'}, {'id': 11162, 'synset': 'spoiler.n.05', 'name': 'spoiler'}, {'id': 11163, 'synset': 'spoiler.n.04', 'name': 'spoiler'}, {'id': 11164, 'synset': 'spoke.n.01', 'name': 'spoke'}, {'id': 11165, 'synset': 'spokeshave.n.01', 'name': 'spokeshave'}, {'id': 11166, 'synset': 'sponge_cloth.n.01', 'name': 'sponge_cloth'}, {'id': 11167, 'synset': 'sponge_mop.n.01', 'name': 'sponge_mop'}, {'id': 11168, 'synset': 'spoon.n.03', 'name': 'spoon'}, {'id': 11169, 'synset': 'spork.n.01', 'name': 'Spork'}, {'id': 11170, 'synset': 'sporran.n.01', 'name': 'sporran'}, {'id': 11171, 'synset': 'sport_kite.n.01', 'name': 'sport_kite'}, {'id': 11172, 'synset': 'sports_car.n.01', 'name': 'sports_car'}, {'id': 11173, 'synset': 'sports_equipment.n.01', 'name': 'sports_equipment'}, {'id': 11174, 'synset': 'sports_implement.n.01', 'name': 'sports_implement'}, {'id': 11175, 'synset': 'sport_utility.n.01', 'name': 'sport_utility'}, {'id': 11176, 'synset': 'spot.n.07', 'name': 'spot'}, {'id': 11177, 'synset': 'spot_weld.n.01', 'name': 'spot_weld'}, {'id': 11178, 'synset': 'spouter.n.02', 'name': 'spouter'}, {'id': 11179, 'synset': 'sprag.n.01', 'name': 'sprag'}, {'id': 11180, 'synset': 'spray_gun.n.01', 'name': 'spray_gun'}, {'id': 11181, 'synset': 'spray_paint.n.01', 'name': 'spray_paint'}, {'id': 11182, 'synset': 'spreader.n.01', 'name': 'spreader'}, {'id': 11183, 'synset': 'sprig.n.02', 'name': 'sprig'}, {'id': 11184, 'synset': 'spring.n.02', 'name': 'spring'}, {'id': 11185, 'synset': 'spring_balance.n.01', 'name': 'spring_balance'}, {'id': 11186, 'synset': 'springboard.n.01', 'name': 'springboard'}, {'id': 11187, 'synset': 'sprinkler.n.01', 'name': 'sprinkler'}, {'id': 11188, 'synset': 'sprinkler_system.n.01', 'name': 'sprinkler_system'}, {'id': 11189, 'synset': 'sprit.n.01', 'name': 'sprit'}, {'id': 11190, 'synset': 'spritsail.n.01', 'name': 'spritsail'}, {'id': 11191, 'synset': 'sprocket.n.02', 'name': 'sprocket'}, {'id': 11192, 'synset': 'sprocket.n.01', 'name': 'sprocket'}, {'id': 11193, 'synset': 'spun_yarn.n.01', 'name': 'spun_yarn'}, {'id': 11194, 'synset': 'spur.n.04', 'name': 'spur'}, {'id': 11195, 'synset': 'spur_gear.n.01', 'name': 'spur_gear'}, {'id': 11196, 'synset': 'sputnik.n.01', 'name': 'sputnik'}, {'id': 11197, 'synset': 'spy_satellite.n.01', 'name': 'spy_satellite'}, {'id': 11198, 'synset': 'squad_room.n.01', 'name': 'squad_room'}, {'id': 11199, 'synset': 'square.n.08', 'name': 'square'}, {'id': 11200, 'synset': 'square_knot.n.01', 'name': 'square_knot'}, {'id': 11201, 'synset': 'square-rigger.n.01', 'name': 'square-rigger'}, {'id': 11202, 'synset': 'square_sail.n.01', 'name': 'square_sail'}, {'id': 11203, 'synset': 'squash_ball.n.01', 'name': 'squash_ball'}, {'id': 11204, 'synset': 'squash_racket.n.01', 'name': 'squash_racket'}, {'id': 11205, 'synset': 'squawk_box.n.01', 'name': 'squawk_box'}, {'id': 11206, 'synset': 'squeegee.n.01', 'name': 'squeegee'}, {'id': 11207, 'synset': 'squeezer.n.01', 'name': 'squeezer'}, {'id': 11208, 'synset': 'squelch_circuit.n.01', 'name': 'squelch_circuit'}, {'id': 11209, 'synset': 'squinch.n.01', 'name': 'squinch'}, {'id': 11210, 'synset': 'stabilizer.n.03', 'name': 'stabilizer'}, {'id': 11211, 'synset': 'stabilizer.n.02', 'name': 'stabilizer'}, {'id': 11212, 'synset': 'stabilizer_bar.n.01', 'name': 'stabilizer_bar'}, {'id': 11213, 'synset': 'stable.n.01', 'name': 'stable'}, {'id': 11214, 'synset': 'stable_gear.n.01', 'name': 'stable_gear'}, {'id': 11215, 'synset': 'stabling.n.01', 'name': 'stabling'}, {'id': 11216, 'synset': 'stacks.n.02', 'name': 'stacks'}, {'id': 11217, 'synset': 'staddle.n.01', 'name': 'staddle'}, {'id': 11218, 'synset': 'stadium.n.01', 'name': 'stadium'}, {'id': 11219, 'synset': 'stage.n.03', 'name': 'stage'}, {'id': 11220, 'synset': 'stained-glass_window.n.01', 'name': 'stained-glass_window'}, {'id': 11221, 'synset': 'stair-carpet.n.01', 'name': 'stair-carpet'}, {'id': 11222, 'synset': 'stair-rod.n.01', 'name': 'stair-rod'}, {'id': 11223, 'synset': 'stairwell.n.01', 'name': 'stairwell'}, {'id': 11224, 'synset': 'stake.n.05', 'name': 'stake'}, {'id': 11225, 'synset': 'stall.n.03', 'name': 'stall'}, {'id': 11226, 'synset': 'stall.n.01', 'name': 'stall'}, {'id': 11227, 'synset': 'stamp.n.08', 'name': 'stamp'}, {'id': 11228, 'synset': 'stamp_mill.n.01', 'name': 'stamp_mill'}, {'id': 11229, 'synset': 'stamping_machine.n.01', 'name': 'stamping_machine'}, {'id': 11230, 'synset': 'stanchion.n.01', 'name': 'stanchion'}, {'id': 11231, 'synset': 'stand.n.04', 'name': 'stand'}, {'id': 11232, 'synset': 'standard.n.05', 'name': 'standard'}, {'id': 11233, 'synset': 'standard_cell.n.01', 'name': 'standard_cell'}, {'id': 11234, 'synset': 'standard_transmission.n.01', 'name': 'standard_transmission'}, {'id': 11235, 'synset': 'standing_press.n.01', 'name': 'standing_press'}, {'id': 11236, 'synset': 'stanhope.n.01', 'name': 'stanhope'}, {'id': 11237, 'synset': 'stanley_steamer.n.01', 'name': 'Stanley_Steamer'}, {'id': 11238, 'synset': 'staple.n.05', 'name': 'staple'}, {'id': 11239, 'synset': 'staple.n.04', 'name': 'staple'}, {'id': 11240, 'synset': 'staple_gun.n.01', 'name': 'staple_gun'}, {'id': 11241, 'synset': 'starship.n.01', 'name': 'starship'}, {'id': 11242, 'synset': 'starter.n.01', 'name': 'starter'}, {'id': 11243, 'synset': 'starting_gate.n.01', 'name': 'starting_gate'}, {'id': 11244, 'synset': 'stassano_furnace.n.01', 'name': 'Stassano_furnace'}, {'id': 11245, 'synset': 'statehouse.n.01', 'name': 'Statehouse'}, {'id': 11246, 'synset': 'stately_home.n.01', 'name': 'stately_home'}, {'id': 11247, 'synset': 'state_prison.n.01', 'name': 'state_prison'}, {'id': 11248, 'synset': 'stateroom.n.01', 'name': 'stateroom'}, {'id': 11249, 'synset': 'static_tube.n.01', 'name': 'static_tube'}, {'id': 11250, 'synset': 'station.n.01', 'name': 'station'}, {'id': 11251, 'synset': 'stator.n.01', 'name': 'stator'}, {'id': 11252, 'synset': 'stay.n.05', 'name': 'stay'}, {'id': 11253, 'synset': 'staysail.n.01', 'name': 'staysail'}, {'id': 11254, 'synset': 'steakhouse.n.01', 'name': 'steakhouse'}, {'id': 11255, 'synset': 'stealth_aircraft.n.01', 'name': 'stealth_aircraft'}, {'id': 11256, 'synset': 'stealth_bomber.n.01', 'name': 'stealth_bomber'}, {'id': 11257, 'synset': 'stealth_fighter.n.01', 'name': 'stealth_fighter'}, {'id': 11258, 'synset': 'steam_bath.n.01', 'name': 'steam_bath'}, {'id': 11259, 'synset': 'steamboat.n.01', 'name': 'steamboat'}, {'id': 11260, 'synset': 'steam_chest.n.01', 'name': 'steam_chest'}, {'id': 11261, 'synset': 'steam_engine.n.01', 'name': 'steam_engine'}, {'id': 11262, 'synset': 'steamer.n.03', 'name': 'steamer'}, {'id': 11263, 'synset': 'steamer.n.02', 'name': 'steamer'}, {'id': 11264, 'synset': 'steam_iron.n.01', 'name': 'steam_iron'}, {'id': 11265, 'synset': 'steam_locomotive.n.01', 'name': 'steam_locomotive'}, {'id': 11266, 'synset': 'steamroller.n.02', 'name': 'steamroller'}, {'id': 11267, 'synset': 'steam_shovel.n.01', 'name': 'steam_shovel'}, {'id': 11268, 'synset': 'steam_turbine.n.01', 'name': 'steam_turbine'}, {'id': 11269, 'synset': 'steam_whistle.n.01', 'name': 'steam_whistle'}, {'id': 11270, 'synset': 'steel.n.03', 'name': 'steel'}, {'id': 11271, 'synset': 'steel_arch_bridge.n.01', 'name': 'steel_arch_bridge'}, {'id': 11272, 'synset': 'steel_drum.n.01', 'name': 'steel_drum'}, {'id': 11273, 'synset': 'steel_mill.n.01', 'name': 'steel_mill'}, {'id': 11274, 'synset': 'steel-wool_pad.n.01', 'name': 'steel-wool_pad'}, {'id': 11275, 'synset': 'steelyard.n.01', 'name': 'steelyard'}, {'id': 11276, 'synset': 'steeple.n.01', 'name': 'steeple'}, {'id': 11277, 'synset': 'steerage.n.01', 'name': 'steerage'}, {'id': 11278, 'synset': 'steering_gear.n.01', 'name': 'steering_gear'}, {'id': 11279, 'synset': 'steering_linkage.n.01', 'name': 'steering_linkage'}, {'id': 11280, 'synset': 'steering_system.n.01', 'name': 'steering_system'}, {'id': 11281, 'synset': 'stele.n.02', 'name': 'stele'}, {'id': 11282, 'synset': 'stem-winder.n.01', 'name': 'stem-winder'}, {'id': 11283, 'synset': 'stencil.n.01', 'name': 'stencil'}, {'id': 11284, 'synset': 'sten_gun.n.01', 'name': 'Sten_gun'}, {'id': 11285, 'synset': 'stenograph.n.02', 'name': 'stenograph'}, {'id': 11286, 'synset': 'step.n.04', 'name': 'step'}, {'id': 11287, 'synset': 'step-down_transformer.n.01', 'name': 'step-down_transformer'}, {'id': 11288, 'synset': 'step-up_transformer.n.01', 'name': 'step-up_transformer'}, {'id': 11289, 'synset': 'stereoscope.n.01', 'name': 'stereoscope'}, {'id': 11290, 'synset': 'stern_chaser.n.01', 'name': 'stern_chaser'}, {'id': 11291, 'synset': 'sternpost.n.01', 'name': 'sternpost'}, {'id': 11292, 'synset': 'sternwheeler.n.01', 'name': 'sternwheeler'}, {'id': 11293, 'synset': 'stethoscope.n.01', 'name': 'stethoscope'}, {'id': 11294, 'synset': 'stewing_pan.n.01', 'name': 'stewing_pan'}, {'id': 11295, 'synset': 'stick.n.01', 'name': 'stick'}, {'id': 11296, 'synset': 'stick.n.07', 'name': 'stick'}, {'id': 11297, 'synset': 'stick.n.03', 'name': 'stick'}, {'id': 11298, 'synset': 'stick.n.06', 'name': 'stick'}, {'id': 11299, 'synset': 'stile.n.01', 'name': 'stile'}, {'id': 11300, 'synset': 'stiletto.n.01', 'name': 'stiletto'}, {'id': 11301, 'synset': 'still.n.03', 'name': 'still'}, {'id': 11302, 'synset': 'stillroom.n.01', 'name': 'stillroom'}, {'id': 11303, 'synset': 'stillson_wrench.n.01', 'name': 'Stillson_wrench'}, {'id': 11304, 'synset': 'stilt.n.02', 'name': 'stilt'}, {'id': 11305, 'synset': 'stinger.n.03', 'name': 'Stinger'}, {'id': 11306, 'synset': 'stink_bomb.n.01', 'name': 'stink_bomb'}, {'id': 11307, 'synset': 'stirrup_pump.n.01', 'name': 'stirrup_pump'}, {'id': 11308, 'synset': 'stob.n.01', 'name': 'stob'}, {'id': 11309, 'synset': 'stock.n.03', 'name': 'stock'}, {'id': 11310, 'synset': 'stockade.n.01', 'name': 'stockade'}, {'id': 11311, 'synset': 'stockcar.n.01', 'name': 'stockcar'}, {'id': 11312, 'synset': 'stock_car.n.02', 'name': 'stock_car'}, {'id': 11313, 'synset': 'stockinet.n.01', 'name': 'stockinet'}, {'id': 11314, 'synset': 'stocking.n.01', 'name': 'stocking'}, {'id': 11315, 'synset': 'stock-in-trade.n.01', 'name': 'stock-in-trade'}, {'id': 11316, 'synset': 'stockpot.n.01', 'name': 'stockpot'}, {'id': 11317, 'synset': 'stockroom.n.01', 'name': 'stockroom'}, {'id': 11318, 'synset': 'stocks.n.03', 'name': 'stocks'}, {'id': 11319, 'synset': 'stock_saddle.n.01', 'name': 'stock_saddle'}, {'id': 11320, 'synset': 'stockyard.n.01', 'name': 'stockyard'}, {'id': 11321, 'synset': 'stole.n.01', 'name': 'stole'}, {'id': 11322, 'synset': 'stomacher.n.01', 'name': 'stomacher'}, {'id': 11323, 'synset': 'stomach_pump.n.01', 'name': 'stomach_pump'}, {'id': 11324, 'synset': 'stone_wall.n.01', 'name': 'stone_wall'}, {'id': 11325, 'synset': 'stoneware.n.01', 'name': 'stoneware'}, {'id': 11326, 'synset': 'stonework.n.01', 'name': 'stonework'}, {'id': 11327, 'synset': 'stoop.n.03', 'name': 'stoop'}, {'id': 11328, 'synset': 'stop_bath.n.01', 'name': 'stop_bath'}, {'id': 11329, 'synset': 'stopcock.n.01', 'name': 'stopcock'}, {'id': 11330, 'synset': 'stopper_knot.n.01', 'name': 'stopper_knot'}, {'id': 11331, 'synset': 'stopwatch.n.01', 'name': 'stopwatch'}, {'id': 11332, 'synset': 'storage_battery.n.01', 'name': 'storage_battery'}, {'id': 11333, 'synset': 'storage_cell.n.01', 'name': 'storage_cell'}, {'id': 11334, 'synset': 'storage_ring.n.01', 'name': 'storage_ring'}, {'id': 11335, 'synset': 'storage_space.n.01', 'name': 'storage_space'}, {'id': 11336, 'synset': 'storeroom.n.01', 'name': 'storeroom'}, {'id': 11337, 'synset': 'storm_cellar.n.01', 'name': 'storm_cellar'}, {'id': 11338, 'synset': 'storm_door.n.01', 'name': 'storm_door'}, {'id': 11339, 'synset': 'storm_window.n.01', 'name': 'storm_window'}, {'id': 11340, 'synset': 'stoup.n.02', 'name': 'stoup'}, {'id': 11341, 'synset': 'stoup.n.01', 'name': 'stoup'}, {'id': 11342, 'synset': 'stove.n.02', 'name': 'stove'}, {'id': 11343, 'synset': 'stove_bolt.n.01', 'name': 'stove_bolt'}, {'id': 11344, 'synset': 'stovepipe.n.01', 'name': 'stovepipe'}, {'id': 11345, 'synset': 'stovepipe_iron.n.01', 'name': 'stovepipe_iron'}, {'id': 11346, 'synset': 'stradavarius.n.01', 'name': 'Stradavarius'}, {'id': 11347, 'synset': 'straight_chair.n.01', 'name': 'straight_chair'}, {'id': 11348, 'synset': 'straightedge.n.01', 'name': 'straightedge'}, {'id': 11349, 'synset': 'straightener.n.01', 'name': 'straightener'}, {'id': 11350, 'synset': 'straight_flute.n.01', 'name': 'straight_flute'}, {'id': 11351, 'synset': 'straight_pin.n.01', 'name': 'straight_pin'}, {'id': 11352, 'synset': 'straight_razor.n.01', 'name': 'straight_razor'}, {'id': 11353, 'synset': 'straitjacket.n.02', 'name': 'straitjacket'}, {'id': 11354, 'synset': 'strap.n.04', 'name': 'strap'}, {'id': 11355, 'synset': 'strap_hinge.n.01', 'name': 'strap_hinge'}, {'id': 11356, 'synset': 'strapless.n.01', 'name': 'strapless'}, {'id': 11357, 'synset': 'streamer_fly.n.01', 'name': 'streamer_fly'}, {'id': 11358, 'synset': 'streamliner.n.01', 'name': 'streamliner'}, {'id': 11359, 'synset': 'street.n.01', 'name': 'street'}, {'id': 11360, 'synset': 'street.n.02', 'name': 'street'}, {'id': 11361, 'synset': 'streetcar.n.01', 'name': 'streetcar'}, {'id': 11362, 'synset': 'street_clothes.n.01', 'name': 'street_clothes'}, {'id': 11363, 'synset': 'stretcher.n.03', 'name': 'stretcher'}, {'id': 11364, 'synset': 'stretcher.n.01', 'name': 'stretcher'}, {'id': 11365, 'synset': 'stretch_pants.n.01', 'name': 'stretch_pants'}, {'id': 11366, 'synset': 'strickle.n.02', 'name': 'strickle'}, {'id': 11367, 'synset': 'strickle.n.01', 'name': 'strickle'}, {'id': 11368, 'synset': 'stringed_instrument.n.01', 'name': 'stringed_instrument'}, {'id': 11369, 'synset': 'stringer.n.04', 'name': 'stringer'}, {'id': 11370, 'synset': 'stringer.n.03', 'name': 'stringer'}, {'id': 11371, 'synset': 'string_tie.n.01', 'name': 'string_tie'}, {'id': 11372, 'synset': 'strip.n.05', 'name': 'strip'}, {'id': 11373, 'synset': 'strip_lighting.n.01', 'name': 'strip_lighting'}, {'id': 11374, 'synset': 'strip_mall.n.01', 'name': 'strip_mall'}, {'id': 11375, 'synset': 'stroboscope.n.01', 'name': 'stroboscope'}, {'id': 11376, 'synset': 'strongbox.n.01', 'name': 'strongbox'}, {'id': 11377, 'synset': 'stronghold.n.01', 'name': 'stronghold'}, {'id': 11378, 'synset': 'strongroom.n.01', 'name': 'strongroom'}, {'id': 11379, 'synset': 'strop.n.01', 'name': 'strop'}, {'id': 11380, 'synset': 'structural_member.n.01', 'name': 'structural_member'}, {'id': 11381, 'synset': 'structure.n.01', 'name': 'structure'}, {'id': 11382, 'synset': 'student_center.n.01', 'name': 'student_center'}, {'id': 11383, 'synset': 'student_lamp.n.01', 'name': 'student_lamp'}, {'id': 11384, 'synset': 'student_union.n.01', 'name': 'student_union'}, {'id': 11385, 'synset': 'stud_finder.n.01', 'name': 'stud_finder'}, {'id': 11386, 'synset': 'studio_apartment.n.01', 'name': 'studio_apartment'}, {'id': 11387, 'synset': 'studio_couch.n.01', 'name': 'studio_couch'}, {'id': 11388, 'synset': 'study.n.05', 'name': 'study'}, {'id': 11389, 'synset': 'study_hall.n.02', 'name': 'study_hall'}, {'id': 11390, 'synset': 'stuffing_nut.n.01', 'name': 'stuffing_nut'}, {'id': 11391, 'synset': 'stump.n.03', 'name': 'stump'}, {'id': 11392, 'synset': 'stun_gun.n.01', 'name': 'stun_gun'}, {'id': 11393, 'synset': 'stupa.n.01', 'name': 'stupa'}, {'id': 11394, 'synset': 'sty.n.02', 'name': 'sty'}, {'id': 11395, 'synset': 'stylus.n.01', 'name': 'stylus'}, {'id': 11396, 'synset': 'sub-assembly.n.01', 'name': 'sub-assembly'}, {'id': 11397, 'synset': 'subcompact.n.01', 'name': 'subcompact'}, {'id': 11398, 'synset': 'submachine_gun.n.01', 'name': 'submachine_gun'}, {'id': 11399, 'synset': 'submarine.n.01', 'name': 'submarine'}, {'id': 11400, 'synset': 'submarine_torpedo.n.01', 'name': 'submarine_torpedo'}, {'id': 11401, 'synset': 'submersible.n.02', 'name': 'submersible'}, {'id': 11402, 'synset': 'submersible.n.01', 'name': 'submersible'}, {'id': 11403, 'synset': 'subtracter.n.02', 'name': 'subtracter'}, {'id': 11404, 'synset': 'subway_token.n.01', 'name': 'subway_token'}, {'id': 11405, 'synset': 'subway_train.n.01', 'name': 'subway_train'}, {'id': 11406, 'synset': 'suction_cup.n.01', 'name': 'suction_cup'}, {'id': 11407, 'synset': 'suction_pump.n.01', 'name': 'suction_pump'}, {'id': 11408, 'synset': 'sudatorium.n.01', 'name': 'sudatorium'}, {'id': 11409, 'synset': 'suede_cloth.n.01', 'name': 'suede_cloth'}, {'id': 11410, 'synset': 'sugar_refinery.n.01', 'name': 'sugar_refinery'}, {'id': 11411, 'synset': 'sugar_spoon.n.01', 'name': 'sugar_spoon'}, {'id': 11412, 'synset': 'suite.n.02', 'name': 'suite'}, {'id': 11413, 'synset': 'suiting.n.01', 'name': 'suiting'}, {'id': 11414, 'synset': 'sulky.n.01', 'name': 'sulky'}, {'id': 11415, 'synset': 'summer_house.n.01', 'name': 'summer_house'}, {'id': 11416, 'synset': 'sumo_ring.n.01', 'name': 'sumo_ring'}, {'id': 11417, 'synset': 'sump.n.01', 'name': 'sump'}, {'id': 11418, 'synset': 'sump_pump.n.01', 'name': 'sump_pump'}, {'id': 11419, 'synset': 'sunbonnet.n.01', 'name': 'sunbonnet'}, {'id': 11420, 'synset': 'sunday_best.n.01', 'name': 'Sunday_best'}, {'id': 11421, 'synset': 'sun_deck.n.01', 'name': 'sun_deck'}, {'id': 11422, 'synset': 'sundial.n.01', 'name': 'sundial'}, {'id': 11423, 'synset': 'sundress.n.01', 'name': 'sundress'}, {'id': 11424, 'synset': 'sundries.n.01', 'name': 'sundries'}, {'id': 11425, 'synset': 'sun_gear.n.01', 'name': 'sun_gear'}, {'id': 11426, 'synset': 'sunglass.n.01', 'name': 'sunglass'}, {'id': 11427, 'synset': 'sunlamp.n.01', 'name': 'sunlamp'}, {'id': 11428, 'synset': 'sun_parlor.n.01', 'name': 'sun_parlor'}, {'id': 11429, 'synset': 'sunroof.n.01', 'name': 'sunroof'}, {'id': 11430, 'synset': 'sunscreen.n.01', 'name': 'sunscreen'}, {'id': 11431, 'synset': 'sunsuit.n.01', 'name': 'sunsuit'}, {'id': 11432, 'synset': 'supercharger.n.01', 'name': 'supercharger'}, {'id': 11433, 'synset': 'supercomputer.n.01', 'name': 'supercomputer'}, {'id': 11434, 'synset': 'superconducting_supercollider.n.01', 'name': 'superconducting_supercollider'}, {'id': 11435, 'synset': 'superhighway.n.02', 'name': 'superhighway'}, {'id': 11436, 'synset': 'supermarket.n.01', 'name': 'supermarket'}, {'id': 11437, 'synset': 'superstructure.n.01', 'name': 'superstructure'}, {'id': 11438, 'synset': 'supertanker.n.01', 'name': 'supertanker'}, {'id': 11439, 'synset': 'supper_club.n.01', 'name': 'supper_club'}, {'id': 11440, 'synset': 'supplejack.n.01', 'name': 'supplejack'}, {'id': 11441, 'synset': 'supply_chamber.n.01', 'name': 'supply_chamber'}, {'id': 11442, 'synset': 'supply_closet.n.01', 'name': 'supply_closet'}, {'id': 11443, 'synset': 'support.n.10', 'name': 'support'}, {'id': 11444, 'synset': 'support.n.07', 'name': 'support'}, {'id': 11445, 'synset': 'support_column.n.01', 'name': 'support_column'}, {'id': 11446, 'synset': 'support_hose.n.01', 'name': 'support_hose'}, {'id': 11447, 'synset': 'supporting_structure.n.01', 'name': 'supporting_structure'}, {'id': 11448, 'synset': 'supporting_tower.n.01', 'name': 'supporting_tower'}, {'id': 11449, 'synset': 'surcoat.n.02', 'name': 'surcoat'}, {'id': 11450, 'synset': 'surface_gauge.n.01', 'name': 'surface_gauge'}, {'id': 11451, 'synset': 'surface_lift.n.01', 'name': 'surface_lift'}, {'id': 11452, 'synset': 'surface_search_radar.n.01', 'name': 'surface_search_radar'}, {'id': 11453, 'synset': 'surface_ship.n.01', 'name': 'surface_ship'}, {'id': 11454, 'synset': 'surface-to-air_missile.n.01', 'name': 'surface-to-air_missile'}, {'id': 11455, 'synset': 'surface-to-air_missile_system.n.01', 'name': 'surface-to-air_missile_system'}, {'id': 11456, 'synset': 'surfboat.n.01', 'name': 'surfboat'}, {'id': 11457, 'synset': 'surcoat.n.01', 'name': 'surcoat'}, {'id': 11458, 'synset': "surgeon's_knot.n.01", 'name': "surgeon's_knot"}, {'id': 11459, 'synset': 'surgery.n.02', 'name': 'surgery'}, {'id': 11460, 'synset': 'surge_suppressor.n.01', 'name': 'surge_suppressor'}, {'id': 11461, 'synset': 'surgical_dressing.n.01', 'name': 'surgical_dressing'}, {'id': 11462, 'synset': 'surgical_instrument.n.01', 'name': 'surgical_instrument'}, {'id': 11463, 'synset': 'surgical_knife.n.01', 'name': 'surgical_knife'}, {'id': 11464, 'synset': 'surplice.n.01', 'name': 'surplice'}, {'id': 11465, 'synset': 'surrey.n.02', 'name': 'surrey'}, {'id': 11466, 'synset': 'surtout.n.01', 'name': 'surtout'}, {'id': 11467, 'synset': 'surveillance_system.n.01', 'name': 'surveillance_system'}, {'id': 11468, 'synset': 'surveying_instrument.n.01', 'name': 'surveying_instrument'}, {'id': 11469, 'synset': "surveyor's_level.n.01", 'name': "surveyor's_level"}, {'id': 11470, 'synset': 'sushi_bar.n.01', 'name': 'sushi_bar'}, {'id': 11471, 'synset': 'suspension.n.05', 'name': 'suspension'}, {'id': 11472, 'synset': 'suspension_bridge.n.01', 'name': 'suspension_bridge'}, {'id': 11473, 'synset': 'suspensory.n.01', 'name': 'suspensory'}, {'id': 11474, 'synset': 'sustaining_pedal.n.01', 'name': 'sustaining_pedal'}, {'id': 11475, 'synset': 'suture.n.02', 'name': 'suture'}, {'id': 11476, 'synset': 'swab.n.01', 'name': 'swab'}, {'id': 11477, 'synset': 'swaddling_clothes.n.01', 'name': 'swaddling_clothes'}, {'id': 11478, 'synset': 'swag.n.03', 'name': 'swag'}, {'id': 11479, 'synset': 'swage_block.n.01', 'name': 'swage_block'}, {'id': 11480, 'synset': 'swagger_stick.n.01', 'name': 'swagger_stick'}, {'id': 11481, 'synset': 'swallow-tailed_coat.n.01', 'name': 'swallow-tailed_coat'}, {'id': 11482, 'synset': 'swamp_buggy.n.01', 'name': 'swamp_buggy'}, {'id': 11483, 'synset': "swan's_down.n.01", 'name': "swan's_down"}, {'id': 11484, 'synset': 'swathe.n.01', 'name': 'swathe'}, {'id': 11485, 'synset': 'swatter.n.01', 'name': 'swatter'}, {'id': 11486, 'synset': 'sweat_bag.n.01', 'name': 'sweat_bag'}, {'id': 11487, 'synset': 'sweatband.n.01', 'name': 'sweatband'}, {'id': 11488, 'synset': 'sweatshop.n.01', 'name': 'sweatshop'}, {'id': 11489, 'synset': 'sweat_suit.n.01', 'name': 'sweat_suit'}, {'id': 11490, 'synset': 'sweep.n.04', 'name': 'sweep'}, {'id': 11491, 'synset': 'sweep_hand.n.01', 'name': 'sweep_hand'}, {'id': 11492, 'synset': 'swimming_trunks.n.01', 'name': 'swimming_trunks'}, {'id': 11493, 'synset': 'swing.n.02', 'name': 'swing'}, {'id': 11494, 'synset': 'swing_door.n.01', 'name': 'swing_door'}, {'id': 11495, 'synset': 'switch.n.01', 'name': 'switch'}, {'id': 11496, 'synset': 'switchblade.n.01', 'name': 'switchblade'}, {'id': 11497, 'synset': 'switch_engine.n.01', 'name': 'switch_engine'}, {'id': 11498, 'synset': 'swivel.n.01', 'name': 'swivel'}, {'id': 11499, 'synset': 'swivel_chair.n.01', 'name': 'swivel_chair'}, {'id': 11500, 'synset': 'swizzle_stick.n.01', 'name': 'swizzle_stick'}, {'id': 11501, 'synset': 'sword_cane.n.01', 'name': 'sword_cane'}, {'id': 11502, 'synset': 's_wrench.n.01', 'name': 'S_wrench'}, {'id': 11503, 'synset': 'synagogue.n.01', 'name': 'synagogue'}, {'id': 11504, 'synset': 'synchrocyclotron.n.01', 'name': 'synchrocyclotron'}, {'id': 11505, 'synset': 'synchroflash.n.01', 'name': 'synchroflash'}, {'id': 11506, 'synset': 'synchromesh.n.01', 'name': 'synchromesh'}, {'id': 11507, 'synset': 'synchronous_converter.n.01', 'name': 'synchronous_converter'}, {'id': 11508, 'synset': 'synchronous_motor.n.01', 'name': 'synchronous_motor'}, {'id': 11509, 'synset': 'synchrotron.n.01', 'name': 'synchrotron'}, {'id': 11510, 'synset': 'synchroscope.n.01', 'name': 'synchroscope'}, {'id': 11511, 'synset': 'synthesizer.n.02', 'name': 'synthesizer'}, {'id': 11512, 'synset': 'system.n.01', 'name': 'system'}, {'id': 11513, 'synset': 'tabard.n.01', 'name': 'tabard'}, {'id': 11514, 'synset': 'tabernacle.n.02', 'name': 'Tabernacle'}, {'id': 11515, 'synset': 'tabi.n.01', 'name': 'tabi'}, {'id': 11516, 'synset': 'tab_key.n.01', 'name': 'tab_key'}, {'id': 11517, 'synset': 'table.n.03', 'name': 'table'}, {'id': 11518, 'synset': 'tablefork.n.01', 'name': 'tablefork'}, {'id': 11519, 'synset': 'table_knife.n.01', 'name': 'table_knife'}, {'id': 11520, 'synset': 'table_saw.n.01', 'name': 'table_saw'}, {'id': 11521, 'synset': 'tablespoon.n.02', 'name': 'tablespoon'}, {'id': 11522, 'synset': 'tablet-armed_chair.n.01', 'name': 'tablet-armed_chair'}, {'id': 11523, 'synset': 'table-tennis_racquet.n.01', 'name': 'table-tennis_racquet'}, {'id': 11524, 'synset': 'tabletop.n.01', 'name': 'tabletop'}, {'id': 11525, 'synset': 'tableware.n.01', 'name': 'tableware'}, {'id': 11526, 'synset': 'tabor.n.01', 'name': 'tabor'}, {'id': 11527, 'synset': 'taboret.n.01', 'name': 'taboret'}, {'id': 11528, 'synset': 'tachistoscope.n.01', 'name': 'tachistoscope'}, {'id': 11529, 'synset': 'tachograph.n.01', 'name': 'tachograph'}, {'id': 11530, 'synset': 'tachymeter.n.01', 'name': 'tachymeter'}, {'id': 11531, 'synset': 'tack.n.02', 'name': 'tack'}, {'id': 11532, 'synset': 'tack_hammer.n.01', 'name': 'tack_hammer'}, {'id': 11533, 'synset': 'taffeta.n.01', 'name': 'taffeta'}, {'id': 11534, 'synset': 'taffrail.n.01', 'name': 'taffrail'}, {'id': 11535, 'synset': 'tailgate.n.01', 'name': 'tailgate'}, {'id': 11536, 'synset': 'tailor-made.n.01', 'name': 'tailor-made'}, {'id': 11537, 'synset': "tailor's_chalk.n.01", 'name': "tailor's_chalk"}, {'id': 11538, 'synset': 'tailpipe.n.01', 'name': 'tailpipe'}, {'id': 11539, 'synset': 'tail_rotor.n.01', 'name': 'tail_rotor'}, {'id': 11540, 'synset': 'tailstock.n.01', 'name': 'tailstock'}, {'id': 11541, 'synset': 'take-up.n.01', 'name': 'take-up'}, {'id': 11542, 'synset': 'talaria.n.01', 'name': 'talaria'}, {'id': 11543, 'synset': 'talcum.n.02', 'name': 'talcum'}, {'id': 11544, 'synset': 'tam.n.01', 'name': 'tam'}, {'id': 11545, 'synset': 'tambour.n.02', 'name': 'tambour'}, {'id': 11546, 'synset': 'tambour.n.01', 'name': 'tambour'}, {'id': 11547, 'synset': 'tammy.n.01', 'name': 'tammy'}, {'id': 11548, 'synset': 'tamp.n.01', 'name': 'tamp'}, {'id': 11549, 'synset': 'tampax.n.01', 'name': 'Tampax'}, {'id': 11550, 'synset': 'tampion.n.01', 'name': 'tampion'}, {'id': 11551, 'synset': 'tampon.n.01', 'name': 'tampon'}, {'id': 11552, 'synset': 'tandoor.n.01', 'name': 'tandoor'}, {'id': 11553, 'synset': 'tangram.n.01', 'name': 'tangram'}, {'id': 11554, 'synset': 'tankard.n.01', 'name': 'tankard'}, {'id': 11555, 'synset': 'tank_car.n.01', 'name': 'tank_car'}, {'id': 11556, 'synset': 'tank_destroyer.n.01', 'name': 'tank_destroyer'}, {'id': 11557, 'synset': 'tank_engine.n.01', 'name': 'tank_engine'}, {'id': 11558, 'synset': 'tanker_plane.n.01', 'name': 'tanker_plane'}, {'id': 11559, 'synset': 'tank_shell.n.01', 'name': 'tank_shell'}, {'id': 11560, 'synset': 'tannoy.n.01', 'name': 'tannoy'}, {'id': 11561, 'synset': 'tap.n.06', 'name': 'tap'}, {'id': 11562, 'synset': 'tapa.n.02', 'name': 'tapa'}, {'id': 11563, 'synset': 'tape.n.02', 'name': 'tape'}, {'id': 11564, 'synset': 'tape_deck.n.01', 'name': 'tape_deck'}, {'id': 11565, 'synset': 'tape_drive.n.01', 'name': 'tape_drive'}, {'id': 11566, 'synset': 'tape_player.n.01', 'name': 'tape_player'}, {'id': 11567, 'synset': 'tape_recorder.n.01', 'name': 'tape_recorder'}, {'id': 11568, 'synset': 'taper_file.n.01', 'name': 'taper_file'}, {'id': 11569, 'synset': 'tappet.n.01', 'name': 'tappet'}, {'id': 11570, 'synset': 'tap_wrench.n.01', 'name': 'tap_wrench'}, {'id': 11571, 'synset': 'tare.n.05', 'name': 'tare'}, {'id': 11572, 'synset': 'target.n.04', 'name': 'target'}, {'id': 11573, 'synset': 'target_acquisition_system.n.01', 'name': 'target_acquisition_system'}, {'id': 11574, 'synset': 'tarmacadam.n.02', 'name': 'tarmacadam'}, {'id': 11575, 'synset': 'tasset.n.01', 'name': 'tasset'}, {'id': 11576, 'synset': 'tattoo.n.02', 'name': 'tattoo'}, {'id': 11577, 'synset': 'tavern.n.01', 'name': 'tavern'}, {'id': 11578, 'synset': 'tawse.n.01', 'name': 'tawse'}, {'id': 11579, 'synset': 'taximeter.n.01', 'name': 'taximeter'}, {'id': 11580, 'synset': 't-bar_lift.n.01', 'name': 'T-bar_lift'}, {'id': 11581, 'synset': 'tea_bag.n.02', 'name': 'tea_bag'}, {'id': 11582, 'synset': 'tea_ball.n.01', 'name': 'tea_ball'}, {'id': 11583, 'synset': 'tea_cart.n.01', 'name': 'tea_cart'}, {'id': 11584, 'synset': 'tea_chest.n.01', 'name': 'tea_chest'}, {'id': 11585, 'synset': 'teaching_aid.n.01', 'name': 'teaching_aid'}, {'id': 11586, 'synset': 'tea_gown.n.01', 'name': 'tea_gown'}, {'id': 11587, 'synset': 'tea_maker.n.01', 'name': 'tea_maker'}, {'id': 11588, 'synset': 'teashop.n.01', 'name': 'teashop'}, {'id': 11589, 'synset': 'teaspoon.n.02', 'name': 'teaspoon'}, {'id': 11590, 'synset': 'tea-strainer.n.01', 'name': 'tea-strainer'}, {'id': 11591, 'synset': 'tea_table.n.01', 'name': 'tea_table'}, {'id': 11592, 'synset': 'tea_tray.n.01', 'name': 'tea_tray'}, {'id': 11593, 'synset': 'tea_urn.n.01', 'name': 'tea_urn'}, {'id': 11594, 'synset': 'tee.n.03', 'name': 'tee'}, {'id': 11595, 'synset': 'tee_hinge.n.01', 'name': 'tee_hinge'}, {'id': 11596, 'synset': 'telecom_hotel.n.01', 'name': 'telecom_hotel'}, {'id': 11597, 'synset': 'telecommunication_system.n.01', 'name': 'telecommunication_system'}, {'id': 11598, 'synset': 'telegraph.n.01', 'name': 'telegraph'}, {'id': 11599, 'synset': 'telegraph_key.n.01', 'name': 'telegraph_key'}, {'id': 11600, 'synset': 'telemeter.n.01', 'name': 'telemeter'}, {'id': 11601, 'synset': 'telephone_bell.n.01', 'name': 'telephone_bell'}, {'id': 11602, 'synset': 'telephone_cord.n.01', 'name': 'telephone_cord'}, {'id': 11603, 'synset': 'telephone_jack.n.01', 'name': 'telephone_jack'}, {'id': 11604, 'synset': 'telephone_line.n.02', 'name': 'telephone_line'}, {'id': 11605, 'synset': 'telephone_plug.n.01', 'name': 'telephone_plug'}, {'id': 11606, 'synset': 'telephone_receiver.n.01', 'name': 'telephone_receiver'}, {'id': 11607, 'synset': 'telephone_system.n.01', 'name': 'telephone_system'}, {'id': 11608, 'synset': 'telephone_wire.n.01', 'name': 'telephone_wire'}, {'id': 11609, 'synset': 'teleprompter.n.01', 'name': 'Teleprompter'}, {'id': 11610, 'synset': 'telescope.n.01', 'name': 'telescope'}, {'id': 11611, 'synset': 'telescopic_sight.n.01', 'name': 'telescopic_sight'}, {'id': 11612, 'synset': 'telethermometer.n.01', 'name': 'telethermometer'}, {'id': 11613, 'synset': 'teletypewriter.n.01', 'name': 'teletypewriter'}, {'id': 11614, 'synset': 'television.n.02', 'name': 'television'}, {'id': 11615, 'synset': 'television_antenna.n.01', 'name': 'television_antenna'}, {'id': 11616, 'synset': 'television_equipment.n.01', 'name': 'television_equipment'}, {'id': 11617, 'synset': 'television_monitor.n.01', 'name': 'television_monitor'}, {'id': 11618, 'synset': 'television_room.n.01', 'name': 'television_room'}, {'id': 11619, 'synset': 'television_transmitter.n.01', 'name': 'television_transmitter'}, {'id': 11620, 'synset': 'telpher.n.01', 'name': 'telpher'}, {'id': 11621, 'synset': 'telpherage.n.01', 'name': 'telpherage'}, {'id': 11622, 'synset': 'tempera.n.01', 'name': 'tempera'}, {'id': 11623, 'synset': 'temple.n.01', 'name': 'temple'}, {'id': 11624, 'synset': 'temple.n.03', 'name': 'temple'}, {'id': 11625, 'synset': 'temporary_hookup.n.01', 'name': 'temporary_hookup'}, {'id': 11626, 'synset': 'tender.n.06', 'name': 'tender'}, {'id': 11627, 'synset': 'tender.n.05', 'name': 'tender'}, {'id': 11628, 'synset': 'tender.n.04', 'name': 'tender'}, {'id': 11629, 'synset': 'tenement.n.01', 'name': 'tenement'}, {'id': 11630, 'synset': 'tennis_camp.n.01', 'name': 'tennis_camp'}, {'id': 11631, 'synset': 'tenon.n.01', 'name': 'tenon'}, {'id': 11632, 'synset': 'tenor_drum.n.01', 'name': 'tenor_drum'}, {'id': 11633, 'synset': 'tenoroon.n.01', 'name': 'tenoroon'}, {'id': 11634, 'synset': 'tenpenny_nail.n.01', 'name': 'tenpenny_nail'}, {'id': 11635, 'synset': 'tenpin.n.01', 'name': 'tenpin'}, {'id': 11636, 'synset': 'tensimeter.n.01', 'name': 'tensimeter'}, {'id': 11637, 'synset': 'tensiometer.n.03', 'name': 'tensiometer'}, {'id': 11638, 'synset': 'tensiometer.n.02', 'name': 'tensiometer'}, {'id': 11639, 'synset': 'tensiometer.n.01', 'name': 'tensiometer'}, {'id': 11640, 'synset': 'tent.n.01', 'name': 'tent'}, {'id': 11641, 'synset': 'tenter.n.01', 'name': 'tenter'}, {'id': 11642, 'synset': 'tenterhook.n.01', 'name': 'tenterhook'}, {'id': 11643, 'synset': 'tent-fly.n.01', 'name': 'tent-fly'}, {'id': 11644, 'synset': 'tent_peg.n.01', 'name': 'tent_peg'}, {'id': 11645, 'synset': 'tepee.n.01', 'name': 'tepee'}, {'id': 11646, 'synset': 'terminal.n.02', 'name': 'terminal'}, {'id': 11647, 'synset': 'terminal.n.04', 'name': 'terminal'}, {'id': 11648, 'synset': 'terraced_house.n.01', 'name': 'terraced_house'}, {'id': 11649, 'synset': 'terra_cotta.n.01', 'name': 'terra_cotta'}, {'id': 11650, 'synset': 'terrarium.n.01', 'name': 'terrarium'}, {'id': 11651, 'synset': 'terra_sigillata.n.01', 'name': 'terra_sigillata'}, {'id': 11652, 'synset': 'terry.n.02', 'name': 'terry'}, {'id': 11653, 'synset': 'tesla_coil.n.01', 'name': 'Tesla_coil'}, {'id': 11654, 'synset': 'tessera.n.01', 'name': 'tessera'}, {'id': 11655, 'synset': 'test_equipment.n.01', 'name': 'test_equipment'}, {'id': 11656, 'synset': 'test_rocket.n.01', 'name': 'test_rocket'}, {'id': 11657, 'synset': 'test_room.n.01', 'name': 'test_room'}, {'id': 11658, 'synset': 'testudo.n.01', 'name': 'testudo'}, {'id': 11659, 'synset': 'tetraskelion.n.01', 'name': 'tetraskelion'}, {'id': 11660, 'synset': 'tetrode.n.01', 'name': 'tetrode'}, {'id': 11661, 'synset': 'textile_machine.n.01', 'name': 'textile_machine'}, {'id': 11662, 'synset': 'textile_mill.n.01', 'name': 'textile_mill'}, {'id': 11663, 'synset': 'thatch.n.04', 'name': 'thatch'}, {'id': 11664, 'synset': 'theater.n.01', 'name': 'theater'}, {'id': 11665, 'synset': 'theater_curtain.n.01', 'name': 'theater_curtain'}, {'id': 11666, 'synset': 'theater_light.n.01', 'name': 'theater_light'}, {'id': 11667, 'synset': 'theodolite.n.01', 'name': 'theodolite'}, {'id': 11668, 'synset': 'theremin.n.01', 'name': 'theremin'}, {'id': 11669, 'synset': 'thermal_printer.n.01', 'name': 'thermal_printer'}, {'id': 11670, 'synset': 'thermal_reactor.n.01', 'name': 'thermal_reactor'}, {'id': 11671, 'synset': 'thermocouple.n.01', 'name': 'thermocouple'}, {'id': 11672, 'synset': 'thermoelectric_thermometer.n.01', 'name': 'thermoelectric_thermometer'}, {'id': 11673, 'synset': 'thermograph.n.02', 'name': 'thermograph'}, {'id': 11674, 'synset': 'thermograph.n.01', 'name': 'thermograph'}, {'id': 11675, 'synset': 'thermohydrometer.n.01', 'name': 'thermohydrometer'}, {'id': 11676, 'synset': 'thermojunction.n.01', 'name': 'thermojunction'}, {'id': 11677, 'synset': 'thermonuclear_reactor.n.01', 'name': 'thermonuclear_reactor'}, {'id': 11678, 'synset': 'thermopile.n.01', 'name': 'thermopile'}, {'id': 11679, 'synset': 'thigh_pad.n.01', 'name': 'thigh_pad'}, {'id': 11680, 'synset': 'thill.n.01', 'name': 'thill'}, {'id': 11681, 'synset': 'thinning_shears.n.01', 'name': 'thinning_shears'}, {'id': 11682, 'synset': 'third_base.n.01', 'name': 'third_base'}, {'id': 11683, 'synset': 'third_gear.n.01', 'name': 'third_gear'}, {'id': 11684, 'synset': 'third_rail.n.01', 'name': 'third_rail'}, {'id': 11685, 'synset': 'thong.n.03', 'name': 'thong'}, {'id': 11686, 'synset': 'thong.n.02', 'name': 'thong'}, {'id': 11687, 'synset': 'three-centered_arch.n.01', 'name': 'three-centered_arch'}, {'id': 11688, 'synset': 'three-decker.n.02', 'name': 'three-decker'}, {'id': 11689, 'synset': 'three-dimensional_radar.n.01', 'name': 'three-dimensional_radar'}, {'id': 11690, 'synset': 'three-piece_suit.n.01', 'name': 'three-piece_suit'}, {'id': 11691, 'synset': 'three-quarter_binding.n.01', 'name': 'three-quarter_binding'}, {'id': 11692, 'synset': 'three-way_switch.n.01', 'name': 'three-way_switch'}, {'id': 11693, 'synset': 'thresher.n.01', 'name': 'thresher'}, {'id': 11694, 'synset': 'threshing_floor.n.01', 'name': 'threshing_floor'}, {'id': 11695, 'synset': 'thriftshop.n.01', 'name': 'thriftshop'}, {'id': 11696, 'synset': 'throat_protector.n.01', 'name': 'throat_protector'}, {'id': 11697, 'synset': 'throne.n.01', 'name': 'throne'}, {'id': 11698, 'synset': 'thrust_bearing.n.01', 'name': 'thrust_bearing'}, {'id': 11699, 'synset': 'thruster.n.02', 'name': 'thruster'}, {'id': 11700, 'synset': 'thumb.n.02', 'name': 'thumb'}, {'id': 11701, 'synset': 'thumbhole.n.02', 'name': 'thumbhole'}, {'id': 11702, 'synset': 'thumbscrew.n.02', 'name': 'thumbscrew'}, {'id': 11703, 'synset': 'thumbstall.n.01', 'name': 'thumbstall'}, {'id': 11704, 'synset': 'thunderer.n.02', 'name': 'thunderer'}, {'id': 11705, 'synset': 'thwart.n.01', 'name': 'thwart'}, {'id': 11706, 'synset': 'ticking.n.02', 'name': 'ticking'}, {'id': 11707, 'synset': 'tickler_coil.n.01', 'name': 'tickler_coil'}, {'id': 11708, 'synset': 'tie.n.04', 'name': 'tie'}, {'id': 11709, 'synset': 'tie.n.08', 'name': 'tie'}, {'id': 11710, 'synset': 'tie_rack.n.01', 'name': 'tie_rack'}, {'id': 11711, 'synset': 'tie_rod.n.01', 'name': 'tie_rod'}, {'id': 11712, 'synset': 'tile.n.01', 'name': 'tile'}, {'id': 11713, 'synset': 'tile_cutter.n.01', 'name': 'tile_cutter'}, {'id': 11714, 'synset': 'tile_roof.n.01', 'name': 'tile_roof'}, {'id': 11715, 'synset': 'tiller.n.03', 'name': 'tiller'}, {'id': 11716, 'synset': 'tilter.n.02', 'name': 'tilter'}, {'id': 11717, 'synset': 'tilt-top_table.n.01', 'name': 'tilt-top_table'}, {'id': 11718, 'synset': 'timber.n.02', 'name': 'timber'}, {'id': 11719, 'synset': 'timber.n.03', 'name': 'timber'}, {'id': 11720, 'synset': 'timber_hitch.n.01', 'name': 'timber_hitch'}, {'id': 11721, 'synset': 'timbrel.n.01', 'name': 'timbrel'}, {'id': 11722, 'synset': 'time_bomb.n.02', 'name': 'time_bomb'}, {'id': 11723, 'synset': 'time_capsule.n.01', 'name': 'time_capsule'}, {'id': 11724, 'synset': 'time_clock.n.01', 'name': 'time_clock'}, {'id': 11725, 'synset': 'time-delay_measuring_instrument.n.01', 'name': 'time-delay_measuring_instrument'}, {'id': 11726, 'synset': 'time-fuse.n.01', 'name': 'time-fuse'}, {'id': 11727, 'synset': 'timepiece.n.01', 'name': 'timepiece'}, {'id': 11728, 'synset': 'timer.n.03', 'name': 'timer'}, {'id': 11729, 'synset': 'time-switch.n.01', 'name': 'time-switch'}, {'id': 11730, 'synset': 'tin.n.02', 'name': 'tin'}, {'id': 11731, 'synset': 'tinderbox.n.02', 'name': 'tinderbox'}, {'id': 11732, 'synset': 'tine.n.01', 'name': 'tine'}, {'id': 11733, 'synset': 'tippet.n.01', 'name': 'tippet'}, {'id': 11734, 'synset': 'tire_chain.n.01', 'name': 'tire_chain'}, {'id': 11735, 'synset': 'tire_iron.n.01', 'name': 'tire_iron'}, {'id': 11736, 'synset': 'titfer.n.01', 'name': 'titfer'}, {'id': 11737, 'synset': 'tithe_barn.n.01', 'name': 'tithe_barn'}, {'id': 11738, 'synset': 'titrator.n.01', 'name': 'titrator'}, {'id': 11739, 'synset': 'toasting_fork.n.01', 'name': 'toasting_fork'}, {'id': 11740, 'synset': 'toastrack.n.01', 'name': 'toastrack'}, {'id': 11741, 'synset': 'tobacco_pouch.n.01', 'name': 'tobacco_pouch'}, {'id': 11742, 'synset': 'tobacco_shop.n.01', 'name': 'tobacco_shop'}, {'id': 11743, 'synset': 'toboggan.n.01', 'name': 'toboggan'}, {'id': 11744, 'synset': 'toby.n.01', 'name': 'toby'}, {'id': 11745, 'synset': 'tocsin.n.02', 'name': 'tocsin'}, {'id': 11746, 'synset': 'toe.n.02', 'name': 'toe'}, {'id': 11747, 'synset': 'toecap.n.01', 'name': 'toecap'}, {'id': 11748, 'synset': 'toehold.n.02', 'name': 'toehold'}, {'id': 11749, 'synset': 'toga.n.01', 'name': 'toga'}, {'id': 11750, 'synset': 'toga_virilis.n.01', 'name': 'toga_virilis'}, {'id': 11751, 'synset': 'toggle.n.03', 'name': 'toggle'}, {'id': 11752, 'synset': 'toggle_bolt.n.01', 'name': 'toggle_bolt'}, {'id': 11753, 'synset': 'toggle_joint.n.01', 'name': 'toggle_joint'}, {'id': 11754, 'synset': 'toggle_switch.n.01', 'name': 'toggle_switch'}, {'id': 11755, 'synset': 'togs.n.01', 'name': 'togs'}, {'id': 11756, 'synset': 'toilet.n.01', 'name': 'toilet'}, {'id': 11757, 'synset': 'toilet_bag.n.01', 'name': 'toilet_bag'}, {'id': 11758, 'synset': 'toilet_bowl.n.01', 'name': 'toilet_bowl'}, {'id': 11759, 'synset': 'toilet_kit.n.01', 'name': 'toilet_kit'}, {'id': 11760, 'synset': 'toilet_powder.n.01', 'name': 'toilet_powder'}, {'id': 11761, 'synset': 'toiletry.n.01', 'name': 'toiletry'}, {'id': 11762, 'synset': 'toilet_seat.n.01', 'name': 'toilet_seat'}, {'id': 11763, 'synset': 'toilet_water.n.01', 'name': 'toilet_water'}, {'id': 11764, 'synset': 'tokamak.n.01', 'name': 'tokamak'}, {'id': 11765, 'synset': 'token.n.03', 'name': 'token'}, {'id': 11766, 'synset': 'tollbooth.n.01', 'name': 'tollbooth'}, {'id': 11767, 'synset': 'toll_bridge.n.01', 'name': 'toll_bridge'}, {'id': 11768, 'synset': 'tollgate.n.01', 'name': 'tollgate'}, {'id': 11769, 'synset': 'toll_line.n.01', 'name': 'toll_line'}, {'id': 11770, 'synset': 'tomahawk.n.01', 'name': 'tomahawk'}, {'id': 11771, 'synset': 'tommy_gun.n.01', 'name': 'Tommy_gun'}, {'id': 11772, 'synset': 'tomograph.n.01', 'name': 'tomograph'}, {'id': 11773, 'synset': 'tone_arm.n.01', 'name': 'tone_arm'}, {'id': 11774, 'synset': 'toner.n.03', 'name': 'toner'}, {'id': 11775, 'synset': 'tongue.n.07', 'name': 'tongue'}, {'id': 11776, 'synset': 'tongue_and_groove_joint.n.01', 'name': 'tongue_and_groove_joint'}, {'id': 11777, 'synset': 'tongue_depressor.n.01', 'name': 'tongue_depressor'}, {'id': 11778, 'synset': 'tonometer.n.01', 'name': 'tonometer'}, {'id': 11779, 'synset': 'tool.n.01', 'name': 'tool'}, {'id': 11780, 'synset': 'tool_bag.n.01', 'name': 'tool_bag'}, {'id': 11781, 'synset': 'toolshed.n.01', 'name': 'toolshed'}, {'id': 11782, 'synset': 'tooth.n.02', 'name': 'tooth'}, {'id': 11783, 'synset': 'tooth.n.05', 'name': 'tooth'}, {'id': 11784, 'synset': 'top.n.10', 'name': 'top'}, {'id': 11785, 'synset': 'topgallant.n.02', 'name': 'topgallant'}, {'id': 11786, 'synset': 'topgallant.n.01', 'name': 'topgallant'}, {'id': 11787, 'synset': 'topiary.n.01', 'name': 'topiary'}, {'id': 11788, 'synset': 'topknot.n.01', 'name': 'topknot'}, {'id': 11789, 'synset': 'topmast.n.01', 'name': 'topmast'}, {'id': 11790, 'synset': 'topper.n.05', 'name': 'topper'}, {'id': 11791, 'synset': 'topsail.n.01', 'name': 'topsail'}, {'id': 11792, 'synset': 'toque.n.01', 'name': 'toque'}, {'id': 11793, 'synset': 'torch.n.01', 'name': 'torch'}, {'id': 11794, 'synset': 'torpedo.n.06', 'name': 'torpedo'}, {'id': 11795, 'synset': 'torpedo.n.05', 'name': 'torpedo'}, {'id': 11796, 'synset': 'torpedo.n.03', 'name': 'torpedo'}, {'id': 11797, 'synset': 'torpedo_boat.n.01', 'name': 'torpedo_boat'}, {'id': 11798, 'synset': 'torpedo-boat_destroyer.n.01', 'name': 'torpedo-boat_destroyer'}, {'id': 11799, 'synset': 'torpedo_tube.n.01', 'name': 'torpedo_tube'}, {'id': 11800, 'synset': 'torque_converter.n.01', 'name': 'torque_converter'}, {'id': 11801, 'synset': 'torque_wrench.n.01', 'name': 'torque_wrench'}, {'id': 11802, 'synset': 'torture_chamber.n.01', 'name': 'torture_chamber'}, {'id': 11803, 'synset': 'totem_pole.n.01', 'name': 'totem_pole'}, {'id': 11804, 'synset': 'touch_screen.n.01', 'name': 'touch_screen'}, {'id': 11805, 'synset': 'toupee.n.01', 'name': 'toupee'}, {'id': 11806, 'synset': 'touring_car.n.01', 'name': 'touring_car'}, {'id': 11807, 'synset': 'tourist_class.n.01', 'name': 'tourist_class'}, {'id': 11808, 'synset': 'toweling.n.01', 'name': 'toweling'}, {'id': 11809, 'synset': 'towel_rail.n.01', 'name': 'towel_rail'}, {'id': 11810, 'synset': 'tower.n.01', 'name': 'tower'}, {'id': 11811, 'synset': 'town_hall.n.01', 'name': 'town_hall'}, {'id': 11812, 'synset': 'towpath.n.01', 'name': 'towpath'}, {'id': 11813, 'synset': 'toy_box.n.01', 'name': 'toy_box'}, {'id': 11814, 'synset': 'toyshop.n.01', 'name': 'toyshop'}, {'id': 11815, 'synset': 'trace_detector.n.01', 'name': 'trace_detector'}, {'id': 11816, 'synset': 'track.n.09', 'name': 'track'}, {'id': 11817, 'synset': 'track.n.08', 'name': 'track'}, {'id': 11818, 'synset': 'trackball.n.01', 'name': 'trackball'}, {'id': 11819, 'synset': 'tracked_vehicle.n.01', 'name': 'tracked_vehicle'}, {'id': 11820, 'synset': 'tract_house.n.01', 'name': 'tract_house'}, {'id': 11821, 'synset': 'tract_housing.n.01', 'name': 'tract_housing'}, {'id': 11822, 'synset': 'traction_engine.n.01', 'name': 'traction_engine'}, {'id': 11823, 'synset': 'tractor.n.02', 'name': 'tractor'}, {'id': 11824, 'synset': 'trailer.n.04', 'name': 'trailer'}, {'id': 11825, 'synset': 'trailer.n.03', 'name': 'trailer'}, {'id': 11826, 'synset': 'trailer_camp.n.01', 'name': 'trailer_camp'}, {'id': 11827, 'synset': 'trailing_edge.n.01', 'name': 'trailing_edge'}, {'id': 11828, 'synset': 'tramline.n.01', 'name': 'tramline'}, {'id': 11829, 'synset': 'trammel.n.02', 'name': 'trammel'}, {'id': 11830, 'synset': 'tramp_steamer.n.01', 'name': 'tramp_steamer'}, {'id': 11831, 'synset': 'tramway.n.01', 'name': 'tramway'}, {'id': 11832, 'synset': 'transdermal_patch.n.01', 'name': 'transdermal_patch'}, {'id': 11833, 'synset': 'transept.n.01', 'name': 'transept'}, {'id': 11834, 'synset': 'transformer.n.01', 'name': 'transformer'}, {'id': 11835, 'synset': 'transistor.n.01', 'name': 'transistor'}, {'id': 11836, 'synset': 'transit_instrument.n.01', 'name': 'transit_instrument'}, {'id': 11837, 'synset': 'transmission.n.05', 'name': 'transmission'}, {'id': 11838, 'synset': 'transmission_shaft.n.01', 'name': 'transmission_shaft'}, {'id': 11839, 'synset': 'transmitter.n.03', 'name': 'transmitter'}, {'id': 11840, 'synset': 'transom.n.02', 'name': 'transom'}, {'id': 11841, 'synset': 'transom.n.01', 'name': 'transom'}, {'id': 11842, 'synset': 'transponder.n.01', 'name': 'transponder'}, {'id': 11843, 'synset': 'transporter.n.02', 'name': 'transporter'}, {'id': 11844, 'synset': 'transporter.n.01', 'name': 'transporter'}, {'id': 11845, 'synset': 'transport_ship.n.01', 'name': 'transport_ship'}, {'id': 11846, 'synset': 'trap.n.01', 'name': 'trap'}, {'id': 11847, 'synset': 'trap_door.n.01', 'name': 'trap_door'}, {'id': 11848, 'synset': 'trapeze.n.01', 'name': 'trapeze'}, {'id': 11849, 'synset': 'trave.n.01', 'name': 'trave'}, {'id': 11850, 'synset': 'travel_iron.n.01', 'name': 'travel_iron'}, {'id': 11851, 'synset': 'trawl.n.02', 'name': 'trawl'}, {'id': 11852, 'synset': 'trawl.n.01', 'name': 'trawl'}, {'id': 11853, 'synset': 'trawler.n.02', 'name': 'trawler'}, {'id': 11854, 'synset': 'tray_cloth.n.01', 'name': 'tray_cloth'}, {'id': 11855, 'synset': 'tread.n.04', 'name': 'tread'}, {'id': 11856, 'synset': 'tread.n.03', 'name': 'tread'}, {'id': 11857, 'synset': 'treadmill.n.02', 'name': 'treadmill'}, {'id': 11858, 'synset': 'treadmill.n.01', 'name': 'treadmill'}, {'id': 11859, 'synset': 'treasure_chest.n.01', 'name': 'treasure_chest'}, {'id': 11860, 'synset': 'treasure_ship.n.01', 'name': 'treasure_ship'}, {'id': 11861, 'synset': 'treenail.n.01', 'name': 'treenail'}, {'id': 11862, 'synset': 'trefoil_arch.n.01', 'name': 'trefoil_arch'}, {'id': 11863, 'synset': 'trellis.n.01', 'name': 'trellis'}, {'id': 11864, 'synset': 'trench.n.01', 'name': 'trench'}, {'id': 11865, 'synset': 'trench_knife.n.01', 'name': 'trench_knife'}, {'id': 11866, 'synset': 'trepan.n.02', 'name': 'trepan'}, {'id': 11867, 'synset': 'trepan.n.01', 'name': 'trepan'}, {'id': 11868, 'synset': 'trestle.n.02', 'name': 'trestle'}, {'id': 11869, 'synset': 'trestle.n.01', 'name': 'trestle'}, {'id': 11870, 'synset': 'trestle_bridge.n.01', 'name': 'trestle_bridge'}, {'id': 11871, 'synset': 'trestle_table.n.01', 'name': 'trestle_table'}, {'id': 11872, 'synset': 'trestlework.n.01', 'name': 'trestlework'}, {'id': 11873, 'synset': 'trews.n.01', 'name': 'trews'}, {'id': 11874, 'synset': 'trial_balloon.n.02', 'name': 'trial_balloon'}, {'id': 11875, 'synset': 'triangle.n.04', 'name': 'triangle'}, {'id': 11876, 'synset': 'triclinium.n.02', 'name': 'triclinium'}, {'id': 11877, 'synset': 'triclinium.n.01', 'name': 'triclinium'}, {'id': 11878, 'synset': 'tricorn.n.01', 'name': 'tricorn'}, {'id': 11879, 'synset': 'tricot.n.01', 'name': 'tricot'}, {'id': 11880, 'synset': 'trident.n.01', 'name': 'trident'}, {'id': 11881, 'synset': 'trigger.n.02', 'name': 'trigger'}, {'id': 11882, 'synset': 'trimaran.n.01', 'name': 'trimaran'}, {'id': 11883, 'synset': 'trimmer.n.02', 'name': 'trimmer'}, {'id': 11884, 'synset': 'trimmer_arch.n.01', 'name': 'trimmer_arch'}, {'id': 11885, 'synset': 'triode.n.01', 'name': 'triode'}, {'id': 11886, 'synset': 'triptych.n.01', 'name': 'triptych'}, {'id': 11887, 'synset': 'trip_wire.n.02', 'name': 'trip_wire'}, {'id': 11888, 'synset': 'trireme.n.01', 'name': 'trireme'}, {'id': 11889, 'synset': 'triskelion.n.01', 'name': 'triskelion'}, {'id': 11890, 'synset': 'triumphal_arch.n.01', 'name': 'triumphal_arch'}, {'id': 11891, 'synset': 'trivet.n.02', 'name': 'trivet'}, {'id': 11892, 'synset': 'trivet.n.01', 'name': 'trivet'}, {'id': 11893, 'synset': 'troika.n.01', 'name': 'troika'}, {'id': 11894, 'synset': 'troll.n.03', 'name': 'troll'}, {'id': 11895, 'synset': 'trolleybus.n.01', 'name': 'trolleybus'}, {'id': 11896, 'synset': 'trombone.n.01', 'name': 'trombone'}, {'id': 11897, 'synset': 'troop_carrier.n.01', 'name': 'troop_carrier'}, {'id': 11898, 'synset': 'troopship.n.01', 'name': 'troopship'}, {'id': 11899, 'synset': 'trophy_case.n.01', 'name': 'trophy_case'}, {'id': 11900, 'synset': 'trough.n.05', 'name': 'trough'}, {'id': 11901, 'synset': 'trouser.n.02', 'name': 'trouser'}, {'id': 11902, 'synset': 'trouser_cuff.n.01', 'name': 'trouser_cuff'}, {'id': 11903, 'synset': 'trouser_press.n.01', 'name': 'trouser_press'}, {'id': 11904, 'synset': 'trousseau.n.01', 'name': 'trousseau'}, {'id': 11905, 'synset': 'trowel.n.01', 'name': 'trowel'}, {'id': 11906, 'synset': 'trumpet_arch.n.01', 'name': 'trumpet_arch'}, {'id': 11907, 'synset': 'truncheon.n.01', 'name': 'truncheon'}, {'id': 11908, 'synset': 'trundle_bed.n.01', 'name': 'trundle_bed'}, {'id': 11909, 'synset': 'trunk_hose.n.01', 'name': 'trunk_hose'}, {'id': 11910, 'synset': 'trunk_lid.n.01', 'name': 'trunk_lid'}, {'id': 11911, 'synset': 'trunk_line.n.02', 'name': 'trunk_line'}, {'id': 11912, 'synset': 'truss.n.02', 'name': 'truss'}, {'id': 11913, 'synset': 'truss_bridge.n.01', 'name': 'truss_bridge'}, {'id': 11914, 'synset': 'try_square.n.01', 'name': 'try_square'}, {'id': 11915, 'synset': 't-square.n.01', 'name': 'T-square'}, {'id': 11916, 'synset': 'tube.n.02', 'name': 'tube'}, {'id': 11917, 'synset': 'tuck_box.n.01', 'name': 'tuck_box'}, {'id': 11918, 'synset': 'tucker.n.04', 'name': 'tucker'}, {'id': 11919, 'synset': 'tucker-bag.n.01', 'name': 'tucker-bag'}, {'id': 11920, 'synset': 'tuck_shop.n.01', 'name': 'tuck_shop'}, {'id': 11921, 'synset': 'tudor_arch.n.01', 'name': 'Tudor_arch'}, {'id': 11922, 'synset': 'tudung.n.01', 'name': 'tudung'}, {'id': 11923, 'synset': 'tugboat.n.01', 'name': 'tugboat'}, {'id': 11924, 'synset': 'tulle.n.01', 'name': 'tulle'}, {'id': 11925, 'synset': 'tumble-dryer.n.01', 'name': 'tumble-dryer'}, {'id': 11926, 'synset': 'tumbler.n.02', 'name': 'tumbler'}, {'id': 11927, 'synset': 'tumbrel.n.01', 'name': 'tumbrel'}, {'id': 11928, 'synset': 'tun.n.01', 'name': 'tun'}, {'id': 11929, 'synset': 'tunic.n.02', 'name': 'tunic'}, {'id': 11930, 'synset': 'tuning_fork.n.01', 'name': 'tuning_fork'}, {'id': 11931, 'synset': 'tupik.n.01', 'name': 'tupik'}, {'id': 11932, 'synset': 'turbine.n.01', 'name': 'turbine'}, {'id': 11933, 'synset': 'turbogenerator.n.01', 'name': 'turbogenerator'}, {'id': 11934, 'synset': 'tureen.n.01', 'name': 'tureen'}, {'id': 11935, 'synset': 'turkish_bath.n.01', 'name': 'Turkish_bath'}, {'id': 11936, 'synset': 'turkish_towel.n.01', 'name': 'Turkish_towel'}, {'id': 11937, 'synset': "turk's_head.n.01", 'name': "Turk's_head"}, {'id': 11938, 'synset': 'turnbuckle.n.01', 'name': 'turnbuckle'}, {'id': 11939, 'synset': 'turner.n.08', 'name': 'turner'}, {'id': 11940, 'synset': 'turnery.n.01', 'name': 'turnery'}, {'id': 11941, 'synset': 'turnpike.n.01', 'name': 'turnpike'}, {'id': 11942, 'synset': 'turnspit.n.01', 'name': 'turnspit'}, {'id': 11943, 'synset': 'turnstile.n.01', 'name': 'turnstile'}, {'id': 11944, 'synset': 'turntable.n.01', 'name': 'turntable'}, {'id': 11945, 'synset': 'turntable.n.02', 'name': 'turntable'}, {'id': 11946, 'synset': 'turret.n.01', 'name': 'turret'}, {'id': 11947, 'synset': 'turret_clock.n.01', 'name': 'turret_clock'}, {'id': 11948, 'synset': 'tweed.n.01', 'name': 'tweed'}, {'id': 11949, 'synset': 'tweeter.n.01', 'name': 'tweeter'}, {'id': 11950, 'synset': 'twenty-two.n.02', 'name': 'twenty-two'}, {'id': 11951, 'synset': 'twenty-two_pistol.n.01', 'name': 'twenty-two_pistol'}, {'id': 11952, 'synset': 'twenty-two_rifle.n.01', 'name': 'twenty-two_rifle'}, {'id': 11953, 'synset': 'twill.n.02', 'name': 'twill'}, {'id': 11954, 'synset': 'twill.n.01', 'name': 'twill'}, {'id': 11955, 'synset': 'twin_bed.n.01', 'name': 'twin_bed'}, {'id': 11956, 'synset': 'twinjet.n.01', 'name': 'twinjet'}, {'id': 11957, 'synset': 'twist_bit.n.01', 'name': 'twist_bit'}, {'id': 11958, 'synset': 'two-by-four.n.01', 'name': 'two-by-four'}, {'id': 11959, 'synset': 'two-man_tent.n.01', 'name': 'two-man_tent'}, {'id': 11960, 'synset': 'two-piece.n.01', 'name': 'two-piece'}, {'id': 11961, 'synset': 'typesetting_machine.n.01', 'name': 'typesetting_machine'}, {'id': 11962, 'synset': 'typewriter_carriage.n.01', 'name': 'typewriter_carriage'}, {'id': 11963, 'synset': 'typewriter_keyboard.n.01', 'name': 'typewriter_keyboard'}, {'id': 11964, 'synset': 'tyrolean.n.02', 'name': 'tyrolean'}, {'id': 11965, 'synset': 'uke.n.01', 'name': 'uke'}, {'id': 11966, 'synset': 'ulster.n.02', 'name': 'ulster'}, {'id': 11967, 'synset': 'ultracentrifuge.n.01', 'name': 'ultracentrifuge'}, {'id': 11968, 'synset': 'ultramicroscope.n.01', 'name': 'ultramicroscope'}, {'id': 11969, 'synset': 'ultrasuede.n.01', 'name': 'Ultrasuede'}, {'id': 11970, 'synset': 'ultraviolet_lamp.n.01', 'name': 'ultraviolet_lamp'}, {'id': 11971, 'synset': 'umbrella_tent.n.01', 'name': 'umbrella_tent'}, {'id': 11972, 'synset': 'undercarriage.n.01', 'name': 'undercarriage'}, {'id': 11973, 'synset': 'undercoat.n.01', 'name': 'undercoat'}, {'id': 11974, 'synset': 'undergarment.n.01', 'name': 'undergarment'}, {'id': 11975, 'synset': 'underpants.n.01', 'name': 'underpants'}, {'id': 11976, 'synset': 'undies.n.01', 'name': 'undies'}, {'id': 11977, 'synset': 'uneven_parallel_bars.n.01', 'name': 'uneven_parallel_bars'}, {'id': 11978, 'synset': 'uniform.n.01', 'name': 'uniform'}, {'id': 11979, 'synset': 'universal_joint.n.01', 'name': 'universal_joint'}, {'id': 11980, 'synset': 'university.n.02', 'name': 'university'}, {'id': 11981, 'synset': 'upholstery.n.01', 'name': 'upholstery'}, {'id': 11982, 'synset': 'upholstery_material.n.01', 'name': 'upholstery_material'}, {'id': 11983, 'synset': 'upholstery_needle.n.01', 'name': 'upholstery_needle'}, {'id': 11984, 'synset': 'uplift.n.02', 'name': 'uplift'}, {'id': 11985, 'synset': 'upper_berth.n.01', 'name': 'upper_berth'}, {'id': 11986, 'synset': 'upright.n.02', 'name': 'upright'}, {'id': 11987, 'synset': 'upset.n.04', 'name': 'upset'}, {'id': 11988, 'synset': 'upstairs.n.01', 'name': 'upstairs'}, {'id': 11989, 'synset': 'urceole.n.01', 'name': 'urceole'}, {'id': 11990, 'synset': 'urn.n.02', 'name': 'urn'}, {'id': 11991, 'synset': 'used-car.n.01', 'name': 'used-car'}, {'id': 11992, 'synset': 'utensil.n.01', 'name': 'utensil'}, {'id': 11993, 'synset': 'uzi.n.01', 'name': 'Uzi'}, {'id': 11994, 'synset': 'vacation_home.n.01', 'name': 'vacation_home'}, {'id': 11995, 'synset': 'vacuum_chamber.n.01', 'name': 'vacuum_chamber'}, {'id': 11996, 'synset': 'vacuum_flask.n.01', 'name': 'vacuum_flask'}, {'id': 11997, 'synset': 'vacuum_gauge.n.01', 'name': 'vacuum_gauge'}, {'id': 11998, 'synset': 'valenciennes.n.02', 'name': 'Valenciennes'}, {'id': 11999, 'synset': 'valise.n.01', 'name': 'valise'}, {'id': 12000, 'synset': 'valve.n.03', 'name': 'valve'}, {'id': 12001, 'synset': 'valve.n.02', 'name': 'valve'}, {'id': 12002, 'synset': 'valve-in-head_engine.n.01', 'name': 'valve-in-head_engine'}, {'id': 12003, 'synset': 'vambrace.n.01', 'name': 'vambrace'}, {'id': 12004, 'synset': 'van.n.05', 'name': 'van'}, {'id': 12005, 'synset': 'van.n.04', 'name': 'van'}, {'id': 12006, 'synset': 'vane.n.02', 'name': 'vane'}, {'id': 12007, 'synset': 'vaporizer.n.01', 'name': 'vaporizer'}, {'id': 12008, 'synset': 'variable-pitch_propeller.n.01', 'name': 'variable-pitch_propeller'}, {'id': 12009, 'synset': 'variometer.n.01', 'name': 'variometer'}, {'id': 12010, 'synset': 'varnish.n.01', 'name': 'varnish'}, {'id': 12011, 'synset': 'vault.n.03', 'name': 'vault'}, {'id': 12012, 'synset': 'vault.n.02', 'name': 'vault'}, {'id': 12013, 'synset': 'vaulting_horse.n.01', 'name': 'vaulting_horse'}, {'id': 12014, 'synset': 'vehicle.n.01', 'name': 'vehicle'}, {'id': 12015, 'synset': 'velcro.n.01', 'name': 'Velcro'}, {'id': 12016, 'synset': 'velocipede.n.01', 'name': 'velocipede'}, {'id': 12017, 'synset': 'velour.n.01', 'name': 'velour'}, {'id': 12018, 'synset': 'velvet.n.01', 'name': 'velvet'}, {'id': 12019, 'synset': 'velveteen.n.01', 'name': 'velveteen'}, {'id': 12020, 'synset': 'veneer.n.01', 'name': 'veneer'}, {'id': 12021, 'synset': 'venetian_blind.n.01', 'name': 'Venetian_blind'}, {'id': 12022, 'synset': 'venn_diagram.n.01', 'name': 'Venn_diagram'}, {'id': 12023, 'synset': 'ventilation.n.02', 'name': 'ventilation'}, {'id': 12024, 'synset': 'ventilation_shaft.n.01', 'name': 'ventilation_shaft'}, {'id': 12025, 'synset': 'ventilator.n.01', 'name': 'ventilator'}, {'id': 12026, 'synset': 'veranda.n.01', 'name': 'veranda'}, {'id': 12027, 'synset': 'verdigris.n.02', 'name': 'verdigris'}, {'id': 12028, 'synset': 'vernier_caliper.n.01', 'name': 'vernier_caliper'}, {'id': 12029, 'synset': 'vernier_scale.n.01', 'name': 'vernier_scale'}, {'id': 12030, 'synset': 'vertical_file.n.01', 'name': 'vertical_file'}, {'id': 12031, 'synset': 'vertical_stabilizer.n.01', 'name': 'vertical_stabilizer'}, {'id': 12032, 'synset': 'vertical_tail.n.01', 'name': 'vertical_tail'}, {'id': 12033, 'synset': 'very_pistol.n.01', 'name': 'Very_pistol'}, {'id': 12034, 'synset': 'vessel.n.02', 'name': 'vessel'}, {'id': 12035, 'synset': 'vessel.n.03', 'name': 'vessel'}, {'id': 12036, 'synset': 'vestiture.n.01', 'name': 'vestiture'}, {'id': 12037, 'synset': 'vestment.n.01', 'name': 'vestment'}, {'id': 12038, 'synset': 'vest_pocket.n.01', 'name': 'vest_pocket'}, {'id': 12039, 'synset': 'vestry.n.02', 'name': 'vestry'}, {'id': 12040, 'synset': 'viaduct.n.01', 'name': 'viaduct'}, {'id': 12041, 'synset': 'vibraphone.n.01', 'name': 'vibraphone'}, {'id': 12042, 'synset': 'vibrator.n.02', 'name': 'vibrator'}, {'id': 12043, 'synset': 'vibrator.n.01', 'name': 'vibrator'}, {'id': 12044, 'synset': 'victrola.n.01', 'name': 'Victrola'}, {'id': 12045, 'synset': 'vicuna.n.02', 'name': 'vicuna'}, {'id': 12046, 'synset': 'videocassette.n.01', 'name': 'videocassette'}, {'id': 12047, 'synset': 'videocassette_recorder.n.01', 'name': 'videocassette_recorder'}, {'id': 12048, 'synset': 'videodisk.n.01', 'name': 'videodisk'}, {'id': 12049, 'synset': 'video_recording.n.01', 'name': 'video_recording'}, {'id': 12050, 'synset': 'videotape.n.02', 'name': 'videotape'}, {'id': 12051, 'synset': 'vigil_light.n.01', 'name': 'vigil_light'}, {'id': 12052, 'synset': 'villa.n.04', 'name': 'villa'}, {'id': 12053, 'synset': 'villa.n.03', 'name': 'villa'}, {'id': 12054, 'synset': 'villa.n.02', 'name': 'villa'}, {'id': 12055, 'synset': 'viol.n.01', 'name': 'viol'}, {'id': 12056, 'synset': 'viola.n.03', 'name': 'viola'}, {'id': 12057, 'synset': 'viola_da_braccio.n.01', 'name': 'viola_da_braccio'}, {'id': 12058, 'synset': 'viola_da_gamba.n.01', 'name': 'viola_da_gamba'}, {'id': 12059, 'synset': "viola_d'amore.n.01", 'name': "viola_d'amore"}, {'id': 12060, 'synset': 'virginal.n.01', 'name': 'virginal'}, {'id': 12061, 'synset': 'viscometer.n.01', 'name': 'viscometer'}, {'id': 12062, 'synset': 'viscose_rayon.n.01', 'name': 'viscose_rayon'}, {'id': 12063, 'synset': 'vise.n.01', 'name': 'vise'}, {'id': 12064, 'synset': 'visor.n.01', 'name': 'visor'}, {'id': 12065, 'synset': 'visual_display_unit.n.01', 'name': 'visual_display_unit'}, {'id': 12066, 'synset': 'vivarium.n.01', 'name': 'vivarium'}, {'id': 12067, 'synset': 'viyella.n.01', 'name': 'Viyella'}, {'id': 12068, 'synset': 'voile.n.01', 'name': 'voile'}, {'id': 12069, 'synset': 'volleyball_net.n.01', 'name': 'volleyball_net'}, {'id': 12070, 'synset': 'voltage_regulator.n.01', 'name': 'voltage_regulator'}, {'id': 12071, 'synset': 'voltaic_cell.n.01', 'name': 'voltaic_cell'}, {'id': 12072, 'synset': 'voltaic_pile.n.01', 'name': 'voltaic_pile'}, {'id': 12073, 'synset': 'voltmeter.n.01', 'name': 'voltmeter'}, {'id': 12074, 'synset': 'vomitory.n.01', 'name': 'vomitory'}, {'id': 12075, 'synset': 'von_neumann_machine.n.01', 'name': 'von_Neumann_machine'}, {'id': 12076, 'synset': 'voting_booth.n.01', 'name': 'voting_booth'}, {'id': 12077, 'synset': 'voting_machine.n.01', 'name': 'voting_machine'}, {'id': 12078, 'synset': 'voussoir.n.01', 'name': 'voussoir'}, {'id': 12079, 'synset': 'vox_angelica.n.01', 'name': 'vox_angelica'}, {'id': 12080, 'synset': 'vox_humana.n.01', 'name': 'vox_humana'}, {'id': 12081, 'synset': 'waders.n.01', 'name': 'waders'}, {'id': 12082, 'synset': 'wading_pool.n.01', 'name': 'wading_pool'}, {'id': 12083, 'synset': 'wagon.n.04', 'name': 'wagon'}, {'id': 12084, 'synset': 'wagon_tire.n.01', 'name': 'wagon_tire'}, {'id': 12085, 'synset': 'wain.n.03', 'name': 'wain'}, {'id': 12086, 'synset': 'wainscot.n.02', 'name': 'wainscot'}, {'id': 12087, 'synset': 'wainscoting.n.01', 'name': 'wainscoting'}, {'id': 12088, 'synset': 'waist_pack.n.01', 'name': 'waist_pack'}, {'id': 12089, 'synset': 'walker.n.06', 'name': 'walker'}, {'id': 12090, 'synset': 'walker.n.05', 'name': 'walker'}, {'id': 12091, 'synset': 'walker.n.04', 'name': 'walker'}, {'id': 12092, 'synset': 'walkie-talkie.n.01', 'name': 'walkie-talkie'}, {'id': 12093, 'synset': 'walk-in.n.04', 'name': 'walk-in'}, {'id': 12094, 'synset': 'walking_shoe.n.01', 'name': 'walking_shoe'}, {'id': 12095, 'synset': 'walkman.n.01', 'name': 'Walkman'}, {'id': 12096, 'synset': 'walk-up_apartment.n.01', 'name': 'walk-up_apartment'}, {'id': 12097, 'synset': 'wall.n.01', 'name': 'wall'}, {'id': 12098, 'synset': 'wall.n.07', 'name': 'wall'}, {'id': 12099, 'synset': 'wall_tent.n.01', 'name': 'wall_tent'}, {'id': 12100, 'synset': 'wall_unit.n.01', 'name': 'wall_unit'}, {'id': 12101, 'synset': 'wand.n.01', 'name': 'wand'}, {'id': 12102, 'synset': 'wankel_engine.n.01', 'name': 'Wankel_engine'}, {'id': 12103, 'synset': 'ward.n.03', 'name': 'ward'}, {'id': 12104, 'synset': 'wardroom.n.01', 'name': 'wardroom'}, {'id': 12105, 'synset': 'warehouse.n.01', 'name': 'warehouse'}, {'id': 12106, 'synset': 'warming_pan.n.01', 'name': 'warming_pan'}, {'id': 12107, 'synset': 'war_paint.n.02', 'name': 'war_paint'}, {'id': 12108, 'synset': 'warplane.n.01', 'name': 'warplane'}, {'id': 12109, 'synset': 'war_room.n.01', 'name': 'war_room'}, {'id': 12110, 'synset': 'warship.n.01', 'name': 'warship'}, {'id': 12111, 'synset': 'wash.n.01', 'name': 'wash'}, {'id': 12112, 'synset': 'wash-and-wear.n.01', 'name': 'wash-and-wear'}, {'id': 12113, 'synset': 'washbasin.n.02', 'name': 'washbasin'}, {'id': 12114, 'synset': 'washboard.n.02', 'name': 'washboard'}, {'id': 12115, 'synset': 'washboard.n.01', 'name': 'washboard'}, {'id': 12116, 'synset': 'washer.n.02', 'name': 'washer'}, {'id': 12117, 'synset': 'washhouse.n.01', 'name': 'washhouse'}, {'id': 12118, 'synset': 'washroom.n.01', 'name': 'washroom'}, {'id': 12119, 'synset': 'washstand.n.01', 'name': 'washstand'}, {'id': 12120, 'synset': 'washtub.n.01', 'name': 'washtub'}, {'id': 12121, 'synset': 'wastepaper_basket.n.01', 'name': 'wastepaper_basket'}, {'id': 12122, 'synset': 'watch_cap.n.01', 'name': 'watch_cap'}, {'id': 12123, 'synset': 'watch_case.n.01', 'name': 'watch_case'}, {'id': 12124, 'synset': 'watch_glass.n.01', 'name': 'watch_glass'}, {'id': 12125, 'synset': 'watchtower.n.01', 'name': 'watchtower'}, {'id': 12126, 'synset': 'water-base_paint.n.01', 'name': 'water-base_paint'}, {'id': 12127, 'synset': 'water_bed.n.01', 'name': 'water_bed'}, {'id': 12128, 'synset': 'water_butt.n.01', 'name': 'water_butt'}, {'id': 12129, 'synset': 'water_cart.n.01', 'name': 'water_cart'}, {'id': 12130, 'synset': 'water_chute.n.01', 'name': 'water_chute'}, {'id': 12131, 'synset': 'water_closet.n.01', 'name': 'water_closet'}, {'id': 12132, 'synset': 'watercolor.n.02', 'name': 'watercolor'}, {'id': 12133, 'synset': 'water-cooled_reactor.n.01', 'name': 'water-cooled_reactor'}, {'id': 12134, 'synset': 'water_filter.n.01', 'name': 'water_filter'}, {'id': 12135, 'synset': 'water_gauge.n.01', 'name': 'water_gauge'}, {'id': 12136, 'synset': 'water_glass.n.02', 'name': 'water_glass'}, {'id': 12137, 'synset': 'water_hazard.n.01', 'name': 'water_hazard'}, {'id': 12138, 'synset': 'watering_cart.n.01', 'name': 'watering_cart'}, {'id': 12139, 'synset': 'water_jacket.n.01', 'name': 'water_jacket'}, {'id': 12140, 'synset': 'water_jump.n.01', 'name': 'water_jump'}, {'id': 12141, 'synset': 'water_level.n.04', 'name': 'water_level'}, {'id': 12142, 'synset': 'water_meter.n.01', 'name': 'water_meter'}, {'id': 12143, 'synset': 'water_mill.n.01', 'name': 'water_mill'}, {'id': 12144, 'synset': 'waterproof.n.01', 'name': 'waterproof'}, {'id': 12145, 'synset': 'waterproofing.n.02', 'name': 'waterproofing'}, {'id': 12146, 'synset': 'water_pump.n.01', 'name': 'water_pump'}, {'id': 12147, 'synset': 'waterspout.n.03', 'name': 'waterspout'}, {'id': 12148, 'synset': 'water_wagon.n.01', 'name': 'water_wagon'}, {'id': 12149, 'synset': 'waterwheel.n.02', 'name': 'waterwheel'}, {'id': 12150, 'synset': 'waterwheel.n.01', 'name': 'waterwheel'}, {'id': 12151, 'synset': 'water_wings.n.01', 'name': 'water_wings'}, {'id': 12152, 'synset': 'waterworks.n.02', 'name': 'waterworks'}, {'id': 12153, 'synset': 'wattmeter.n.01', 'name': 'wattmeter'}, {'id': 12154, 'synset': 'waxwork.n.02', 'name': 'waxwork'}, {'id': 12155, 'synset': 'ways.n.01', 'name': 'ways'}, {'id': 12156, 'synset': 'weapon.n.01', 'name': 'weapon'}, {'id': 12157, 'synset': 'weaponry.n.01', 'name': 'weaponry'}, {'id': 12158, 'synset': 'weapons_carrier.n.01', 'name': 'weapons_carrier'}, {'id': 12159, 'synset': 'weathercock.n.01', 'name': 'weathercock'}, {'id': 12160, 'synset': 'weatherglass.n.01', 'name': 'weatherglass'}, {'id': 12161, 'synset': 'weather_satellite.n.01', 'name': 'weather_satellite'}, {'id': 12162, 'synset': 'weather_ship.n.01', 'name': 'weather_ship'}, {'id': 12163, 'synset': 'web.n.02', 'name': 'web'}, {'id': 12164, 'synset': 'web.n.06', 'name': 'web'}, {'id': 12165, 'synset': 'webbing.n.03', 'name': 'webbing'}, {'id': 12166, 'synset': 'wedge.n.06', 'name': 'wedge'}, {'id': 12167, 'synset': 'wedge.n.05', 'name': 'wedge'}, {'id': 12168, 'synset': 'wedgie.n.01', 'name': 'wedgie'}, {'id': 12169, 'synset': 'wedgwood.n.02', 'name': 'Wedgwood'}, {'id': 12170, 'synset': 'weeder.n.02', 'name': 'weeder'}, {'id': 12171, 'synset': 'weeds.n.01', 'name': 'weeds'}, {'id': 12172, 'synset': 'weekender.n.02', 'name': 'weekender'}, {'id': 12173, 'synset': 'weighbridge.n.01', 'name': 'weighbridge'}, {'id': 12174, 'synset': 'weight.n.02', 'name': 'weight'}, {'id': 12175, 'synset': 'weir.n.01', 'name': 'weir'}, {'id': 12176, 'synset': 'weir.n.02', 'name': 'weir'}, {'id': 12177, 'synset': 'welcome_wagon.n.01', 'name': 'welcome_wagon'}, {'id': 12178, 'synset': 'weld.n.03', 'name': 'weld'}, {'id': 12179, 'synset': "welder's_mask.n.01", 'name': "welder's_mask"}, {'id': 12180, 'synset': 'weldment.n.01', 'name': 'weldment'}, {'id': 12181, 'synset': 'well.n.02', 'name': 'well'}, {'id': 12182, 'synset': 'wellhead.n.02', 'name': 'wellhead'}, {'id': 12183, 'synset': 'welt.n.02', 'name': 'welt'}, {'id': 12184, 'synset': 'weston_cell.n.01', 'name': 'Weston_cell'}, {'id': 12185, 'synset': 'wet_bar.n.01', 'name': 'wet_bar'}, {'id': 12186, 'synset': 'wet-bulb_thermometer.n.01', 'name': 'wet-bulb_thermometer'}, {'id': 12187, 'synset': 'wet_cell.n.01', 'name': 'wet_cell'}, {'id': 12188, 'synset': 'wet_fly.n.01', 'name': 'wet_fly'}, {'id': 12189, 'synset': 'whaleboat.n.01', 'name': 'whaleboat'}, {'id': 12190, 'synset': 'whaler.n.02', 'name': 'whaler'}, {'id': 12191, 'synset': 'whaling_gun.n.01', 'name': 'whaling_gun'}, {'id': 12192, 'synset': 'wheel.n.04', 'name': 'wheel'}, {'id': 12193, 'synset': 'wheel_and_axle.n.01', 'name': 'wheel_and_axle'}, {'id': 12194, 'synset': 'wheeled_vehicle.n.01', 'name': 'wheeled_vehicle'}, {'id': 12195, 'synset': 'wheelwork.n.01', 'name': 'wheelwork'}, {'id': 12196, 'synset': 'wherry.n.02', 'name': 'wherry'}, {'id': 12197, 'synset': 'wherry.n.01', 'name': 'wherry'}, {'id': 12198, 'synset': 'whetstone.n.01', 'name': 'whetstone'}, {'id': 12199, 'synset': 'whiffletree.n.01', 'name': 'whiffletree'}, {'id': 12200, 'synset': 'whip.n.01', 'name': 'whip'}, {'id': 12201, 'synset': 'whipcord.n.02', 'name': 'whipcord'}, {'id': 12202, 'synset': 'whipping_post.n.01', 'name': 'whipping_post'}, {'id': 12203, 'synset': 'whipstitch.n.01', 'name': 'whipstitch'}, {'id': 12204, 'synset': 'whirler.n.02', 'name': 'whirler'}, {'id': 12205, 'synset': 'whisk.n.02', 'name': 'whisk'}, {'id': 12206, 'synset': 'whisk.n.01', 'name': 'whisk'}, {'id': 12207, 'synset': 'whiskey_bottle.n.01', 'name': 'whiskey_bottle'}, {'id': 12208, 'synset': 'whiskey_jug.n.01', 'name': 'whiskey_jug'}, {'id': 12209, 'synset': 'whispering_gallery.n.01', 'name': 'whispering_gallery'}, {'id': 12210, 'synset': 'whistle.n.04', 'name': 'whistle'}, {'id': 12211, 'synset': 'white.n.11', 'name': 'white'}, {'id': 12212, 'synset': 'white_goods.n.01', 'name': 'white_goods'}, {'id': 12213, 'synset': 'whitewash.n.02', 'name': 'whitewash'}, {'id': 12214, 'synset': 'whorehouse.n.01', 'name': 'whorehouse'}, {'id': 12215, 'synset': 'wick.n.02', 'name': 'wick'}, {'id': 12216, 'synset': 'wicker.n.02', 'name': 'wicker'}, {'id': 12217, 'synset': 'wicker_basket.n.01', 'name': 'wicker_basket'}, {'id': 12218, 'synset': 'wicket.n.02', 'name': 'wicket'}, {'id': 12219, 'synset': 'wicket.n.01', 'name': 'wicket'}, {'id': 12220, 'synset': 'wickiup.n.01', 'name': 'wickiup'}, {'id': 12221, 'synset': 'wide-angle_lens.n.01', 'name': 'wide-angle_lens'}, {'id': 12222, 'synset': 'widebody_aircraft.n.01', 'name': 'widebody_aircraft'}, {'id': 12223, 'synset': 'wide_wale.n.01', 'name': 'wide_wale'}, {'id': 12224, 'synset': "widow's_walk.n.01", 'name': "widow's_walk"}, {'id': 12225, 'synset': 'wiffle.n.01', 'name': 'Wiffle'}, {'id': 12226, 'synset': 'wigwam.n.01', 'name': 'wigwam'}, {'id': 12227, 'synset': 'wilton.n.01', 'name': 'Wilton'}, {'id': 12228, 'synset': 'wimple.n.01', 'name': 'wimple'}, {'id': 12229, 'synset': 'wincey.n.01', 'name': 'wincey'}, {'id': 12230, 'synset': 'winceyette.n.01', 'name': 'winceyette'}, {'id': 12231, 'synset': 'winch.n.01', 'name': 'winch'}, {'id': 12232, 'synset': 'winchester.n.02', 'name': 'Winchester'}, {'id': 12233, 'synset': 'windbreak.n.01', 'name': 'windbreak'}, {'id': 12234, 'synset': 'winder.n.02', 'name': 'winder'}, {'id': 12235, 'synset': 'wind_instrument.n.01', 'name': 'wind_instrument'}, {'id': 12236, 'synset': 'windjammer.n.01', 'name': 'windjammer'}, {'id': 12237, 'synset': 'windmill.n.02', 'name': 'windmill'}, {'id': 12238, 'synset': 'window.n.01', 'name': 'window'}, {'id': 12239, 'synset': 'window.n.08', 'name': 'window'}, {'id': 12240, 'synset': 'window_blind.n.01', 'name': 'window_blind'}, {'id': 12241, 'synset': 'window_envelope.n.01', 'name': 'window_envelope'}, {'id': 12242, 'synset': 'window_frame.n.01', 'name': 'window_frame'}, {'id': 12243, 'synset': 'window_screen.n.01', 'name': 'window_screen'}, {'id': 12244, 'synset': 'window_seat.n.01', 'name': 'window_seat'}, {'id': 12245, 'synset': 'window_shade.n.01', 'name': 'window_shade'}, {'id': 12246, 'synset': 'windowsill.n.01', 'name': 'windowsill'}, {'id': 12247, 'synset': 'windshield.n.01', 'name': 'windshield'}, {'id': 12248, 'synset': 'windsor_chair.n.01', 'name': 'Windsor_chair'}, {'id': 12249, 'synset': 'windsor_knot.n.01', 'name': 'Windsor_knot'}, {'id': 12250, 'synset': 'windsor_tie.n.01', 'name': 'Windsor_tie'}, {'id': 12251, 'synset': 'wind_tee.n.01', 'name': 'wind_tee'}, {'id': 12252, 'synset': 'wind_tunnel.n.01', 'name': 'wind_tunnel'}, {'id': 12253, 'synset': 'wind_turbine.n.01', 'name': 'wind_turbine'}, {'id': 12254, 'synset': 'wine_bar.n.01', 'name': 'wine_bar'}, {'id': 12255, 'synset': 'wine_cask.n.01', 'name': 'wine_cask'}, {'id': 12256, 'synset': 'winepress.n.01', 'name': 'winepress'}, {'id': 12257, 'synset': 'winery.n.01', 'name': 'winery'}, {'id': 12258, 'synset': 'wineskin.n.01', 'name': 'wineskin'}, {'id': 12259, 'synset': 'wing.n.02', 'name': 'wing'}, {'id': 12260, 'synset': 'wing_chair.n.01', 'name': 'wing_chair'}, {'id': 12261, 'synset': 'wing_nut.n.02', 'name': 'wing_nut'}, {'id': 12262, 'synset': 'wing_tip.n.02', 'name': 'wing_tip'}, {'id': 12263, 'synset': 'wing_tip.n.01', 'name': 'wing_tip'}, {'id': 12264, 'synset': 'wiper.n.02', 'name': 'wiper'}, {'id': 12265, 'synset': 'wiper_motor.n.01', 'name': 'wiper_motor'}, {'id': 12266, 'synset': 'wire.n.01', 'name': 'wire'}, {'id': 12267, 'synset': 'wire.n.02', 'name': 'wire'}, {'id': 12268, 'synset': 'wire_cloth.n.01', 'name': 'wire_cloth'}, {'id': 12269, 'synset': 'wire_cutter.n.01', 'name': 'wire_cutter'}, {'id': 12270, 'synset': 'wire_gauge.n.01', 'name': 'wire_gauge'}, {'id': 12271, 'synset': 'wireless_local_area_network.n.01', 'name': 'wireless_local_area_network'}, {'id': 12272, 'synset': 'wire_matrix_printer.n.01', 'name': 'wire_matrix_printer'}, {'id': 12273, 'synset': 'wire_recorder.n.01', 'name': 'wire_recorder'}, {'id': 12274, 'synset': 'wire_stripper.n.01', 'name': 'wire_stripper'}, {'id': 12275, 'synset': 'wirework.n.01', 'name': 'wirework'}, {'id': 12276, 'synset': 'wiring.n.01', 'name': 'wiring'}, {'id': 12277, 'synset': 'wishing_cap.n.01', 'name': 'wishing_cap'}, {'id': 12278, 'synset': 'witness_box.n.01', 'name': 'witness_box'}, {'id': 12279, 'synset': "woman's_clothing.n.01", 'name': "woman's_clothing"}, {'id': 12280, 'synset': 'wood.n.08', 'name': 'wood'}, {'id': 12281, 'synset': 'woodcarving.n.01', 'name': 'woodcarving'}, {'id': 12282, 'synset': 'wood_chisel.n.01', 'name': 'wood_chisel'}, {'id': 12283, 'synset': 'woodenware.n.01', 'name': 'woodenware'}, {'id': 12284, 'synset': 'woodscrew.n.01', 'name': 'woodscrew'}, {'id': 12285, 'synset': 'woodshed.n.01', 'name': 'woodshed'}, {'id': 12286, 'synset': 'wood_vise.n.01', 'name': 'wood_vise'}, {'id': 12287, 'synset': 'woodwind.n.01', 'name': 'woodwind'}, {'id': 12288, 'synset': 'woof.n.01', 'name': 'woof'}, {'id': 12289, 'synset': 'woofer.n.01', 'name': 'woofer'}, {'id': 12290, 'synset': 'wool.n.01', 'name': 'wool'}, {'id': 12291, 'synset': 'workbasket.n.01', 'name': 'workbasket'}, {'id': 12292, 'synset': 'workbench.n.01', 'name': 'workbench'}, {'id': 12293, 'synset': 'work-clothing.n.01', 'name': 'work-clothing'}, {'id': 12294, 'synset': 'workhouse.n.02', 'name': 'workhouse'}, {'id': 12295, 'synset': 'workhouse.n.01', 'name': 'workhouse'}, {'id': 12296, 'synset': 'workpiece.n.01', 'name': 'workpiece'}, {'id': 12297, 'synset': 'workroom.n.01', 'name': 'workroom'}, {'id': 12298, 'synset': 'works.n.04', 'name': 'works'}, {'id': 12299, 'synset': 'work-shirt.n.01', 'name': 'work-shirt'}, {'id': 12300, 'synset': 'workstation.n.01', 'name': 'workstation'}, {'id': 12301, 'synset': 'worktable.n.01', 'name': 'worktable'}, {'id': 12302, 'synset': 'workwear.n.01', 'name': 'workwear'}, {'id': 12303, 'synset': 'world_wide_web.n.01', 'name': 'World_Wide_Web'}, {'id': 12304, 'synset': 'worm_fence.n.01', 'name': 'worm_fence'}, {'id': 12305, 'synset': 'worm_gear.n.01', 'name': 'worm_gear'}, {'id': 12306, 'synset': 'worm_wheel.n.01', 'name': 'worm_wheel'}, {'id': 12307, 'synset': 'worsted.n.01', 'name': 'worsted'}, {'id': 12308, 'synset': 'worsted.n.02', 'name': 'worsted'}, {'id': 12309, 'synset': 'wrap.n.01', 'name': 'wrap'}, {'id': 12310, 'synset': 'wraparound.n.01', 'name': 'wraparound'}, {'id': 12311, 'synset': 'wrapping.n.01', 'name': 'wrapping'}, {'id': 12312, 'synset': 'wreck.n.04', 'name': 'wreck'}, {'id': 12313, 'synset': 'wrestling_mat.n.01', 'name': 'wrestling_mat'}, {'id': 12314, 'synset': 'wringer.n.01', 'name': 'wringer'}, {'id': 12315, 'synset': 'wrist_pad.n.01', 'name': 'wrist_pad'}, {'id': 12316, 'synset': 'wrist_pin.n.01', 'name': 'wrist_pin'}, {'id': 12317, 'synset': 'wristwatch.n.01', 'name': 'wristwatch'}, {'id': 12318, 'synset': 'writing_arm.n.01', 'name': 'writing_arm'}, {'id': 12319, 'synset': 'writing_desk.n.02', 'name': 'writing_desk'}, {'id': 12320, 'synset': 'writing_desk.n.01', 'name': 'writing_desk'}, {'id': 12321, 'synset': 'writing_implement.n.01', 'name': 'writing_implement'}, {'id': 12322, 'synset': 'xerographic_printer.n.01', 'name': 'xerographic_printer'}, {'id': 12323, 'synset': 'xerox.n.02', 'name': 'Xerox'}, {'id': 12324, 'synset': 'x-ray_film.n.01', 'name': 'X-ray_film'}, {'id': 12325, 'synset': 'x-ray_machine.n.01', 'name': 'X-ray_machine'}, {'id': 12326, 'synset': 'x-ray_tube.n.01', 'name': 'X-ray_tube'}, {'id': 12327, 'synset': 'yacht_chair.n.01', 'name': 'yacht_chair'}, {'id': 12328, 'synset': 'yagi.n.01', 'name': 'yagi'}, {'id': 12329, 'synset': 'yard.n.09', 'name': 'yard'}, {'id': 12330, 'synset': 'yard.n.08', 'name': 'yard'}, {'id': 12331, 'synset': 'yardarm.n.01', 'name': 'yardarm'}, {'id': 12332, 'synset': 'yard_marker.n.01', 'name': 'yard_marker'}, {'id': 12333, 'synset': 'yardstick.n.02', 'name': 'yardstick'}, {'id': 12334, 'synset': 'yarmulke.n.01', 'name': 'yarmulke'}, {'id': 12335, 'synset': 'yashmak.n.01', 'name': 'yashmak'}, {'id': 12336, 'synset': 'yataghan.n.01', 'name': 'yataghan'}, {'id': 12337, 'synset': 'yawl.n.02', 'name': 'yawl'}, {'id': 12338, 'synset': 'yawl.n.01', 'name': 'yawl'}, {'id': 12339, 'synset': 'yoke.n.01', 'name': 'yoke'}, {'id': 12340, 'synset': 'yoke.n.06', 'name': 'yoke'}, {'id': 12341, 'synset': 'yurt.n.01', 'name': 'yurt'}, {'id': 12342, 'synset': 'zamboni.n.01', 'name': 'Zamboni'}, {'id': 12343, 'synset': 'zero.n.04', 'name': 'zero'}, {'id': 12344, 'synset': 'ziggurat.n.01', 'name': 'ziggurat'}, {'id': 12345, 'synset': 'zill.n.01', 'name': 'zill'}, {'id': 12346, 'synset': 'zip_gun.n.01', 'name': 'zip_gun'}, {'id': 12347, 'synset': 'zither.n.01', 'name': 'zither'}, {'id': 12348, 'synset': 'zoot_suit.n.01', 'name': 'zoot_suit'}, {'id': 12349, 'synset': 'shading.n.01', 'name': 'shading'}, {'id': 12350, 'synset': 'grain.n.10', 'name': 'grain'}, {'id': 12351, 'synset': 'wood_grain.n.01', 'name': 'wood_grain'}, {'id': 12352, 'synset': 'graining.n.01', 'name': 'graining'}, {'id': 12353, 'synset': 'marbleization.n.01', 'name': 'marbleization'}, {'id': 12354, 'synset': 'light.n.07', 'name': 'light'}, {'id': 12355, 'synset': 'aura.n.02', 'name': 'aura'}, {'id': 12356, 'synset': 'sunniness.n.01', 'name': 'sunniness'}, {'id': 12357, 'synset': 'glint.n.02', 'name': 'glint'}, {'id': 12358, 'synset': 'opalescence.n.01', 'name': 'opalescence'}, {'id': 12359, 'synset': 'polish.n.01', 'name': 'polish'}, {'id': 12360, 'synset': 'primary_color_for_pigments.n.01', 'name': 'primary_color_for_pigments'}, {'id': 12361, 'synset': 'primary_color_for_light.n.01', 'name': 'primary_color_for_light'}, {'id': 12362, 'synset': 'colorlessness.n.01', 'name': 'colorlessness'}, {'id': 12363, 'synset': 'mottle.n.01', 'name': 'mottle'}, {'id': 12364, 'synset': 'achromia.n.01', 'name': 'achromia'}, {'id': 12365, 'synset': 'shade.n.02', 'name': 'shade'}, {'id': 12366, 'synset': 'chromatic_color.n.01', 'name': 'chromatic_color'}, {'id': 12367, 'synset': 'black.n.01', 'name': 'black'}, {'id': 12368, 'synset': 'coal_black.n.01', 'name': 'coal_black'}, {'id': 12369, 'synset': 'alabaster.n.03', 'name': 'alabaster'}, {'id': 12370, 'synset': 'bone.n.03', 'name': 'bone'}, {'id': 12371, 'synset': 'gray.n.01', 'name': 'gray'}, {'id': 12372, 'synset': 'ash_grey.n.01', 'name': 'ash_grey'}, {'id': 12373, 'synset': 'charcoal.n.03', 'name': 'charcoal'}, {'id': 12374, 'synset': 'sanguine.n.01', 'name': 'sanguine'}, {'id': 12375, 'synset': 'turkey_red.n.01', 'name': 'Turkey_red'}, {'id': 12376, 'synset': 'crimson.n.01', 'name': 'crimson'}, {'id': 12377, 'synset': 'dark_red.n.01', 'name': 'dark_red'}, {'id': 12378, 'synset': 'claret.n.01', 'name': 'claret'}, {'id': 12379, 'synset': 'fuschia.n.01', 'name': 'fuschia'}, {'id': 12380, 'synset': 'maroon.n.02', 'name': 'maroon'}, {'id': 12381, 'synset': 'orange.n.02', 'name': 'orange'}, {'id': 12382, 'synset': 'reddish_orange.n.01', 'name': 'reddish_orange'}, {'id': 12383, 'synset': 'yellow.n.01', 'name': 'yellow'}, {'id': 12384, 'synset': 'gamboge.n.02', 'name': 'gamboge'}, {'id': 12385, 'synset': 'pale_yellow.n.01', 'name': 'pale_yellow'}, {'id': 12386, 'synset': 'green.n.01', 'name': 'green'}, {'id': 12387, 'synset': 'greenishness.n.01', 'name': 'greenishness'}, {'id': 12388, 'synset': 'sea_green.n.01', 'name': 'sea_green'}, {'id': 12389, 'synset': 'sage_green.n.01', 'name': 'sage_green'}, {'id': 12390, 'synset': 'bottle_green.n.01', 'name': 'bottle_green'}, {'id': 12391, 'synset': 'emerald.n.03', 'name': 'emerald'}, {'id': 12392, 'synset': 'olive_green.n.01', 'name': 'olive_green'}, {'id': 12393, 'synset': 'jade_green.n.01', 'name': 'jade_green'}, {'id': 12394, 'synset': 'blue.n.01', 'name': 'blue'}, {'id': 12395, 'synset': 'azure.n.01', 'name': 'azure'}, {'id': 12396, 'synset': 'steel_blue.n.01', 'name': 'steel_blue'}, {'id': 12397, 'synset': 'greenish_blue.n.01', 'name': 'greenish_blue'}, {'id': 12398, 'synset': 'purplish_blue.n.01', 'name': 'purplish_blue'}, {'id': 12399, 'synset': 'purple.n.01', 'name': 'purple'}, {'id': 12400, 'synset': 'tyrian_purple.n.02', 'name': 'Tyrian_purple'}, {'id': 12401, 'synset': 'indigo.n.03', 'name': 'indigo'}, {'id': 12402, 'synset': 'lavender.n.02', 'name': 'lavender'}, {'id': 12403, 'synset': 'reddish_purple.n.01', 'name': 'reddish_purple'}, {'id': 12404, 'synset': 'pink.n.01', 'name': 'pink'}, {'id': 12405, 'synset': 'carnation.n.02', 'name': 'carnation'}, {'id': 12406, 'synset': 'rose.n.03', 'name': 'rose'}, {'id': 12407, 'synset': 'chestnut.n.04', 'name': 'chestnut'}, {'id': 12408, 'synset': 'chocolate.n.03', 'name': 'chocolate'}, {'id': 12409, 'synset': 'light_brown.n.01', 'name': 'light_brown'}, {'id': 12410, 'synset': 'tan.n.02', 'name': 'tan'}, {'id': 12411, 'synset': 'beige.n.01', 'name': 'beige'}, {'id': 12412, 'synset': 'reddish_brown.n.01', 'name': 'reddish_brown'}, {'id': 12413, 'synset': 'brick_red.n.01', 'name': 'brick_red'}, {'id': 12414, 'synset': 'copper.n.04', 'name': 'copper'}, {'id': 12415, 'synset': 'indian_red.n.03', 'name': 'Indian_red'}, {'id': 12416, 'synset': 'puce.n.01', 'name': 'puce'}, {'id': 12417, 'synset': 'olive.n.05', 'name': 'olive'}, {'id': 12418, 'synset': 'ultramarine.n.02', 'name': 'ultramarine'}, {'id': 12419, 'synset': 'complementary_color.n.01', 'name': 'complementary_color'}, {'id': 12420, 'synset': 'pigmentation.n.02', 'name': 'pigmentation'}, {'id': 12421, 'synset': 'complexion.n.01', 'name': 'complexion'}, {'id': 12422, 'synset': 'ruddiness.n.01', 'name': 'ruddiness'}, {'id': 12423, 'synset': 'nonsolid_color.n.01', 'name': 'nonsolid_color'}, {'id': 12424, 'synset': 'aposematic_coloration.n.01', 'name': 'aposematic_coloration'}, {'id': 12425, 'synset': 'cryptic_coloration.n.01', 'name': 'cryptic_coloration'}, {'id': 12426, 'synset': 'ring.n.01', 'name': 'ring'}, {'id': 12427, 'synset': 'center_of_curvature.n.01', 'name': 'center_of_curvature'}, {'id': 12428, 'synset': 'cadaver.n.01', 'name': 'cadaver'}, {'id': 12429, 'synset': 'mandibular_notch.n.01', 'name': 'mandibular_notch'}, {'id': 12430, 'synset': 'rib.n.05', 'name': 'rib'}, {'id': 12431, 'synset': 'skin.n.01', 'name': 'skin'}, {'id': 12432, 'synset': 'skin_graft.n.01', 'name': 'skin_graft'}, {'id': 12433, 'synset': 'epidermal_cell.n.01', 'name': 'epidermal_cell'}, {'id': 12434, 'synset': 'melanocyte.n.01', 'name': 'melanocyte'}, {'id': 12435, 'synset': 'prickle_cell.n.01', 'name': 'prickle_cell'}, {'id': 12436, 'synset': 'columnar_cell.n.01', 'name': 'columnar_cell'}, {'id': 12437, 'synset': 'spongioblast.n.01', 'name': 'spongioblast'}, {'id': 12438, 'synset': 'squamous_cell.n.01', 'name': 'squamous_cell'}, {'id': 12439, 'synset': 'amyloid_plaque.n.01', 'name': 'amyloid_plaque'}, {'id': 12440, 'synset': 'dental_plaque.n.01', 'name': 'dental_plaque'}, {'id': 12441, 'synset': 'macule.n.01', 'name': 'macule'}, {'id': 12442, 'synset': 'freckle.n.01', 'name': 'freckle'}, {'id': 12443, 'synset': 'bouffant.n.01', 'name': 'bouffant'}, {'id': 12444, 'synset': 'sausage_curl.n.01', 'name': 'sausage_curl'}, {'id': 12445, 'synset': 'forelock.n.01', 'name': 'forelock'}, {'id': 12446, 'synset': 'spit_curl.n.01', 'name': 'spit_curl'}, {'id': 12447, 'synset': 'pigtail.n.01', 'name': 'pigtail'}, {'id': 12448, 'synset': 'pageboy.n.02', 'name': 'pageboy'}, {'id': 12449, 'synset': 'pompadour.n.02', 'name': 'pompadour'}, {'id': 12450, 'synset': 'thatch.n.01', 'name': 'thatch'}, {'id': 12451, 'synset': 'soup-strainer.n.01', 'name': 'soup-strainer'}, {'id': 12452, 'synset': 'mustachio.n.01', 'name': 'mustachio'}, {'id': 12453, 'synset': 'walrus_mustache.n.01', 'name': 'walrus_mustache'}, {'id': 12454, 'synset': 'stubble.n.02', 'name': 'stubble'}, {'id': 12455, 'synset': 'vandyke_beard.n.01', 'name': 'vandyke_beard'}, {'id': 12456, 'synset': 'soul_patch.n.01', 'name': 'soul_patch'}, {'id': 12457, 'synset': 'esophageal_smear.n.01', 'name': 'esophageal_smear'}, {'id': 12458, 'synset': 'paraduodenal_smear.n.01', 'name': 'paraduodenal_smear'}, {'id': 12459, 'synset': 'specimen.n.02', 'name': 'specimen'}, {'id': 12460, 'synset': 'punctum.n.01', 'name': 'punctum'}, {'id': 12461, 'synset': 'glenoid_fossa.n.02', 'name': 'glenoid_fossa'}, {'id': 12462, 'synset': 'diastema.n.01', 'name': 'diastema'}, {'id': 12463, 'synset': 'marrow.n.01', 'name': 'marrow'}, {'id': 12464, 'synset': 'mouth.n.01', 'name': 'mouth'}, {'id': 12465, 'synset': 'canthus.n.01', 'name': 'canthus'}, {'id': 12466, 'synset': 'milk.n.02', 'name': 'milk'}, {'id': 12467, 'synset': "mother's_milk.n.01", 'name': "mother's_milk"}, {'id': 12468, 'synset': 'colostrum.n.01', 'name': 'colostrum'}, {'id': 12469, 'synset': 'vein.n.01', 'name': 'vein'}, {'id': 12470, 'synset': 'ganglion_cell.n.01', 'name': 'ganglion_cell'}, {'id': 12471, 'synset': 'x_chromosome.n.01', 'name': 'X_chromosome'}, {'id': 12472, 'synset': 'embryonic_cell.n.01', 'name': 'embryonic_cell'}, {'id': 12473, 'synset': 'myeloblast.n.01', 'name': 'myeloblast'}, {'id': 12474, 'synset': 'sideroblast.n.01', 'name': 'sideroblast'}, {'id': 12475, 'synset': 'osteocyte.n.01', 'name': 'osteocyte'}, {'id': 12476, 'synset': 'megalocyte.n.01', 'name': 'megalocyte'}, {'id': 12477, 'synset': 'leukocyte.n.01', 'name': 'leukocyte'}, {'id': 12478, 'synset': 'histiocyte.n.01', 'name': 'histiocyte'}, {'id': 12479, 'synset': 'fixed_phagocyte.n.01', 'name': 'fixed_phagocyte'}, {'id': 12480, 'synset': 'lymphocyte.n.01', 'name': 'lymphocyte'}, {'id': 12481, 'synset': 'monoblast.n.01', 'name': 'monoblast'}, {'id': 12482, 'synset': 'neutrophil.n.01', 'name': 'neutrophil'}, {'id': 12483, 'synset': 'microphage.n.01', 'name': 'microphage'}, {'id': 12484, 'synset': 'sickle_cell.n.01', 'name': 'sickle_cell'}, {'id': 12485, 'synset': 'siderocyte.n.01', 'name': 'siderocyte'}, {'id': 12486, 'synset': 'spherocyte.n.01', 'name': 'spherocyte'}, {'id': 12487, 'synset': 'ootid.n.01', 'name': 'ootid'}, {'id': 12488, 'synset': 'oocyte.n.01', 'name': 'oocyte'}, {'id': 12489, 'synset': 'spermatid.n.01', 'name': 'spermatid'}, {'id': 12490, 'synset': 'leydig_cell.n.01', 'name': 'Leydig_cell'}, {'id': 12491, 'synset': 'striated_muscle_cell.n.01', 'name': 'striated_muscle_cell'}, {'id': 12492, 'synset': 'smooth_muscle_cell.n.01', 'name': 'smooth_muscle_cell'}, {'id': 12493, 'synset': "ranvier's_nodes.n.01", 'name': "Ranvier's_nodes"}, {'id': 12494, 'synset': 'neuroglia.n.01', 'name': 'neuroglia'}, {'id': 12495, 'synset': 'astrocyte.n.01', 'name': 'astrocyte'}, {'id': 12496, 'synset': 'protoplasmic_astrocyte.n.01', 'name': 'protoplasmic_astrocyte'}, {'id': 12497, 'synset': 'oligodendrocyte.n.01', 'name': 'oligodendrocyte'}, {'id': 12498, 'synset': 'proprioceptor.n.01', 'name': 'proprioceptor'}, {'id': 12499, 'synset': 'dendrite.n.01', 'name': 'dendrite'}, {'id': 12500, 'synset': 'sensory_fiber.n.01', 'name': 'sensory_fiber'}, {'id': 12501, 'synset': 'subarachnoid_space.n.01', 'name': 'subarachnoid_space'}, {'id': 12502, 'synset': 'cerebral_cortex.n.01', 'name': 'cerebral_cortex'}, {'id': 12503, 'synset': 'renal_cortex.n.01', 'name': 'renal_cortex'}, {'id': 12504, 'synset': 'prepuce.n.02', 'name': 'prepuce'}, {'id': 12505, 'synset': 'head.n.01', 'name': 'head'}, {'id': 12506, 'synset': 'scalp.n.01', 'name': 'scalp'}, {'id': 12507, 'synset': 'frontal_eminence.n.01', 'name': 'frontal_eminence'}, {'id': 12508, 'synset': 'suture.n.01', 'name': 'suture'}, {'id': 12509, 'synset': 'foramen_magnum.n.01', 'name': 'foramen_magnum'}, {'id': 12510, 'synset': 'esophagogastric_junction.n.01', 'name': 'esophagogastric_junction'}, {'id': 12511, 'synset': 'heel.n.02', 'name': 'heel'}, {'id': 12512, 'synset': 'cuticle.n.01', 'name': 'cuticle'}, {'id': 12513, 'synset': 'hangnail.n.01', 'name': 'hangnail'}, {'id': 12514, 'synset': 'exoskeleton.n.01', 'name': 'exoskeleton'}, {'id': 12515, 'synset': 'abdominal_wall.n.01', 'name': 'abdominal_wall'}, {'id': 12516, 'synset': 'lemon.n.04', 'name': 'lemon'}, {'id': 12517, 'synset': 'coordinate_axis.n.01', 'name': 'coordinate_axis'}, {'id': 12518, 'synset': 'landscape.n.04', 'name': 'landscape'}, {'id': 12519, 'synset': 'medium.n.01', 'name': 'medium'}, {'id': 12520, 'synset': 'vehicle.n.02', 'name': 'vehicle'}, {'id': 12521, 'synset': 'paper.n.04', 'name': 'paper'}, {'id': 12522, 'synset': 'channel.n.01', 'name': 'channel'}, {'id': 12523, 'synset': 'film.n.02', 'name': 'film'}, {'id': 12524, 'synset': 'silver_screen.n.01', 'name': 'silver_screen'}, {'id': 12525, 'synset': 'free_press.n.01', 'name': 'free_press'}, {'id': 12526, 'synset': 'press.n.02', 'name': 'press'}, {'id': 12527, 'synset': 'print_media.n.01', 'name': 'print_media'}, {'id': 12528, 'synset': 'storage_medium.n.01', 'name': 'storage_medium'}, {'id': 12529, 'synset': 'magnetic_storage_medium.n.01', 'name': 'magnetic_storage_medium'}, {'id': 12530, 'synset': 'journalism.n.01', 'name': 'journalism'}, {'id': 12531, 'synset': 'fleet_street.n.02', 'name': 'Fleet_Street'}, {'id': 12532, 'synset': 'photojournalism.n.01', 'name': 'photojournalism'}, {'id': 12533, 'synset': 'news_photography.n.01', 'name': 'news_photography'}, {'id': 12534, 'synset': 'rotogravure.n.02', 'name': 'rotogravure'}, {'id': 12535, 'synset': 'daily.n.01', 'name': 'daily'}, {'id': 12536, 'synset': 'gazette.n.01', 'name': 'gazette'}, {'id': 12537, 'synset': 'school_newspaper.n.01', 'name': 'school_newspaper'}, {'id': 12538, 'synset': 'tabloid.n.02', 'name': 'tabloid'}, {'id': 12539, 'synset': 'yellow_journalism.n.01', 'name': 'yellow_journalism'}, {'id': 12540, 'synset': 'telecommunication.n.01', 'name': 'telecommunication'}, {'id': 12541, 'synset': 'telephone.n.02', 'name': 'telephone'}, {'id': 12542, 'synset': 'voice_mail.n.01', 'name': 'voice_mail'}, {'id': 12543, 'synset': 'call.n.01', 'name': 'call'}, {'id': 12544, 'synset': 'call-back.n.01', 'name': 'call-back'}, {'id': 12545, 'synset': 'collect_call.n.01', 'name': 'collect_call'}, {'id': 12546, 'synset': 'call_forwarding.n.01', 'name': 'call_forwarding'}, {'id': 12547, 'synset': 'call-in.n.01', 'name': 'call-in'}, {'id': 12548, 'synset': 'call_waiting.n.01', 'name': 'call_waiting'}, {'id': 12549, 'synset': 'crank_call.n.01', 'name': 'crank_call'}, {'id': 12550, 'synset': 'local_call.n.01', 'name': 'local_call'}, {'id': 12551, 'synset': 'long_distance.n.01', 'name': 'long_distance'}, {'id': 12552, 'synset': 'toll_call.n.01', 'name': 'toll_call'}, {'id': 12553, 'synset': 'wake-up_call.n.02', 'name': 'wake-up_call'}, {'id': 12554, 'synset': 'three-way_calling.n.01', 'name': 'three-way_calling'}, {'id': 12555, 'synset': 'telegraphy.n.01', 'name': 'telegraphy'}, {'id': 12556, 'synset': 'cable.n.01', 'name': 'cable'}, {'id': 12557, 'synset': 'wireless.n.02', 'name': 'wireless'}, {'id': 12558, 'synset': 'radiotelegraph.n.01', 'name': 'radiotelegraph'}, {'id': 12559, 'synset': 'radiotelephone.n.01', 'name': 'radiotelephone'}, {'id': 12560, 'synset': 'broadcasting.n.02', 'name': 'broadcasting'}, {'id': 12561, 'synset': 'rediffusion.n.01', 'name': 'Rediffusion'}, {'id': 12562, 'synset': 'multiplex.n.01', 'name': 'multiplex'}, {'id': 12563, 'synset': 'radio.n.01', 'name': 'radio'}, {'id': 12564, 'synset': 'television.n.01', 'name': 'television'}, {'id': 12565, 'synset': 'cable_television.n.01', 'name': 'cable_television'}, {'id': 12566, 'synset': 'high-definition_television.n.01', 'name': 'high-definition_television'}, {'id': 12567, 'synset': 'reception.n.03', 'name': 'reception'}, {'id': 12568, 'synset': 'signal_detection.n.01', 'name': 'signal_detection'}, {'id': 12569, 'synset': 'hakham.n.01', 'name': 'Hakham'}, {'id': 12570, 'synset': 'web_site.n.01', 'name': 'web_site'}, {'id': 12571, 'synset': 'chat_room.n.01', 'name': 'chat_room'}, {'id': 12572, 'synset': 'portal_site.n.01', 'name': 'portal_site'}, {'id': 12573, 'synset': 'jotter.n.01', 'name': 'jotter'}, {'id': 12574, 'synset': 'breviary.n.01', 'name': 'breviary'}, {'id': 12575, 'synset': 'wordbook.n.01', 'name': 'wordbook'}, {'id': 12576, 'synset': 'desk_dictionary.n.01', 'name': 'desk_dictionary'}, {'id': 12577, 'synset': 'reckoner.n.02', 'name': 'reckoner'}, {'id': 12578, 'synset': 'document.n.01', 'name': 'document'}, {'id': 12579, 'synset': 'album.n.01', 'name': 'album'}, {'id': 12580, 'synset': 'concept_album.n.01', 'name': 'concept_album'}, {'id': 12581, 'synset': 'rock_opera.n.01', 'name': 'rock_opera'}, {'id': 12582, 'synset': 'tribute_album.n.01', 'name': 'tribute_album'}, {'id': 12583, 'synset': 'magazine.n.01', 'name': 'magazine'}, {'id': 12584, 'synset': 'colour_supplement.n.01', 'name': 'colour_supplement'}, {'id': 12585, 'synset': 'news_magazine.n.01', 'name': 'news_magazine'}, {'id': 12586, 'synset': 'pulp.n.04', 'name': 'pulp'}, {'id': 12587, 'synset': 'slick.n.02', 'name': 'slick'}, {'id': 12588, 'synset': 'trade_magazine.n.01', 'name': 'trade_magazine'}, {'id': 12589, 'synset': 'movie.n.01', 'name': 'movie'}, {'id': 12590, 'synset': 'outtake.n.01', 'name': 'outtake'}, {'id': 12591, 'synset': "shoot-'em-up.n.01", 'name': "shoot-'em-up"}, {'id': 12592, 'synset': 'spaghetti_western.n.01', 'name': 'spaghetti_Western'}, {'id': 12593, 'synset': 'encyclical.n.01', 'name': 'encyclical'}, {'id': 12594, 'synset': 'crossword_puzzle.n.01', 'name': 'crossword_puzzle'}, {'id': 12595, 'synset': 'sign.n.02', 'name': 'sign'}, {'id': 12596, 'synset': 'swastika.n.01', 'name': 'swastika'}, {'id': 12597, 'synset': 'concert.n.01', 'name': 'concert'}, {'id': 12598, 'synset': 'artwork.n.01', 'name': 'artwork'}, {'id': 12599, 'synset': 'lobe.n.03', 'name': 'lobe'}, {'id': 12600, 'synset': 'book_jacket.n.01', 'name': 'book_jacket'}, {'id': 12601, 'synset': 'cairn.n.01', 'name': 'cairn'}, {'id': 12602, 'synset': 'three-day_event.n.01', 'name': 'three-day_event'}, {'id': 12603, 'synset': 'comfort_food.n.01', 'name': 'comfort_food'}, {'id': 12604, 'synset': 'comestible.n.01', 'name': 'comestible'}, {'id': 12605, 'synset': 'tuck.n.01', 'name': 'tuck'}, {'id': 12606, 'synset': 'course.n.07', 'name': 'course'}, {'id': 12607, 'synset': 'dainty.n.01', 'name': 'dainty'}, {'id': 12608, 'synset': 'dish.n.02', 'name': 'dish'}, {'id': 12609, 'synset': 'fast_food.n.01', 'name': 'fast_food'}, {'id': 12610, 'synset': 'finger_food.n.01', 'name': 'finger_food'}, {'id': 12611, 'synset': 'ingesta.n.01', 'name': 'ingesta'}, {'id': 12612, 'synset': 'kosher.n.01', 'name': 'kosher'}, {'id': 12613, 'synset': 'fare.n.04', 'name': 'fare'}, {'id': 12614, 'synset': 'diet.n.03', 'name': 'diet'}, {'id': 12615, 'synset': 'diet.n.01', 'name': 'diet'}, {'id': 12616, 'synset': 'dietary.n.01', 'name': 'dietary'}, {'id': 12617, 'synset': 'balanced_diet.n.01', 'name': 'balanced_diet'}, {'id': 12618, 'synset': 'bland_diet.n.01', 'name': 'bland_diet'}, {'id': 12619, 'synset': 'clear_liquid_diet.n.01', 'name': 'clear_liquid_diet'}, {'id': 12620, 'synset': 'diabetic_diet.n.01', 'name': 'diabetic_diet'}, {'id': 12621, 'synset': 'dietary_supplement.n.01', 'name': 'dietary_supplement'}, {'id': 12622, 'synset': 'carbohydrate_loading.n.01', 'name': 'carbohydrate_loading'}, {'id': 12623, 'synset': 'fad_diet.n.01', 'name': 'fad_diet'}, {'id': 12624, 'synset': 'gluten-free_diet.n.01', 'name': 'gluten-free_diet'}, {'id': 12625, 'synset': 'high-protein_diet.n.01', 'name': 'high-protein_diet'}, {'id': 12626, 'synset': 'high-vitamin_diet.n.01', 'name': 'high-vitamin_diet'}, {'id': 12627, 'synset': 'light_diet.n.01', 'name': 'light_diet'}, {'id': 12628, 'synset': 'liquid_diet.n.01', 'name': 'liquid_diet'}, {'id': 12629, 'synset': 'low-calorie_diet.n.01', 'name': 'low-calorie_diet'}, {'id': 12630, 'synset': 'low-fat_diet.n.01', 'name': 'low-fat_diet'}, {'id': 12631, 'synset': 'low-sodium_diet.n.01', 'name': 'low-sodium_diet'}, {'id': 12632, 'synset': 'macrobiotic_diet.n.01', 'name': 'macrobiotic_diet'}, {'id': 12633, 'synset': 'reducing_diet.n.01', 'name': 'reducing_diet'}, {'id': 12634, 'synset': 'soft_diet.n.01', 'name': 'soft_diet'}, {'id': 12635, 'synset': 'vegetarianism.n.01', 'name': 'vegetarianism'}, {'id': 12636, 'synset': 'menu.n.02', 'name': 'menu'}, {'id': 12637, 'synset': 'chow.n.02', 'name': 'chow'}, {'id': 12638, 'synset': 'board.n.04', 'name': 'board'}, {'id': 12639, 'synset': 'mess.n.04', 'name': 'mess'}, {'id': 12640, 'synset': 'ration.n.01', 'name': 'ration'}, {'id': 12641, 'synset': 'field_ration.n.01', 'name': 'field_ration'}, {'id': 12642, 'synset': 'k_ration.n.01', 'name': 'K_ration'}, {'id': 12643, 'synset': 'c-ration.n.01', 'name': 'C-ration'}, {'id': 12644, 'synset': 'foodstuff.n.02', 'name': 'foodstuff'}, {'id': 12645, 'synset': 'starches.n.01', 'name': 'starches'}, {'id': 12646, 'synset': 'breadstuff.n.02', 'name': 'breadstuff'}, {'id': 12647, 'synset': 'coloring.n.01', 'name': 'coloring'}, {'id': 12648, 'synset': 'concentrate.n.02', 'name': 'concentrate'}, {'id': 12649, 'synset': 'tomato_concentrate.n.01', 'name': 'tomato_concentrate'}, {'id': 12650, 'synset': 'meal.n.03', 'name': 'meal'}, {'id': 12651, 'synset': 'kibble.n.01', 'name': 'kibble'}, {'id': 12652, 'synset': 'farina.n.01', 'name': 'farina'}, {'id': 12653, 'synset': 'matzo_meal.n.01', 'name': 'matzo_meal'}, {'id': 12654, 'synset': 'oatmeal.n.02', 'name': 'oatmeal'}, {'id': 12655, 'synset': 'pea_flour.n.01', 'name': 'pea_flour'}, {'id': 12656, 'synset': 'roughage.n.01', 'name': 'roughage'}, {'id': 12657, 'synset': 'bran.n.02', 'name': 'bran'}, {'id': 12658, 'synset': 'flour.n.01', 'name': 'flour'}, {'id': 12659, 'synset': 'plain_flour.n.01', 'name': 'plain_flour'}, {'id': 12660, 'synset': 'wheat_flour.n.01', 'name': 'wheat_flour'}, {'id': 12661, 'synset': 'whole_wheat_flour.n.01', 'name': 'whole_wheat_flour'}, {'id': 12662, 'synset': 'soybean_meal.n.01', 'name': 'soybean_meal'}, {'id': 12663, 'synset': 'semolina.n.01', 'name': 'semolina'}, {'id': 12664, 'synset': 'corn_gluten_feed.n.01', 'name': 'corn_gluten_feed'}, {'id': 12665, 'synset': 'nutriment.n.01', 'name': 'nutriment'}, {'id': 12666, 'synset': 'commissariat.n.01', 'name': 'commissariat'}, {'id': 12667, 'synset': 'larder.n.01', 'name': 'larder'}, {'id': 12668, 'synset': 'frozen_food.n.01', 'name': 'frozen_food'}, {'id': 12669, 'synset': 'canned_food.n.01', 'name': 'canned_food'}, {'id': 12670, 'synset': 'canned_meat.n.01', 'name': 'canned_meat'}, {'id': 12671, 'synset': 'spam.n.01', 'name': 'Spam'}, {'id': 12672, 'synset': 'dehydrated_food.n.01', 'name': 'dehydrated_food'}, {'id': 12673, 'synset': 'square_meal.n.01', 'name': 'square_meal'}, {'id': 12674, 'synset': 'meal.n.01', 'name': 'meal'}, {'id': 12675, 'synset': 'potluck.n.01', 'name': 'potluck'}, {'id': 12676, 'synset': 'refection.n.01', 'name': 'refection'}, {'id': 12677, 'synset': 'refreshment.n.01', 'name': 'refreshment'}, {'id': 12678, 'synset': 'breakfast.n.01', 'name': 'breakfast'}, {'id': 12679, 'synset': 'continental_breakfast.n.01', 'name': 'continental_breakfast'}, {'id': 12680, 'synset': 'brunch.n.01', 'name': 'brunch'}, {'id': 12681, 'synset': 'lunch.n.01', 'name': 'lunch'}, {'id': 12682, 'synset': 'business_lunch.n.01', 'name': 'business_lunch'}, {'id': 12683, 'synset': 'high_tea.n.01', 'name': 'high_tea'}, {'id': 12684, 'synset': 'tea.n.02', 'name': 'tea'}, {'id': 12685, 'synset': 'dinner.n.01', 'name': 'dinner'}, {'id': 12686, 'synset': 'supper.n.01', 'name': 'supper'}, {'id': 12687, 'synset': 'buffet.n.02', 'name': 'buffet'}, {'id': 12688, 'synset': 'picnic.n.03', 'name': 'picnic'}, {'id': 12689, 'synset': 'cookout.n.01', 'name': 'cookout'}, {'id': 12690, 'synset': 'barbecue.n.02', 'name': 'barbecue'}, {'id': 12691, 'synset': 'clambake.n.01', 'name': 'clambake'}, {'id': 12692, 'synset': 'fish_fry.n.01', 'name': 'fish_fry'}, {'id': 12693, 'synset': 'bite.n.04', 'name': 'bite'}, {'id': 12694, 'synset': 'nosh.n.01', 'name': 'nosh'}, {'id': 12695, 'synset': 'nosh-up.n.01', 'name': 'nosh-up'}, {'id': 12696, 'synset': "ploughman's_lunch.n.01", 'name': "ploughman's_lunch"}, {'id': 12697, 'synset': 'coffee_break.n.01', 'name': 'coffee_break'}, {'id': 12698, 'synset': 'banquet.n.02', 'name': 'banquet'}, {'id': 12699, 'synset': 'entree.n.01', 'name': 'entree'}, {'id': 12700, 'synset': 'piece_de_resistance.n.02', 'name': 'piece_de_resistance'}, {'id': 12701, 'synset': 'plate.n.08', 'name': 'plate'}, {'id': 12702, 'synset': 'adobo.n.01', 'name': 'adobo'}, {'id': 12703, 'synset': 'side_dish.n.01', 'name': 'side_dish'}, {'id': 12704, 'synset': 'special.n.02', 'name': 'special'}, {'id': 12705, 'synset': 'chicken_casserole.n.01', 'name': 'chicken_casserole'}, {'id': 12706, 'synset': 'chicken_cacciatore.n.01', 'name': 'chicken_cacciatore'}, {'id': 12707, 'synset': 'antipasto.n.01', 'name': 'antipasto'}, {'id': 12708, 'synset': 'appetizer.n.01', 'name': 'appetizer'}, {'id': 12709, 'synset': 'canape.n.01', 'name': 'canape'}, {'id': 12710, 'synset': 'cocktail.n.02', 'name': 'cocktail'}, {'id': 12711, 'synset': 'fruit_cocktail.n.01', 'name': 'fruit_cocktail'}, {'id': 12712, 'synset': 'crab_cocktail.n.01', 'name': 'crab_cocktail'}, {'id': 12713, 'synset': 'shrimp_cocktail.n.01', 'name': 'shrimp_cocktail'}, {'id': 12714, 'synset': "hors_d'oeuvre.n.01", 'name': "hors_d'oeuvre"}, {'id': 12715, 'synset': 'relish.n.02', 'name': 'relish'}, {'id': 12716, 'synset': 'dip.n.04', 'name': 'dip'}, {'id': 12717, 'synset': 'bean_dip.n.01', 'name': 'bean_dip'}, {'id': 12718, 'synset': 'cheese_dip.n.01', 'name': 'cheese_dip'}, {'id': 12719, 'synset': 'clam_dip.n.01', 'name': 'clam_dip'}, {'id': 12720, 'synset': 'guacamole.n.01', 'name': 'guacamole'}, {'id': 12721, 'synset': 'soup_du_jour.n.01', 'name': 'soup_du_jour'}, {'id': 12722, 'synset': 'alphabet_soup.n.02', 'name': 'alphabet_soup'}, {'id': 12723, 'synset': 'consomme.n.01', 'name': 'consomme'}, {'id': 12724, 'synset': 'madrilene.n.01', 'name': 'madrilene'}, {'id': 12725, 'synset': 'bisque.n.01', 'name': 'bisque'}, {'id': 12726, 'synset': 'borsch.n.01', 'name': 'borsch'}, {'id': 12727, 'synset': 'broth.n.02', 'name': 'broth'}, {'id': 12728, 'synset': 'barley_water.n.01', 'name': 'barley_water'}, {'id': 12729, 'synset': 'bouillon.n.01', 'name': 'bouillon'}, {'id': 12730, 'synset': 'beef_broth.n.01', 'name': 'beef_broth'}, {'id': 12731, 'synset': 'chicken_broth.n.01', 'name': 'chicken_broth'}, {'id': 12732, 'synset': 'broth.n.01', 'name': 'broth'}, {'id': 12733, 'synset': 'stock_cube.n.01', 'name': 'stock_cube'}, {'id': 12734, 'synset': 'chicken_soup.n.01', 'name': 'chicken_soup'}, {'id': 12735, 'synset': 'cock-a-leekie.n.01', 'name': 'cock-a-leekie'}, {'id': 12736, 'synset': 'gazpacho.n.01', 'name': 'gazpacho'}, {'id': 12737, 'synset': 'gumbo.n.04', 'name': 'gumbo'}, {'id': 12738, 'synset': 'julienne.n.02', 'name': 'julienne'}, {'id': 12739, 'synset': 'marmite.n.01', 'name': 'marmite'}, {'id': 12740, 'synset': 'mock_turtle_soup.n.01', 'name': 'mock_turtle_soup'}, {'id': 12741, 'synset': 'mulligatawny.n.01', 'name': 'mulligatawny'}, {'id': 12742, 'synset': 'oxtail_soup.n.01', 'name': 'oxtail_soup'}, {'id': 12743, 'synset': 'pea_soup.n.01', 'name': 'pea_soup'}, {'id': 12744, 'synset': 'pepper_pot.n.01', 'name': 'pepper_pot'}, {'id': 12745, 'synset': 'petite_marmite.n.01', 'name': 'petite_marmite'}, {'id': 12746, 'synset': 'potage.n.01', 'name': 'potage'}, {'id': 12747, 'synset': 'pottage.n.01', 'name': 'pottage'}, {'id': 12748, 'synset': 'turtle_soup.n.01', 'name': 'turtle_soup'}, {'id': 12749, 'synset': 'eggdrop_soup.n.01', 'name': 'eggdrop_soup'}, {'id': 12750, 'synset': 'chowder.n.01', 'name': 'chowder'}, {'id': 12751, 'synset': 'corn_chowder.n.01', 'name': 'corn_chowder'}, {'id': 12752, 'synset': 'clam_chowder.n.01', 'name': 'clam_chowder'}, {'id': 12753, 'synset': 'manhattan_clam_chowder.n.01', 'name': 'Manhattan_clam_chowder'}, {'id': 12754, 'synset': 'new_england_clam_chowder.n.01', 'name': 'New_England_clam_chowder'}, {'id': 12755, 'synset': 'fish_chowder.n.01', 'name': 'fish_chowder'}, {'id': 12756, 'synset': 'won_ton.n.02', 'name': 'won_ton'}, {'id': 12757, 'synset': 'split-pea_soup.n.01', 'name': 'split-pea_soup'}, {'id': 12758, 'synset': 'green_pea_soup.n.01', 'name': 'green_pea_soup'}, {'id': 12759, 'synset': 'lentil_soup.n.01', 'name': 'lentil_soup'}, {'id': 12760, 'synset': 'scotch_broth.n.01', 'name': 'Scotch_broth'}, {'id': 12761, 'synset': 'vichyssoise.n.01', 'name': 'vichyssoise'}, {'id': 12762, 'synset': 'bigos.n.01', 'name': 'bigos'}, {'id': 12763, 'synset': 'brunswick_stew.n.01', 'name': 'Brunswick_stew'}, {'id': 12764, 'synset': 'burgoo.n.03', 'name': 'burgoo'}, {'id': 12765, 'synset': 'burgoo.n.02', 'name': 'burgoo'}, {'id': 12766, 'synset': 'olla_podrida.n.01', 'name': 'olla_podrida'}, {'id': 12767, 'synset': 'mulligan_stew.n.01', 'name': 'mulligan_stew'}, {'id': 12768, 'synset': 'purloo.n.01', 'name': 'purloo'}, {'id': 12769, 'synset': 'goulash.n.01', 'name': 'goulash'}, {'id': 12770, 'synset': 'hotchpotch.n.02', 'name': 'hotchpotch'}, {'id': 12771, 'synset': 'hot_pot.n.01', 'name': 'hot_pot'}, {'id': 12772, 'synset': 'beef_goulash.n.01', 'name': 'beef_goulash'}, {'id': 12773, 'synset': 'pork-and-veal_goulash.n.01', 'name': 'pork-and-veal_goulash'}, {'id': 12774, 'synset': 'porkholt.n.01', 'name': 'porkholt'}, {'id': 12775, 'synset': 'irish_stew.n.01', 'name': 'Irish_stew'}, {'id': 12776, 'synset': 'oyster_stew.n.01', 'name': 'oyster_stew'}, {'id': 12777, 'synset': 'lobster_stew.n.01', 'name': 'lobster_stew'}, {'id': 12778, 'synset': 'lobscouse.n.01', 'name': 'lobscouse'}, {'id': 12779, 'synset': 'fish_stew.n.01', 'name': 'fish_stew'}, {'id': 12780, 'synset': 'bouillabaisse.n.01', 'name': 'bouillabaisse'}, {'id': 12781, 'synset': 'matelote.n.01', 'name': 'matelote'}, {'id': 12782, 'synset': 'paella.n.01', 'name': 'paella'}, {'id': 12783, 'synset': 'fricassee.n.01', 'name': 'fricassee'}, {'id': 12784, 'synset': 'chicken_stew.n.01', 'name': 'chicken_stew'}, {'id': 12785, 'synset': 'turkey_stew.n.01', 'name': 'turkey_stew'}, {'id': 12786, 'synset': 'beef_stew.n.01', 'name': 'beef_stew'}, {'id': 12787, 'synset': 'ragout.n.01', 'name': 'ragout'}, {'id': 12788, 'synset': 'ratatouille.n.01', 'name': 'ratatouille'}, {'id': 12789, 'synset': 'salmi.n.01', 'name': 'salmi'}, {'id': 12790, 'synset': 'pot-au-feu.n.01', 'name': 'pot-au-feu'}, {'id': 12791, 'synset': 'slumgullion.n.01', 'name': 'slumgullion'}, {'id': 12792, 'synset': 'smorgasbord.n.02', 'name': 'smorgasbord'}, {'id': 12793, 'synset': 'viand.n.01', 'name': 'viand'}, {'id': 12794, 'synset': 'ready-mix.n.01', 'name': 'ready-mix'}, {'id': 12795, 'synset': 'brownie_mix.n.01', 'name': 'brownie_mix'}, {'id': 12796, 'synset': 'cake_mix.n.01', 'name': 'cake_mix'}, {'id': 12797, 'synset': 'lemonade_mix.n.01', 'name': 'lemonade_mix'}, {'id': 12798, 'synset': 'self-rising_flour.n.01', 'name': 'self-rising_flour'}, {'id': 12799, 'synset': 'choice_morsel.n.01', 'name': 'choice_morsel'}, {'id': 12800, 'synset': 'savory.n.04', 'name': 'savory'}, {'id': 12801, 'synset': "calf's-foot_jelly.n.01", 'name': "calf's-foot_jelly"}, {'id': 12802, 'synset': 'caramel.n.02', 'name': 'caramel'}, {'id': 12803, 'synset': 'lump_sugar.n.01', 'name': 'lump_sugar'}, {'id': 12804, 'synset': 'cane_sugar.n.02', 'name': 'cane_sugar'}, {'id': 12805, 'synset': 'castor_sugar.n.01', 'name': 'castor_sugar'}, {'id': 12806, 'synset': 'powdered_sugar.n.01', 'name': 'powdered_sugar'}, {'id': 12807, 'synset': 'granulated_sugar.n.01', 'name': 'granulated_sugar'}, {'id': 12808, 'synset': 'icing_sugar.n.01', 'name': 'icing_sugar'}, {'id': 12809, 'synset': 'corn_sugar.n.02', 'name': 'corn_sugar'}, {'id': 12810, 'synset': 'brown_sugar.n.01', 'name': 'brown_sugar'}, {'id': 12811, 'synset': 'demerara.n.05', 'name': 'demerara'}, {'id': 12812, 'synset': 'sweet.n.03', 'name': 'sweet'}, {'id': 12813, 'synset': 'confectionery.n.01', 'name': 'confectionery'}, {'id': 12814, 'synset': 'confiture.n.01', 'name': 'confiture'}, {'id': 12815, 'synset': 'sweetmeat.n.01', 'name': 'sweetmeat'}, {'id': 12816, 'synset': 'candy.n.01', 'name': 'candy'}, {'id': 12817, 'synset': 'carob_bar.n.01', 'name': 'carob_bar'}, {'id': 12818, 'synset': 'hardbake.n.01', 'name': 'hardbake'}, {'id': 12819, 'synset': 'hard_candy.n.01', 'name': 'hard_candy'}, {'id': 12820, 'synset': 'barley-sugar.n.01', 'name': 'barley-sugar'}, {'id': 12821, 'synset': 'brandyball.n.01', 'name': 'brandyball'}, {'id': 12822, 'synset': 'jawbreaker.n.01', 'name': 'jawbreaker'}, {'id': 12823, 'synset': 'lemon_drop.n.01', 'name': 'lemon_drop'}, {'id': 12824, 'synset': 'sourball.n.01', 'name': 'sourball'}, {'id': 12825, 'synset': 'patty.n.03', 'name': 'patty'}, {'id': 12826, 'synset': 'peppermint_patty.n.01', 'name': 'peppermint_patty'}, {'id': 12827, 'synset': 'bonbon.n.01', 'name': 'bonbon'}, {'id': 12828, 'synset': 'brittle.n.01', 'name': 'brittle'}, {'id': 12829, 'synset': 'peanut_brittle.n.01', 'name': 'peanut_brittle'}, {'id': 12830, 'synset': 'chewing_gum.n.01', 'name': 'chewing_gum'}, {'id': 12831, 'synset': 'gum_ball.n.01', 'name': 'gum_ball'}, {'id': 12832, 'synset': 'butterscotch.n.01', 'name': 'butterscotch'}, {'id': 12833, 'synset': 'candied_fruit.n.01', 'name': 'candied_fruit'}, {'id': 12834, 'synset': 'candied_apple.n.01', 'name': 'candied_apple'}, {'id': 12835, 'synset': 'crystallized_ginger.n.01', 'name': 'crystallized_ginger'}, {'id': 12836, 'synset': 'grapefruit_peel.n.01', 'name': 'grapefruit_peel'}, {'id': 12837, 'synset': 'lemon_peel.n.02', 'name': 'lemon_peel'}, {'id': 12838, 'synset': 'orange_peel.n.02', 'name': 'orange_peel'}, {'id': 12839, 'synset': 'candied_citrus_peel.n.01', 'name': 'candied_citrus_peel'}, {'id': 12840, 'synset': 'candy_corn.n.01', 'name': 'candy_corn'}, {'id': 12841, 'synset': 'caramel.n.01', 'name': 'caramel'}, {'id': 12842, 'synset': 'center.n.14', 'name': 'center'}, {'id': 12843, 'synset': 'comfit.n.01', 'name': 'comfit'}, {'id': 12844, 'synset': 'cotton_candy.n.01', 'name': 'cotton_candy'}, {'id': 12845, 'synset': 'dragee.n.02', 'name': 'dragee'}, {'id': 12846, 'synset': 'dragee.n.01', 'name': 'dragee'}, {'id': 12847, 'synset': 'fondant.n.01', 'name': 'fondant'}, {'id': 12848, 'synset': 'chocolate_fudge.n.01', 'name': 'chocolate_fudge'}, {'id': 12849, 'synset': 'divinity.n.03', 'name': 'divinity'}, {'id': 12850, 'synset': 'penuche.n.01', 'name': 'penuche'}, {'id': 12851, 'synset': 'gumdrop.n.01', 'name': 'gumdrop'}, {'id': 12852, 'synset': 'jujube.n.03', 'name': 'jujube'}, {'id': 12853, 'synset': 'honey_crisp.n.01', 'name': 'honey_crisp'}, {'id': 12854, 'synset': 'horehound.n.02', 'name': 'horehound'}, {'id': 12855, 'synset': 'peppermint.n.03', 'name': 'peppermint'}, {'id': 12856, 'synset': 'kiss.n.03', 'name': 'kiss'}, {'id': 12857, 'synset': 'molasses_kiss.n.01', 'name': 'molasses_kiss'}, {'id': 12858, 'synset': 'meringue_kiss.n.01', 'name': 'meringue_kiss'}, {'id': 12859, 'synset': 'chocolate_kiss.n.01', 'name': 'chocolate_kiss'}, {'id': 12860, 'synset': 'licorice.n.02', 'name': 'licorice'}, {'id': 12861, 'synset': 'life_saver.n.01', 'name': 'Life_Saver'}, {'id': 12862, 'synset': 'lozenge.n.01', 'name': 'lozenge'}, {'id': 12863, 'synset': 'cachou.n.01', 'name': 'cachou'}, {'id': 12864, 'synset': 'cough_drop.n.01', 'name': 'cough_drop'}, {'id': 12865, 'synset': 'marshmallow.n.01', 'name': 'marshmallow'}, {'id': 12866, 'synset': 'marzipan.n.01', 'name': 'marzipan'}, {'id': 12867, 'synset': 'nougat.n.01', 'name': 'nougat'}, {'id': 12868, 'synset': 'nougat_bar.n.01', 'name': 'nougat_bar'}, {'id': 12869, 'synset': 'nut_bar.n.01', 'name': 'nut_bar'}, {'id': 12870, 'synset': 'peanut_bar.n.01', 'name': 'peanut_bar'}, {'id': 12871, 'synset': 'popcorn_ball.n.01', 'name': 'popcorn_ball'}, {'id': 12872, 'synset': 'praline.n.01', 'name': 'praline'}, {'id': 12873, 'synset': 'rock_candy.n.02', 'name': 'rock_candy'}, {'id': 12874, 'synset': 'rock_candy.n.01', 'name': 'rock_candy'}, {'id': 12875, 'synset': 'sugar_candy.n.01', 'name': 'sugar_candy'}, {'id': 12876, 'synset': 'sugarplum.n.01', 'name': 'sugarplum'}, {'id': 12877, 'synset': 'taffy.n.01', 'name': 'taffy'}, {'id': 12878, 'synset': 'molasses_taffy.n.01', 'name': 'molasses_taffy'}, {'id': 12879, 'synset': 'turkish_delight.n.01', 'name': 'Turkish_Delight'}, {'id': 12880, 'synset': 'dessert.n.01', 'name': 'dessert'}, {'id': 12881, 'synset': 'ambrosia.n.04', 'name': 'ambrosia'}, {'id': 12882, 'synset': 'ambrosia.n.03', 'name': 'ambrosia'}, {'id': 12883, 'synset': 'baked_alaska.n.01', 'name': 'baked_Alaska'}, {'id': 12884, 'synset': 'blancmange.n.01', 'name': 'blancmange'}, {'id': 12885, 'synset': 'charlotte.n.02', 'name': 'charlotte'}, {'id': 12886, 'synset': 'compote.n.01', 'name': 'compote'}, {'id': 12887, 'synset': 'dumpling.n.02', 'name': 'dumpling'}, {'id': 12888, 'synset': 'flan.n.01', 'name': 'flan'}, {'id': 12889, 'synset': 'frozen_dessert.n.01', 'name': 'frozen_dessert'}, {'id': 12890, 'synset': 'junket.n.01', 'name': 'junket'}, {'id': 12891, 'synset': 'mousse.n.02', 'name': 'mousse'}, {'id': 12892, 'synset': 'mousse.n.01', 'name': 'mousse'}, {'id': 12893, 'synset': 'pavlova.n.02', 'name': 'pavlova'}, {'id': 12894, 'synset': 'peach_melba.n.01', 'name': 'peach_melba'}, {'id': 12895, 'synset': 'whip.n.03', 'name': 'whip'}, {'id': 12896, 'synset': 'prune_whip.n.01', 'name': 'prune_whip'}, {'id': 12897, 'synset': 'pudding.n.03', 'name': 'pudding'}, {'id': 12898, 'synset': 'pudding.n.02', 'name': 'pudding'}, {'id': 12899, 'synset': 'syllabub.n.02', 'name': 'syllabub'}, {'id': 12900, 'synset': 'tiramisu.n.01', 'name': 'tiramisu'}, {'id': 12901, 'synset': 'trifle.n.01', 'name': 'trifle'}, {'id': 12902, 'synset': 'tipsy_cake.n.01', 'name': 'tipsy_cake'}, {'id': 12903, 'synset': 'jello.n.01', 'name': 'jello'}, {'id': 12904, 'synset': 'apple_dumpling.n.01', 'name': 'apple_dumpling'}, {'id': 12905, 'synset': 'ice.n.05', 'name': 'ice'}, {'id': 12906, 'synset': 'water_ice.n.02', 'name': 'water_ice'}, {'id': 12907, 'synset': 'ice-cream_cone.n.01', 'name': 'ice-cream_cone'}, {'id': 12908, 'synset': 'chocolate_ice_cream.n.01', 'name': 'chocolate_ice_cream'}, {'id': 12909, 'synset': 'neapolitan_ice_cream.n.01', 'name': 'Neapolitan_ice_cream'}, {'id': 12910, 'synset': 'peach_ice_cream.n.01', 'name': 'peach_ice_cream'}, {'id': 12911, 'synset': 'strawberry_ice_cream.n.01', 'name': 'strawberry_ice_cream'}, {'id': 12912, 'synset': 'tutti-frutti.n.01', 'name': 'tutti-frutti'}, {'id': 12913, 'synset': 'vanilla_ice_cream.n.01', 'name': 'vanilla_ice_cream'}, {'id': 12914, 'synset': 'ice_milk.n.01', 'name': 'ice_milk'}, {'id': 12915, 'synset': 'frozen_yogurt.n.01', 'name': 'frozen_yogurt'}, {'id': 12916, 'synset': 'snowball.n.03', 'name': 'snowball'}, {'id': 12917, 'synset': 'snowball.n.02', 'name': 'snowball'}, {'id': 12918, 'synset': 'parfait.n.01', 'name': 'parfait'}, {'id': 12919, 'synset': 'ice-cream_sundae.n.01', 'name': 'ice-cream_sundae'}, {'id': 12920, 'synset': 'split.n.07', 'name': 'split'}, {'id': 12921, 'synset': 'banana_split.n.01', 'name': 'banana_split'}, {'id': 12922, 'synset': 'frozen_pudding.n.01', 'name': 'frozen_pudding'}, {'id': 12923, 'synset': 'frozen_custard.n.01', 'name': 'frozen_custard'}, {'id': 12924, 'synset': 'flummery.n.01', 'name': 'flummery'}, {'id': 12925, 'synset': 'fish_mousse.n.01', 'name': 'fish_mousse'}, {'id': 12926, 'synset': 'chicken_mousse.n.01', 'name': 'chicken_mousse'}, {'id': 12927, 'synset': 'plum_pudding.n.01', 'name': 'plum_pudding'}, {'id': 12928, 'synset': 'carrot_pudding.n.01', 'name': 'carrot_pudding'}, {'id': 12929, 'synset': 'corn_pudding.n.01', 'name': 'corn_pudding'}, {'id': 12930, 'synset': 'steamed_pudding.n.01', 'name': 'steamed_pudding'}, {'id': 12931, 'synset': 'duff.n.01', 'name': 'duff'}, {'id': 12932, 'synset': 'vanilla_pudding.n.01', 'name': 'vanilla_pudding'}, {'id': 12933, 'synset': 'chocolate_pudding.n.01', 'name': 'chocolate_pudding'}, {'id': 12934, 'synset': 'brown_betty.n.01', 'name': 'brown_Betty'}, {'id': 12935, 'synset': 'nesselrode.n.01', 'name': 'Nesselrode'}, {'id': 12936, 'synset': 'pease_pudding.n.01', 'name': 'pease_pudding'}, {'id': 12937, 'synset': 'custard.n.01', 'name': 'custard'}, {'id': 12938, 'synset': 'creme_caramel.n.01', 'name': 'creme_caramel'}, {'id': 12939, 'synset': 'creme_anglais.n.01', 'name': 'creme_anglais'}, {'id': 12940, 'synset': 'creme_brulee.n.01', 'name': 'creme_brulee'}, {'id': 12941, 'synset': 'fruit_custard.n.01', 'name': 'fruit_custard'}, {'id': 12942, 'synset': 'tapioca.n.01', 'name': 'tapioca'}, {'id': 12943, 'synset': 'tapioca_pudding.n.01', 'name': 'tapioca_pudding'}, {'id': 12944, 'synset': 'roly-poly.n.02', 'name': 'roly-poly'}, {'id': 12945, 'synset': 'suet_pudding.n.01', 'name': 'suet_pudding'}, {'id': 12946, 'synset': 'bavarian_cream.n.01', 'name': 'Bavarian_cream'}, {'id': 12947, 'synset': 'maraschino.n.02', 'name': 'maraschino'}, {'id': 12948, 'synset': 'nonpareil.n.02', 'name': 'nonpareil'}, {'id': 12949, 'synset': 'zabaglione.n.01', 'name': 'zabaglione'}, {'id': 12950, 'synset': 'garnish.n.01', 'name': 'garnish'}, {'id': 12951, 'synset': 'pastry.n.01', 'name': 'pastry'}, {'id': 12952, 'synset': 'turnover.n.02', 'name': 'turnover'}, {'id': 12953, 'synset': 'apple_turnover.n.01', 'name': 'apple_turnover'}, {'id': 12954, 'synset': 'knish.n.01', 'name': 'knish'}, {'id': 12955, 'synset': 'pirogi.n.01', 'name': 'pirogi'}, {'id': 12956, 'synset': 'samosa.n.01', 'name': 'samosa'}, {'id': 12957, 'synset': 'timbale.n.01', 'name': 'timbale'}, {'id': 12958, 'synset': 'puff_paste.n.01', 'name': 'puff_paste'}, {'id': 12959, 'synset': 'phyllo.n.01', 'name': 'phyllo'}, {'id': 12960, 'synset': 'puff_batter.n.01', 'name': 'puff_batter'}, {'id': 12961, 'synset': 'ice-cream_cake.n.01', 'name': 'ice-cream_cake'}, {'id': 12962, 'synset': 'fish_cake.n.01', 'name': 'fish_cake'}, {'id': 12963, 'synset': 'fish_stick.n.01', 'name': 'fish_stick'}, {'id': 12964, 'synset': 'conserve.n.01', 'name': 'conserve'}, {'id': 12965, 'synset': 'apple_butter.n.01', 'name': 'apple_butter'}, {'id': 12966, 'synset': 'chowchow.n.02', 'name': 'chowchow'}, {'id': 12967, 'synset': 'lemon_curd.n.01', 'name': 'lemon_curd'}, {'id': 12968, 'synset': 'strawberry_jam.n.01', 'name': 'strawberry_jam'}, {'id': 12969, 'synset': 'jelly.n.02', 'name': 'jelly'}, {'id': 12970, 'synset': 'apple_jelly.n.01', 'name': 'apple_jelly'}, {'id': 12971, 'synset': 'crabapple_jelly.n.01', 'name': 'crabapple_jelly'}, {'id': 12972, 'synset': 'grape_jelly.n.01', 'name': 'grape_jelly'}, {'id': 12973, 'synset': 'marmalade.n.01', 'name': 'marmalade'}, {'id': 12974, 'synset': 'orange_marmalade.n.01', 'name': 'orange_marmalade'}, {'id': 12975, 'synset': 'gelatin_dessert.n.01', 'name': 'gelatin_dessert'}, {'id': 12976, 'synset': 'buffalo_wing.n.01', 'name': 'buffalo_wing'}, {'id': 12977, 'synset': 'barbecued_wing.n.01', 'name': 'barbecued_wing'}, {'id': 12978, 'synset': 'mess.n.03', 'name': 'mess'}, {'id': 12979, 'synset': 'mince.n.01', 'name': 'mince'}, {'id': 12980, 'synset': 'puree.n.01', 'name': 'puree'}, {'id': 12981, 'synset': 'barbecue.n.01', 'name': 'barbecue'}, {'id': 12982, 'synset': 'biryani.n.01', 'name': 'biryani'}, {'id': 12983, 'synset': 'escalope_de_veau_orloff.n.01', 'name': 'escalope_de_veau_Orloff'}, {'id': 12984, 'synset': 'saute.n.01', 'name': 'saute'}, {'id': 12985, 'synset': 'veal_parmesan.n.01', 'name': 'veal_parmesan'}, {'id': 12986, 'synset': 'veal_cordon_bleu.n.01', 'name': 'veal_cordon_bleu'}, {'id': 12987, 'synset': 'margarine.n.01', 'name': 'margarine'}, {'id': 12988, 'synset': 'mincemeat.n.01', 'name': 'mincemeat'}, {'id': 12989, 'synset': 'stuffing.n.01', 'name': 'stuffing'}, {'id': 12990, 'synset': 'turkey_stuffing.n.01', 'name': 'turkey_stuffing'}, {'id': 12991, 'synset': 'oyster_stuffing.n.01', 'name': 'oyster_stuffing'}, {'id': 12992, 'synset': 'forcemeat.n.01', 'name': 'forcemeat'}, {'id': 12993, 'synset': 'anadama_bread.n.01', 'name': 'anadama_bread'}, {'id': 12994, 'synset': 'bap.n.01', 'name': 'bap'}, {'id': 12995, 'synset': 'barmbrack.n.01', 'name': 'barmbrack'}, {'id': 12996, 'synset': 'breadstick.n.01', 'name': 'breadstick'}, {'id': 12997, 'synset': 'grissino.n.01', 'name': 'grissino'}, {'id': 12998, 'synset': 'brown_bread.n.02', 'name': 'brown_bread'}, {'id': 12999, 'synset': 'tea_bread.n.01', 'name': 'tea_bread'}, {'id': 13000, 'synset': 'caraway_seed_bread.n.01', 'name': 'caraway_seed_bread'}, {'id': 13001, 'synset': 'challah.n.01', 'name': 'challah'}, {'id': 13002, 'synset': 'cinnamon_bread.n.01', 'name': 'cinnamon_bread'}, {'id': 13003, 'synset': 'cracked-wheat_bread.n.01', 'name': 'cracked-wheat_bread'}, {'id': 13004, 'synset': 'dark_bread.n.01', 'name': 'dark_bread'}, {'id': 13005, 'synset': 'english_muffin.n.01', 'name': 'English_muffin'}, {'id': 13006, 'synset': 'flatbread.n.01', 'name': 'flatbread'}, {'id': 13007, 'synset': 'garlic_bread.n.01', 'name': 'garlic_bread'}, {'id': 13008, 'synset': 'gluten_bread.n.01', 'name': 'gluten_bread'}, {'id': 13009, 'synset': 'graham_bread.n.01', 'name': 'graham_bread'}, {'id': 13010, 'synset': 'host.n.09', 'name': 'Host'}, {'id': 13011, 'synset': 'flatbrod.n.01', 'name': 'flatbrod'}, {'id': 13012, 'synset': 'bannock.n.01', 'name': 'bannock'}, {'id': 13013, 'synset': 'chapatti.n.01', 'name': 'chapatti'}, {'id': 13014, 'synset': 'loaf_of_bread.n.01', 'name': 'loaf_of_bread'}, {'id': 13015, 'synset': 'french_loaf.n.01', 'name': 'French_loaf'}, {'id': 13016, 'synset': 'matzo.n.01', 'name': 'matzo'}, {'id': 13017, 'synset': 'nan.n.04', 'name': 'nan'}, {'id': 13018, 'synset': 'onion_bread.n.01', 'name': 'onion_bread'}, {'id': 13019, 'synset': 'raisin_bread.n.01', 'name': 'raisin_bread'}, {'id': 13020, 'synset': 'quick_bread.n.01', 'name': 'quick_bread'}, {'id': 13021, 'synset': 'banana_bread.n.01', 'name': 'banana_bread'}, {'id': 13022, 'synset': 'date_bread.n.01', 'name': 'date_bread'}, {'id': 13023, 'synset': 'date-nut_bread.n.01', 'name': 'date-nut_bread'}, {'id': 13024, 'synset': 'nut_bread.n.01', 'name': 'nut_bread'}, {'id': 13025, 'synset': 'oatcake.n.01', 'name': 'oatcake'}, {'id': 13026, 'synset': 'irish_soda_bread.n.01', 'name': 'Irish_soda_bread'}, {'id': 13027, 'synset': 'skillet_bread.n.01', 'name': 'skillet_bread'}, {'id': 13028, 'synset': 'rye_bread.n.01', 'name': 'rye_bread'}, {'id': 13029, 'synset': 'black_bread.n.01', 'name': 'black_bread'}, {'id': 13030, 'synset': 'jewish_rye_bread.n.01', 'name': 'Jewish_rye_bread'}, {'id': 13031, 'synset': 'limpa.n.01', 'name': 'limpa'}, {'id': 13032, 'synset': 'swedish_rye_bread.n.01', 'name': 'Swedish_rye_bread'}, {'id': 13033, 'synset': 'salt-rising_bread.n.01', 'name': 'salt-rising_bread'}, {'id': 13034, 'synset': 'simnel.n.01', 'name': 'simnel'}, {'id': 13035, 'synset': 'sour_bread.n.01', 'name': 'sour_bread'}, {'id': 13036, 'synset': 'wafer.n.03', 'name': 'wafer'}, {'id': 13037, 'synset': 'white_bread.n.01', 'name': 'white_bread'}, {'id': 13038, 'synset': 'french_bread.n.01', 'name': 'French_bread'}, {'id': 13039, 'synset': 'italian_bread.n.01', 'name': 'Italian_bread'}, {'id': 13040, 'synset': 'corn_cake.n.01', 'name': 'corn_cake'}, {'id': 13041, 'synset': 'skillet_corn_bread.n.01', 'name': 'skillet_corn_bread'}, {'id': 13042, 'synset': 'ashcake.n.01', 'name': 'ashcake'}, {'id': 13043, 'synset': 'hoecake.n.01', 'name': 'hoecake'}, {'id': 13044, 'synset': 'cornpone.n.01', 'name': 'cornpone'}, {'id': 13045, 'synset': 'corn_dab.n.01', 'name': 'corn_dab'}, {'id': 13046, 'synset': 'hush_puppy.n.01', 'name': 'hush_puppy'}, {'id': 13047, 'synset': 'johnnycake.n.01', 'name': 'johnnycake'}, {'id': 13048, 'synset': 'shawnee_cake.n.01', 'name': 'Shawnee_cake'}, {'id': 13049, 'synset': 'spoon_bread.n.01', 'name': 'spoon_bread'}, {'id': 13050, 'synset': 'cinnamon_toast.n.01', 'name': 'cinnamon_toast'}, {'id': 13051, 'synset': 'orange_toast.n.01', 'name': 'orange_toast'}, {'id': 13052, 'synset': 'melba_toast.n.01', 'name': 'Melba_toast'}, {'id': 13053, 'synset': 'zwieback.n.01', 'name': 'zwieback'}, {'id': 13054, 'synset': 'frankfurter_bun.n.01', 'name': 'frankfurter_bun'}, {'id': 13055, 'synset': 'hamburger_bun.n.01', 'name': 'hamburger_bun'}, {'id': 13056, 'synset': 'bran_muffin.n.01', 'name': 'bran_muffin'}, {'id': 13057, 'synset': 'corn_muffin.n.01', 'name': 'corn_muffin'}, {'id': 13058, 'synset': 'yorkshire_pudding.n.01', 'name': 'Yorkshire_pudding'}, {'id': 13059, 'synset': 'popover.n.01', 'name': 'popover'}, {'id': 13060, 'synset': 'scone.n.01', 'name': 'scone'}, {'id': 13061, 'synset': 'drop_scone.n.01', 'name': 'drop_scone'}, {'id': 13062, 'synset': 'cross_bun.n.01', 'name': 'cross_bun'}, {'id': 13063, 'synset': 'brioche.n.01', 'name': 'brioche'}, {'id': 13064, 'synset': 'hard_roll.n.01', 'name': 'hard_roll'}, {'id': 13065, 'synset': 'soft_roll.n.01', 'name': 'soft_roll'}, {'id': 13066, 'synset': 'kaiser_roll.n.01', 'name': 'kaiser_roll'}, {'id': 13067, 'synset': 'parker_house_roll.n.01', 'name': 'Parker_House_roll'}, {'id': 13068, 'synset': 'clover-leaf_roll.n.01', 'name': 'clover-leaf_roll'}, {'id': 13069, 'synset': 'onion_roll.n.01', 'name': 'onion_roll'}, {'id': 13070, 'synset': 'bialy.n.01', 'name': 'bialy'}, {'id': 13071, 'synset': 'sweet_roll.n.01', 'name': 'sweet_roll'}, {'id': 13072, 'synset': 'bear_claw.n.01', 'name': 'bear_claw'}, {'id': 13073, 'synset': 'cinnamon_roll.n.01', 'name': 'cinnamon_roll'}, {'id': 13074, 'synset': 'honey_bun.n.01', 'name': 'honey_bun'}, {'id': 13075, 'synset': 'pinwheel_roll.n.01', 'name': 'pinwheel_roll'}, {'id': 13076, 'synset': 'danish.n.02', 'name': 'danish'}, {'id': 13077, 'synset': 'onion_bagel.n.01', 'name': 'onion_bagel'}, {'id': 13078, 'synset': 'biscuit.n.01', 'name': 'biscuit'}, {'id': 13079, 'synset': 'rolled_biscuit.n.01', 'name': 'rolled_biscuit'}, {'id': 13080, 'synset': 'baking-powder_biscuit.n.01', 'name': 'baking-powder_biscuit'}, {'id': 13081, 'synset': 'buttermilk_biscuit.n.01', 'name': 'buttermilk_biscuit'}, {'id': 13082, 'synset': 'shortcake.n.01', 'name': 'shortcake'}, {'id': 13083, 'synset': 'hardtack.n.01', 'name': 'hardtack'}, {'id': 13084, 'synset': 'saltine.n.01', 'name': 'saltine'}, {'id': 13085, 'synset': 'soda_cracker.n.01', 'name': 'soda_cracker'}, {'id': 13086, 'synset': 'oyster_cracker.n.01', 'name': 'oyster_cracker'}, {'id': 13087, 'synset': 'water_biscuit.n.01', 'name': 'water_biscuit'}, {'id': 13088, 'synset': 'graham_cracker.n.01', 'name': 'graham_cracker'}, {'id': 13089, 'synset': 'soft_pretzel.n.01', 'name': 'soft_pretzel'}, {'id': 13090, 'synset': 'sandwich_plate.n.01', 'name': 'sandwich_plate'}, {'id': 13091, 'synset': 'butty.n.01', 'name': 'butty'}, {'id': 13092, 'synset': 'ham_sandwich.n.01', 'name': 'ham_sandwich'}, {'id': 13093, 'synset': 'chicken_sandwich.n.01', 'name': 'chicken_sandwich'}, {'id': 13094, 'synset': 'club_sandwich.n.01', 'name': 'club_sandwich'}, {'id': 13095, 'synset': 'open-face_sandwich.n.01', 'name': 'open-face_sandwich'}, {'id': 13096, 'synset': 'cheeseburger.n.01', 'name': 'cheeseburger'}, {'id': 13097, 'synset': 'tunaburger.n.01', 'name': 'tunaburger'}, {'id': 13098, 'synset': 'hotdog.n.02', 'name': 'hotdog'}, {'id': 13099, 'synset': 'sloppy_joe.n.01', 'name': 'Sloppy_Joe'}, {'id': 13100, 'synset': 'bomber.n.03', 'name': 'bomber'}, {'id': 13101, 'synset': 'gyro.n.01', 'name': 'gyro'}, {'id': 13102, 'synset': 'bacon-lettuce-tomato_sandwich.n.01', 'name': 'bacon-lettuce-tomato_sandwich'}, {'id': 13103, 'synset': 'reuben.n.02', 'name': 'Reuben'}, {'id': 13104, 'synset': 'western.n.02', 'name': 'western'}, {'id': 13105, 'synset': 'wrap.n.02', 'name': 'wrap'}, {'id': 13106, 'synset': 'spaghetti.n.01', 'name': 'spaghetti'}, {'id': 13107, 'synset': 'hasty_pudding.n.01', 'name': 'hasty_pudding'}, {'id': 13108, 'synset': 'gruel.n.01', 'name': 'gruel'}, {'id': 13109, 'synset': 'congee.n.01', 'name': 'congee'}, {'id': 13110, 'synset': 'skilly.n.01', 'name': 'skilly'}, {'id': 13111, 'synset': 'edible_fruit.n.01', 'name': 'edible_fruit'}, {'id': 13112, 'synset': 'vegetable.n.01', 'name': 'vegetable'}, {'id': 13113, 'synset': 'julienne.n.01', 'name': 'julienne'}, {'id': 13114, 'synset': 'raw_vegetable.n.01', 'name': 'raw_vegetable'}, {'id': 13115, 'synset': 'crudites.n.01', 'name': 'crudites'}, {'id': 13116, 'synset': 'celery_stick.n.01', 'name': 'celery_stick'}, {'id': 13117, 'synset': 'legume.n.03', 'name': 'legume'}, {'id': 13118, 'synset': 'pulse.n.04', 'name': 'pulse'}, {'id': 13119, 'synset': 'potherb.n.01', 'name': 'potherb'}, {'id': 13120, 'synset': 'greens.n.01', 'name': 'greens'}, {'id': 13121, 'synset': 'chop-suey_greens.n.02', 'name': 'chop-suey_greens'}, {'id': 13122, 'synset': 'solanaceous_vegetable.n.01', 'name': 'solanaceous_vegetable'}, {'id': 13123, 'synset': 'root_vegetable.n.01', 'name': 'root_vegetable'}, {'id': 13124, 'synset': 'baked_potato.n.01', 'name': 'baked_potato'}, {'id': 13125, 'synset': 'french_fries.n.01', 'name': 'french_fries'}, {'id': 13126, 'synset': 'home_fries.n.01', 'name': 'home_fries'}, {'id': 13127, 'synset': 'jacket_potato.n.01', 'name': 'jacket_potato'}, {'id': 13128, 'synset': 'potato_skin.n.01', 'name': 'potato_skin'}, {'id': 13129, 'synset': 'uruguay_potato.n.02', 'name': 'Uruguay_potato'}, {'id': 13130, 'synset': 'yam.n.04', 'name': 'yam'}, {'id': 13131, 'synset': 'yam.n.03', 'name': 'yam'}, {'id': 13132, 'synset': 'snack_food.n.01', 'name': 'snack_food'}, {'id': 13133, 'synset': 'corn_chip.n.01', 'name': 'corn_chip'}, {'id': 13134, 'synset': 'tortilla_chip.n.01', 'name': 'tortilla_chip'}, {'id': 13135, 'synset': 'nacho.n.01', 'name': 'nacho'}, {'id': 13136, 'synset': 'pieplant.n.01', 'name': 'pieplant'}, {'id': 13137, 'synset': 'cruciferous_vegetable.n.01', 'name': 'cruciferous_vegetable'}, {'id': 13138, 'synset': 'mustard.n.03', 'name': 'mustard'}, {'id': 13139, 'synset': 'cabbage.n.01', 'name': 'cabbage'}, {'id': 13140, 'synset': 'kale.n.03', 'name': 'kale'}, {'id': 13141, 'synset': 'collards.n.01', 'name': 'collards'}, {'id': 13142, 'synset': 'chinese_cabbage.n.02', 'name': 'Chinese_cabbage'}, {'id': 13143, 'synset': 'bok_choy.n.02', 'name': 'bok_choy'}, {'id': 13144, 'synset': 'head_cabbage.n.02', 'name': 'head_cabbage'}, {'id': 13145, 'synset': 'red_cabbage.n.02', 'name': 'red_cabbage'}, {'id': 13146, 'synset': 'savoy_cabbage.n.02', 'name': 'savoy_cabbage'}, {'id': 13147, 'synset': 'broccoli.n.02', 'name': 'broccoli'}, {'id': 13148, 'synset': 'broccoli_rabe.n.02', 'name': 'broccoli_rabe'}, {'id': 13149, 'synset': 'squash.n.02', 'name': 'squash'}, {'id': 13150, 'synset': 'summer_squash.n.02', 'name': 'summer_squash'}, {'id': 13151, 'synset': 'yellow_squash.n.02', 'name': 'yellow_squash'}, {'id': 13152, 'synset': 'crookneck.n.01', 'name': 'crookneck'}, {'id': 13153, 'synset': 'marrow.n.04', 'name': 'marrow'}, {'id': 13154, 'synset': 'cocozelle.n.02', 'name': 'cocozelle'}, {'id': 13155, 'synset': 'pattypan_squash.n.02', 'name': 'pattypan_squash'}, {'id': 13156, 'synset': 'spaghetti_squash.n.02', 'name': 'spaghetti_squash'}, {'id': 13157, 'synset': 'winter_squash.n.02', 'name': 'winter_squash'}, {'id': 13158, 'synset': 'acorn_squash.n.02', 'name': 'acorn_squash'}, {'id': 13159, 'synset': 'butternut_squash.n.02', 'name': 'butternut_squash'}, {'id': 13160, 'synset': 'hubbard_squash.n.02', 'name': 'hubbard_squash'}, {'id': 13161, 'synset': 'turban_squash.n.02', 'name': 'turban_squash'}, {'id': 13162, 'synset': 'buttercup_squash.n.02', 'name': 'buttercup_squash'}, {'id': 13163, 'synset': 'cushaw.n.02', 'name': 'cushaw'}, {'id': 13164, 'synset': 'winter_crookneck_squash.n.02', 'name': 'winter_crookneck_squash'}, {'id': 13165, 'synset': 'gherkin.n.02', 'name': 'gherkin'}, {'id': 13166, 'synset': 'artichoke_heart.n.01', 'name': 'artichoke_heart'}, {'id': 13167, 'synset': 'jerusalem_artichoke.n.03', 'name': 'Jerusalem_artichoke'}, {'id': 13168, 'synset': 'bamboo_shoot.n.01', 'name': 'bamboo_shoot'}, {'id': 13169, 'synset': 'sprout.n.02', 'name': 'sprout'}, {'id': 13170, 'synset': 'bean_sprout.n.01', 'name': 'bean_sprout'}, {'id': 13171, 'synset': 'alfalfa_sprout.n.01', 'name': 'alfalfa_sprout'}, {'id': 13172, 'synset': 'beet.n.02', 'name': 'beet'}, {'id': 13173, 'synset': 'beet_green.n.01', 'name': 'beet_green'}, {'id': 13174, 'synset': 'sugar_beet.n.02', 'name': 'sugar_beet'}, {'id': 13175, 'synset': 'mangel-wurzel.n.02', 'name': 'mangel-wurzel'}, {'id': 13176, 'synset': 'chard.n.02', 'name': 'chard'}, {'id': 13177, 'synset': 'pepper.n.04', 'name': 'pepper'}, {'id': 13178, 'synset': 'sweet_pepper.n.02', 'name': 'sweet_pepper'}, {'id': 13179, 'synset': 'green_pepper.n.01', 'name': 'green_pepper'}, {'id': 13180, 'synset': 'globe_pepper.n.01', 'name': 'globe_pepper'}, {'id': 13181, 'synset': 'pimento.n.02', 'name': 'pimento'}, {'id': 13182, 'synset': 'hot_pepper.n.02', 'name': 'hot_pepper'}, {'id': 13183, 'synset': 'jalapeno.n.02', 'name': 'jalapeno'}, {'id': 13184, 'synset': 'chipotle.n.01', 'name': 'chipotle'}, {'id': 13185, 'synset': 'cayenne.n.03', 'name': 'cayenne'}, {'id': 13186, 'synset': 'tabasco.n.03', 'name': 'tabasco'}, {'id': 13187, 'synset': 'onion.n.03', 'name': 'onion'}, {'id': 13188, 'synset': 'bermuda_onion.n.01', 'name': 'Bermuda_onion'}, {'id': 13189, 'synset': 'vidalia_onion.n.01', 'name': 'Vidalia_onion'}, {'id': 13190, 'synset': 'spanish_onion.n.01', 'name': 'Spanish_onion'}, {'id': 13191, 'synset': 'purple_onion.n.01', 'name': 'purple_onion'}, {'id': 13192, 'synset': 'leek.n.02', 'name': 'leek'}, {'id': 13193, 'synset': 'shallot.n.03', 'name': 'shallot'}, {'id': 13194, 'synset': 'salad_green.n.01', 'name': 'salad_green'}, {'id': 13195, 'synset': 'lettuce.n.03', 'name': 'lettuce'}, {'id': 13196, 'synset': 'butterhead_lettuce.n.01', 'name': 'butterhead_lettuce'}, {'id': 13197, 'synset': 'buttercrunch.n.01', 'name': 'buttercrunch'}, {'id': 13198, 'synset': 'bibb_lettuce.n.01', 'name': 'Bibb_lettuce'}, {'id': 13199, 'synset': 'boston_lettuce.n.01', 'name': 'Boston_lettuce'}, {'id': 13200, 'synset': 'crisphead_lettuce.n.01', 'name': 'crisphead_lettuce'}, {'id': 13201, 'synset': 'cos.n.02', 'name': 'cos'}, {'id': 13202, 'synset': 'leaf_lettuce.n.02', 'name': 'leaf_lettuce'}, {'id': 13203, 'synset': 'celtuce.n.02', 'name': 'celtuce'}, {'id': 13204, 'synset': 'bean.n.01', 'name': 'bean'}, {'id': 13205, 'synset': 'goa_bean.n.02', 'name': 'goa_bean'}, {'id': 13206, 'synset': 'lentil.n.01', 'name': 'lentil'}, {'id': 13207, 'synset': 'green_pea.n.01', 'name': 'green_pea'}, {'id': 13208, 'synset': 'marrowfat_pea.n.01', 'name': 'marrowfat_pea'}, {'id': 13209, 'synset': 'snow_pea.n.02', 'name': 'snow_pea'}, {'id': 13210, 'synset': 'sugar_snap_pea.n.02', 'name': 'sugar_snap_pea'}, {'id': 13211, 'synset': 'split-pea.n.01', 'name': 'split-pea'}, {'id': 13212, 'synset': 'chickpea.n.03', 'name': 'chickpea'}, {'id': 13213, 'synset': 'cajan_pea.n.02', 'name': 'cajan_pea'}, {'id': 13214, 'synset': 'field_pea.n.03', 'name': 'field_pea'}, {'id': 13215, 'synset': 'mushy_peas.n.01', 'name': 'mushy_peas'}, {'id': 13216, 'synset': 'black-eyed_pea.n.03', 'name': 'black-eyed_pea'}, {'id': 13217, 'synset': 'common_bean.n.02', 'name': 'common_bean'}, {'id': 13218, 'synset': 'kidney_bean.n.02', 'name': 'kidney_bean'}, {'id': 13219, 'synset': 'navy_bean.n.01', 'name': 'navy_bean'}, {'id': 13220, 'synset': 'pinto_bean.n.01', 'name': 'pinto_bean'}, {'id': 13221, 'synset': 'frijole.n.02', 'name': 'frijole'}, {'id': 13222, 'synset': 'black_bean.n.01', 'name': 'black_bean'}, {'id': 13223, 'synset': 'fresh_bean.n.01', 'name': 'fresh_bean'}, {'id': 13224, 'synset': 'flageolet.n.01', 'name': 'flageolet'}, {'id': 13225, 'synset': 'green_bean.n.01', 'name': 'green_bean'}, {'id': 13226, 'synset': 'snap_bean.n.01', 'name': 'snap_bean'}, {'id': 13227, 'synset': 'string_bean.n.01', 'name': 'string_bean'}, {'id': 13228, 'synset': 'kentucky_wonder.n.01', 'name': 'Kentucky_wonder'}, {'id': 13229, 'synset': 'scarlet_runner.n.03', 'name': 'scarlet_runner'}, {'id': 13230, 'synset': 'haricot_vert.n.01', 'name': 'haricot_vert'}, {'id': 13231, 'synset': 'wax_bean.n.02', 'name': 'wax_bean'}, {'id': 13232, 'synset': 'shell_bean.n.02', 'name': 'shell_bean'}, {'id': 13233, 'synset': 'lima_bean.n.03', 'name': 'lima_bean'}, {'id': 13234, 'synset': 'fordhooks.n.01', 'name': 'Fordhooks'}, {'id': 13235, 'synset': 'sieva_bean.n.02', 'name': 'sieva_bean'}, {'id': 13236, 'synset': 'fava_bean.n.02', 'name': 'fava_bean'}, {'id': 13237, 'synset': 'soy.n.04', 'name': 'soy'}, {'id': 13238, 'synset': 'green_soybean.n.01', 'name': 'green_soybean'}, {'id': 13239, 'synset': 'field_soybean.n.01', 'name': 'field_soybean'}, {'id': 13240, 'synset': 'cardoon.n.02', 'name': 'cardoon'}, {'id': 13241, 'synset': 'carrot.n.03', 'name': 'carrot'}, {'id': 13242, 'synset': 'carrot_stick.n.01', 'name': 'carrot_stick'}, {'id': 13243, 'synset': 'celery.n.02', 'name': 'celery'}, {'id': 13244, 'synset': 'pascal_celery.n.01', 'name': 'pascal_celery'}, {'id': 13245, 'synset': 'celeriac.n.02', 'name': 'celeriac'}, {'id': 13246, 'synset': 'chicory.n.04', 'name': 'chicory'}, {'id': 13247, 'synset': 'radicchio.n.01', 'name': 'radicchio'}, {'id': 13248, 'synset': 'coffee_substitute.n.01', 'name': 'coffee_substitute'}, {'id': 13249, 'synset': 'chicory.n.03', 'name': 'chicory'}, {'id': 13250, 'synset': 'postum.n.01', 'name': 'Postum'}, {'id': 13251, 'synset': 'chicory_escarole.n.01', 'name': 'chicory_escarole'}, {'id': 13252, 'synset': 'belgian_endive.n.01', 'name': 'Belgian_endive'}, {'id': 13253, 'synset': 'sweet_corn.n.02', 'name': 'sweet_corn'}, {'id': 13254, 'synset': 'hominy.n.01', 'name': 'hominy'}, {'id': 13255, 'synset': 'lye_hominy.n.01', 'name': 'lye_hominy'}, {'id': 13256, 'synset': 'pearl_hominy.n.01', 'name': 'pearl_hominy'}, {'id': 13257, 'synset': 'popcorn.n.02', 'name': 'popcorn'}, {'id': 13258, 'synset': 'cress.n.02', 'name': 'cress'}, {'id': 13259, 'synset': 'watercress.n.02', 'name': 'watercress'}, {'id': 13260, 'synset': 'garden_cress.n.01', 'name': 'garden_cress'}, {'id': 13261, 'synset': 'winter_cress.n.02', 'name': 'winter_cress'}, {'id': 13262, 'synset': 'dandelion_green.n.02', 'name': 'dandelion_green'}, {'id': 13263, 'synset': 'gumbo.n.03', 'name': 'gumbo'}, {'id': 13264, 'synset': 'kohlrabi.n.02', 'name': 'kohlrabi'}, {'id': 13265, 'synset': "lamb's-quarter.n.01", 'name': "lamb's-quarter"}, {'id': 13266, 'synset': 'wild_spinach.n.03', 'name': 'wild_spinach'}, {'id': 13267, 'synset': 'beefsteak_tomato.n.01', 'name': 'beefsteak_tomato'}, {'id': 13268, 'synset': 'cherry_tomato.n.02', 'name': 'cherry_tomato'}, {'id': 13269, 'synset': 'plum_tomato.n.02', 'name': 'plum_tomato'}, {'id': 13270, 'synset': 'tomatillo.n.03', 'name': 'tomatillo'}, {'id': 13271, 'synset': 'mushroom.n.05', 'name': 'mushroom'}, {'id': 13272, 'synset': 'stuffed_mushroom.n.01', 'name': 'stuffed_mushroom'}, {'id': 13273, 'synset': 'salsify.n.03', 'name': 'salsify'}, {'id': 13274, 'synset': 'oyster_plant.n.03', 'name': 'oyster_plant'}, {'id': 13275, 'synset': 'scorzonera.n.02', 'name': 'scorzonera'}, {'id': 13276, 'synset': 'parsnip.n.03', 'name': 'parsnip'}, {'id': 13277, 'synset': 'radish.n.01', 'name': 'radish'}, {'id': 13278, 'synset': 'turnip.n.02', 'name': 'turnip'}, {'id': 13279, 'synset': 'white_turnip.n.02', 'name': 'white_turnip'}, {'id': 13280, 'synset': 'rutabaga.n.01', 'name': 'rutabaga'}, {'id': 13281, 'synset': 'turnip_greens.n.01', 'name': 'turnip_greens'}, {'id': 13282, 'synset': 'sorrel.n.04', 'name': 'sorrel'}, {'id': 13283, 'synset': 'french_sorrel.n.02', 'name': 'French_sorrel'}, {'id': 13284, 'synset': 'spinach.n.02', 'name': 'spinach'}, {'id': 13285, 'synset': 'taro.n.03', 'name': 'taro'}, {'id': 13286, 'synset': 'truffle.n.02', 'name': 'truffle'}, {'id': 13287, 'synset': 'edible_nut.n.01', 'name': 'edible_nut'}, {'id': 13288, 'synset': 'bunya_bunya.n.02', 'name': 'bunya_bunya'}, {'id': 13289, 'synset': 'peanut.n.04', 'name': 'peanut'}, {'id': 13290, 'synset': 'freestone.n.01', 'name': 'freestone'}, {'id': 13291, 'synset': 'cling.n.01', 'name': 'cling'}, {'id': 13292, 'synset': 'windfall.n.01', 'name': 'windfall'}, {'id': 13293, 'synset': 'crab_apple.n.03', 'name': 'crab_apple'}, {'id': 13294, 'synset': 'eating_apple.n.01', 'name': 'eating_apple'}, {'id': 13295, 'synset': 'baldwin.n.03', 'name': 'Baldwin'}, {'id': 13296, 'synset': 'cortland.n.01', 'name': 'Cortland'}, {'id': 13297, 'synset': "cox's_orange_pippin.n.01", 'name': "Cox's_Orange_Pippin"}, {'id': 13298, 'synset': 'delicious.n.01', 'name': 'Delicious'}, {'id': 13299, 'synset': 'golden_delicious.n.01', 'name': 'Golden_Delicious'}, {'id': 13300, 'synset': 'red_delicious.n.01', 'name': 'Red_Delicious'}, {'id': 13301, 'synset': 'empire.n.05', 'name': 'Empire'}, {'id': 13302, 'synset': "grimes'_golden.n.01", 'name': "Grimes'_golden"}, {'id': 13303, 'synset': 'jonathan.n.01', 'name': 'Jonathan'}, {'id': 13304, 'synset': 'mcintosh.n.01', 'name': 'McIntosh'}, {'id': 13305, 'synset': 'macoun.n.01', 'name': 'Macoun'}, {'id': 13306, 'synset': 'northern_spy.n.01', 'name': 'Northern_Spy'}, {'id': 13307, 'synset': 'pearmain.n.01', 'name': 'Pearmain'}, {'id': 13308, 'synset': 'pippin.n.01', 'name': 'Pippin'}, {'id': 13309, 'synset': 'prima.n.01', 'name': 'Prima'}, {'id': 13310, 'synset': 'stayman.n.01', 'name': 'Stayman'}, {'id': 13311, 'synset': 'winesap.n.01', 'name': 'Winesap'}, {'id': 13312, 'synset': 'stayman_winesap.n.01', 'name': 'Stayman_Winesap'}, {'id': 13313, 'synset': 'cooking_apple.n.01', 'name': 'cooking_apple'}, {'id': 13314, 'synset': "bramley's_seedling.n.01", 'name': "Bramley's_Seedling"}, {'id': 13315, 'synset': 'granny_smith.n.01', 'name': 'Granny_Smith'}, {'id': 13316, 'synset': "lane's_prince_albert.n.01", 'name': "Lane's_Prince_Albert"}, {'id': 13317, 'synset': 'newtown_wonder.n.01', 'name': 'Newtown_Wonder'}, {'id': 13318, 'synset': 'rome_beauty.n.01', 'name': 'Rome_Beauty'}, {'id': 13319, 'synset': 'berry.n.01', 'name': 'berry'}, {'id': 13320, 'synset': 'bilberry.n.03', 'name': 'bilberry'}, {'id': 13321, 'synset': 'huckleberry.n.03', 'name': 'huckleberry'}, {'id': 13322, 'synset': 'wintergreen.n.03', 'name': 'wintergreen'}, {'id': 13323, 'synset': 'cranberry.n.02', 'name': 'cranberry'}, {'id': 13324, 'synset': 'lingonberry.n.02', 'name': 'lingonberry'}, {'id': 13325, 'synset': 'currant.n.01', 'name': 'currant'}, {'id': 13326, 'synset': 'gooseberry.n.02', 'name': 'gooseberry'}, {'id': 13327, 'synset': 'black_currant.n.02', 'name': 'black_currant'}, {'id': 13328, 'synset': 'red_currant.n.02', 'name': 'red_currant'}, {'id': 13329, 'synset': 'boysenberry.n.02', 'name': 'boysenberry'}, {'id': 13330, 'synset': 'dewberry.n.02', 'name': 'dewberry'}, {'id': 13331, 'synset': 'loganberry.n.02', 'name': 'loganberry'}, {'id': 13332, 'synset': 'saskatoon.n.02', 'name': 'saskatoon'}, {'id': 13333, 'synset': 'sugarberry.n.02', 'name': 'sugarberry'}, {'id': 13334, 'synset': 'acerola.n.02', 'name': 'acerola'}, {'id': 13335, 'synset': 'carambola.n.02', 'name': 'carambola'}, {'id': 13336, 'synset': 'ceriman.n.02', 'name': 'ceriman'}, {'id': 13337, 'synset': 'carissa_plum.n.01', 'name': 'carissa_plum'}, {'id': 13338, 'synset': 'citrus.n.01', 'name': 'citrus'}, {'id': 13339, 'synset': 'temple_orange.n.02', 'name': 'temple_orange'}, {'id': 13340, 'synset': 'clementine.n.02', 'name': 'clementine'}, {'id': 13341, 'synset': 'satsuma.n.02', 'name': 'satsuma'}, {'id': 13342, 'synset': 'tangerine.n.02', 'name': 'tangerine'}, {'id': 13343, 'synset': 'tangelo.n.02', 'name': 'tangelo'}, {'id': 13344, 'synset': 'bitter_orange.n.02', 'name': 'bitter_orange'}, {'id': 13345, 'synset': 'sweet_orange.n.01', 'name': 'sweet_orange'}, {'id': 13346, 'synset': 'jaffa_orange.n.01', 'name': 'Jaffa_orange'}, {'id': 13347, 'synset': 'navel_orange.n.01', 'name': 'navel_orange'}, {'id': 13348, 'synset': 'valencia_orange.n.01', 'name': 'Valencia_orange'}, {'id': 13349, 'synset': 'kumquat.n.02', 'name': 'kumquat'}, {'id': 13350, 'synset': 'key_lime.n.01', 'name': 'key_lime'}, {'id': 13351, 'synset': 'grapefruit.n.02', 'name': 'grapefruit'}, {'id': 13352, 'synset': 'pomelo.n.02', 'name': 'pomelo'}, {'id': 13353, 'synset': 'citrange.n.02', 'name': 'citrange'}, {'id': 13354, 'synset': 'citron.n.01', 'name': 'citron'}, {'id': 13355, 'synset': 'jordan_almond.n.02', 'name': 'Jordan_almond'}, {'id': 13356, 'synset': 'nectarine.n.02', 'name': 'nectarine'}, {'id': 13357, 'synset': 'pitahaya.n.02', 'name': 'pitahaya'}, {'id': 13358, 'synset': 'plum.n.02', 'name': 'plum'}, {'id': 13359, 'synset': 'damson.n.01', 'name': 'damson'}, {'id': 13360, 'synset': 'greengage.n.01', 'name': 'greengage'}, {'id': 13361, 'synset': 'beach_plum.n.02', 'name': 'beach_plum'}, {'id': 13362, 'synset': 'sloe.n.03', 'name': 'sloe'}, {'id': 13363, 'synset': 'victoria_plum.n.01', 'name': 'Victoria_plum'}, {'id': 13364, 'synset': 'dried_fruit.n.01', 'name': 'dried_fruit'}, {'id': 13365, 'synset': 'dried_apricot.n.01', 'name': 'dried_apricot'}, {'id': 13366, 'synset': 'raisin.n.01', 'name': 'raisin'}, {'id': 13367, 'synset': 'seedless_raisin.n.01', 'name': 'seedless_raisin'}, {'id': 13368, 'synset': 'seeded_raisin.n.01', 'name': 'seeded_raisin'}, {'id': 13369, 'synset': 'currant.n.03', 'name': 'currant'}, {'id': 13370, 'synset': 'anchovy_pear.n.02', 'name': 'anchovy_pear'}, {'id': 13371, 'synset': 'passion_fruit.n.01', 'name': 'passion_fruit'}, {'id': 13372, 'synset': 'granadilla.n.04', 'name': 'granadilla'}, {'id': 13373, 'synset': 'sweet_calabash.n.02', 'name': 'sweet_calabash'}, {'id': 13374, 'synset': 'bell_apple.n.01', 'name': 'bell_apple'}, {'id': 13375, 'synset': 'breadfruit.n.02', 'name': 'breadfruit'}, {'id': 13376, 'synset': 'jackfruit.n.02', 'name': 'jackfruit'}, {'id': 13377, 'synset': 'cacao_bean.n.01', 'name': 'cacao_bean'}, {'id': 13378, 'synset': 'cocoa.n.02', 'name': 'cocoa'}, {'id': 13379, 'synset': 'canistel.n.02', 'name': 'canistel'}, {'id': 13380, 'synset': 'melon_ball.n.01', 'name': 'melon_ball'}, {'id': 13381, 'synset': 'muskmelon.n.02', 'name': 'muskmelon'}, {'id': 13382, 'synset': 'winter_melon.n.02', 'name': 'winter_melon'}, {'id': 13383, 'synset': 'honeydew.n.01', 'name': 'honeydew'}, {'id': 13384, 'synset': 'persian_melon.n.02', 'name': 'Persian_melon'}, {'id': 13385, 'synset': 'net_melon.n.02', 'name': 'net_melon'}, {'id': 13386, 'synset': 'casaba.n.01', 'name': 'casaba'}, {'id': 13387, 'synset': 'sweet_cherry.n.02', 'name': 'sweet_cherry'}, {'id': 13388, 'synset': 'bing_cherry.n.01', 'name': 'bing_cherry'}, {'id': 13389, 'synset': 'heart_cherry.n.02', 'name': 'heart_cherry'}, {'id': 13390, 'synset': 'blackheart.n.02', 'name': 'blackheart'}, {'id': 13391, 'synset': 'capulin.n.02', 'name': 'capulin'}, {'id': 13392, 'synset': 'sour_cherry.n.03', 'name': 'sour_cherry'}, {'id': 13393, 'synset': 'amarelle.n.02', 'name': 'amarelle'}, {'id': 13394, 'synset': 'morello.n.02', 'name': 'morello'}, {'id': 13395, 'synset': 'cocoa_plum.n.02', 'name': 'cocoa_plum'}, {'id': 13396, 'synset': 'gherkin.n.01', 'name': 'gherkin'}, {'id': 13397, 'synset': 'fox_grape.n.02', 'name': 'fox_grape'}, {'id': 13398, 'synset': 'concord_grape.n.01', 'name': 'Concord_grape'}, {'id': 13399, 'synset': 'catawba.n.02', 'name': 'Catawba'}, {'id': 13400, 'synset': 'muscadine.n.02', 'name': 'muscadine'}, {'id': 13401, 'synset': 'scuppernong.n.01', 'name': 'scuppernong'}, {'id': 13402, 'synset': 'slipskin_grape.n.01', 'name': 'slipskin_grape'}, {'id': 13403, 'synset': 'vinifera_grape.n.02', 'name': 'vinifera_grape'}, {'id': 13404, 'synset': 'emperor.n.02', 'name': 'emperor'}, {'id': 13405, 'synset': 'muscat.n.04', 'name': 'muscat'}, {'id': 13406, 'synset': 'ribier.n.01', 'name': 'ribier'}, {'id': 13407, 'synset': 'sultana.n.01', 'name': 'sultana'}, {'id': 13408, 'synset': 'tokay.n.02', 'name': 'Tokay'}, {'id': 13409, 'synset': 'flame_tokay.n.01', 'name': 'flame_tokay'}, {'id': 13410, 'synset': 'thompson_seedless.n.01', 'name': 'Thompson_Seedless'}, {'id': 13411, 'synset': 'custard_apple.n.02', 'name': 'custard_apple'}, {'id': 13412, 'synset': 'cherimoya.n.02', 'name': 'cherimoya'}, {'id': 13413, 'synset': 'soursop.n.02', 'name': 'soursop'}, {'id': 13414, 'synset': 'sweetsop.n.02', 'name': 'sweetsop'}, {'id': 13415, 'synset': 'ilama.n.02', 'name': 'ilama'}, {'id': 13416, 'synset': 'pond_apple.n.02', 'name': 'pond_apple'}, {'id': 13417, 'synset': 'papaw.n.02', 'name': 'papaw'}, {'id': 13418, 'synset': 'kai_apple.n.01', 'name': 'kai_apple'}, {'id': 13419, 'synset': 'ketembilla.n.02', 'name': 'ketembilla'}, {'id': 13420, 'synset': 'ackee.n.01', 'name': 'ackee'}, {'id': 13421, 'synset': 'durian.n.02', 'name': 'durian'}, {'id': 13422, 'synset': 'feijoa.n.02', 'name': 'feijoa'}, {'id': 13423, 'synset': 'genip.n.02', 'name': 'genip'}, {'id': 13424, 'synset': 'genipap.n.01', 'name': 'genipap'}, {'id': 13425, 'synset': 'loquat.n.02', 'name': 'loquat'}, {'id': 13426, 'synset': 'mangosteen.n.02', 'name': 'mangosteen'}, {'id': 13427, 'synset': 'mango.n.02', 'name': 'mango'}, {'id': 13428, 'synset': 'sapodilla.n.02', 'name': 'sapodilla'}, {'id': 13429, 'synset': 'sapote.n.02', 'name': 'sapote'}, {'id': 13430, 'synset': 'tamarind.n.02', 'name': 'tamarind'}, {'id': 13431, 'synset': 'elderberry.n.02', 'name': 'elderberry'}, {'id': 13432, 'synset': 'guava.n.03', 'name': 'guava'}, {'id': 13433, 'synset': 'mombin.n.02', 'name': 'mombin'}, {'id': 13434, 'synset': 'hog_plum.n.04', 'name': 'hog_plum'}, {'id': 13435, 'synset': 'hog_plum.n.03', 'name': 'hog_plum'}, {'id': 13436, 'synset': 'jaboticaba.n.02', 'name': 'jaboticaba'}, {'id': 13437, 'synset': 'jujube.n.02', 'name': 'jujube'}, {'id': 13438, 'synset': 'litchi.n.02', 'name': 'litchi'}, {'id': 13439, 'synset': 'longanberry.n.02', 'name': 'longanberry'}, {'id': 13440, 'synset': 'mamey.n.02', 'name': 'mamey'}, {'id': 13441, 'synset': 'marang.n.02', 'name': 'marang'}, {'id': 13442, 'synset': 'medlar.n.04', 'name': 'medlar'}, {'id': 13443, 'synset': 'medlar.n.03', 'name': 'medlar'}, {'id': 13444, 'synset': 'mulberry.n.02', 'name': 'mulberry'}, {'id': 13445, 'synset': 'olive.n.04', 'name': 'olive'}, {'id': 13446, 'synset': 'black_olive.n.01', 'name': 'black_olive'}, {'id': 13447, 'synset': 'green_olive.n.01', 'name': 'green_olive'}, {'id': 13448, 'synset': 'bosc.n.01', 'name': 'bosc'}, {'id': 13449, 'synset': 'anjou.n.02', 'name': 'anjou'}, {'id': 13450, 'synset': 'bartlett.n.03', 'name': 'bartlett'}, {'id': 13451, 'synset': 'seckel.n.01', 'name': 'seckel'}, {'id': 13452, 'synset': 'plantain.n.03', 'name': 'plantain'}, {'id': 13453, 'synset': 'plumcot.n.02', 'name': 'plumcot'}, {'id': 13454, 'synset': 'pomegranate.n.02', 'name': 'pomegranate'}, {'id': 13455, 'synset': 'prickly_pear.n.02', 'name': 'prickly_pear'}, {'id': 13456, 'synset': 'barbados_gooseberry.n.02', 'name': 'Barbados_gooseberry'}, {'id': 13457, 'synset': 'quandong.n.04', 'name': 'quandong'}, {'id': 13458, 'synset': 'quandong_nut.n.01', 'name': 'quandong_nut'}, {'id': 13459, 'synset': 'quince.n.02', 'name': 'quince'}, {'id': 13460, 'synset': 'rambutan.n.02', 'name': 'rambutan'}, {'id': 13461, 'synset': 'pulasan.n.02', 'name': 'pulasan'}, {'id': 13462, 'synset': 'rose_apple.n.02', 'name': 'rose_apple'}, {'id': 13463, 'synset': 'sorb.n.01', 'name': 'sorb'}, {'id': 13464, 'synset': 'sour_gourd.n.02', 'name': 'sour_gourd'}, {'id': 13465, 'synset': 'edible_seed.n.01', 'name': 'edible_seed'}, {'id': 13466, 'synset': 'pumpkin_seed.n.01', 'name': 'pumpkin_seed'}, {'id': 13467, 'synset': 'betel_nut.n.01', 'name': 'betel_nut'}, {'id': 13468, 'synset': 'beechnut.n.01', 'name': 'beechnut'}, {'id': 13469, 'synset': 'walnut.n.01', 'name': 'walnut'}, {'id': 13470, 'synset': 'black_walnut.n.02', 'name': 'black_walnut'}, {'id': 13471, 'synset': 'english_walnut.n.02', 'name': 'English_walnut'}, {'id': 13472, 'synset': 'brazil_nut.n.02', 'name': 'brazil_nut'}, {'id': 13473, 'synset': 'butternut.n.02', 'name': 'butternut'}, {'id': 13474, 'synset': 'souari_nut.n.02', 'name': 'souari_nut'}, {'id': 13475, 'synset': 'cashew.n.02', 'name': 'cashew'}, {'id': 13476, 'synset': 'chestnut.n.03', 'name': 'chestnut'}, {'id': 13477, 'synset': 'chincapin.n.01', 'name': 'chincapin'}, {'id': 13478, 'synset': 'hazelnut.n.02', 'name': 'hazelnut'}, {'id': 13479, 'synset': 'coconut_milk.n.02', 'name': 'coconut_milk'}, {'id': 13480, 'synset': 'grugru_nut.n.01', 'name': 'grugru_nut'}, {'id': 13481, 'synset': 'hickory_nut.n.01', 'name': 'hickory_nut'}, {'id': 13482, 'synset': 'cola_extract.n.01', 'name': 'cola_extract'}, {'id': 13483, 'synset': 'macadamia_nut.n.02', 'name': 'macadamia_nut'}, {'id': 13484, 'synset': 'pecan.n.03', 'name': 'pecan'}, {'id': 13485, 'synset': 'pine_nut.n.01', 'name': 'pine_nut'}, {'id': 13486, 'synset': 'pistachio.n.02', 'name': 'pistachio'}, {'id': 13487, 'synset': 'sunflower_seed.n.01', 'name': 'sunflower_seed'}, {'id': 13488, 'synset': 'anchovy_paste.n.01', 'name': 'anchovy_paste'}, {'id': 13489, 'synset': 'rollmops.n.01', 'name': 'rollmops'}, {'id': 13490, 'synset': 'feed.n.01', 'name': 'feed'}, {'id': 13491, 'synset': 'cattle_cake.n.01', 'name': 'cattle_cake'}, {'id': 13492, 'synset': 'creep_feed.n.01', 'name': 'creep_feed'}, {'id': 13493, 'synset': 'fodder.n.02', 'name': 'fodder'}, {'id': 13494, 'synset': 'feed_grain.n.01', 'name': 'feed_grain'}, {'id': 13495, 'synset': 'eatage.n.01', 'name': 'eatage'}, {'id': 13496, 'synset': 'silage.n.01', 'name': 'silage'}, {'id': 13497, 'synset': 'oil_cake.n.01', 'name': 'oil_cake'}, {'id': 13498, 'synset': 'oil_meal.n.01', 'name': 'oil_meal'}, {'id': 13499, 'synset': 'alfalfa.n.02', 'name': 'alfalfa'}, {'id': 13500, 'synset': 'broad_bean.n.03', 'name': 'broad_bean'}, {'id': 13501, 'synset': 'hay.n.01', 'name': 'hay'}, {'id': 13502, 'synset': 'timothy.n.03', 'name': 'timothy'}, {'id': 13503, 'synset': 'stover.n.01', 'name': 'stover'}, {'id': 13504, 'synset': 'grain.n.02', 'name': 'grain'}, {'id': 13505, 'synset': 'grist.n.01', 'name': 'grist'}, {'id': 13506, 'synset': 'groats.n.01', 'name': 'groats'}, {'id': 13507, 'synset': 'millet.n.03', 'name': 'millet'}, {'id': 13508, 'synset': 'barley.n.01', 'name': 'barley'}, {'id': 13509, 'synset': 'pearl_barley.n.01', 'name': 'pearl_barley'}, {'id': 13510, 'synset': 'buckwheat.n.02', 'name': 'buckwheat'}, {'id': 13511, 'synset': 'bulgur.n.01', 'name': 'bulgur'}, {'id': 13512, 'synset': 'wheat.n.02', 'name': 'wheat'}, {'id': 13513, 'synset': 'cracked_wheat.n.01', 'name': 'cracked_wheat'}, {'id': 13514, 'synset': 'stodge.n.01', 'name': 'stodge'}, {'id': 13515, 'synset': 'wheat_germ.n.01', 'name': 'wheat_germ'}, {'id': 13516, 'synset': 'oat.n.02', 'name': 'oat'}, {'id': 13517, 'synset': 'rice.n.01', 'name': 'rice'}, {'id': 13518, 'synset': 'brown_rice.n.01', 'name': 'brown_rice'}, {'id': 13519, 'synset': 'white_rice.n.01', 'name': 'white_rice'}, {'id': 13520, 'synset': 'wild_rice.n.02', 'name': 'wild_rice'}, {'id': 13521, 'synset': 'paddy.n.03', 'name': 'paddy'}, {'id': 13522, 'synset': 'slop.n.01', 'name': 'slop'}, {'id': 13523, 'synset': 'mash.n.02', 'name': 'mash'}, {'id': 13524, 'synset': 'chicken_feed.n.01', 'name': 'chicken_feed'}, {'id': 13525, 'synset': 'cud.n.01', 'name': 'cud'}, {'id': 13526, 'synset': 'bird_feed.n.01', 'name': 'bird_feed'}, {'id': 13527, 'synset': 'petfood.n.01', 'name': 'petfood'}, {'id': 13528, 'synset': 'dog_food.n.01', 'name': 'dog_food'}, {'id': 13529, 'synset': 'cat_food.n.01', 'name': 'cat_food'}, {'id': 13530, 'synset': 'canary_seed.n.01', 'name': 'canary_seed'}, {'id': 13531, 'synset': 'tossed_salad.n.01', 'name': 'tossed_salad'}, {'id': 13532, 'synset': 'green_salad.n.01', 'name': 'green_salad'}, {'id': 13533, 'synset': 'caesar_salad.n.01', 'name': 'Caesar_salad'}, {'id': 13534, 'synset': 'salmagundi.n.02', 'name': 'salmagundi'}, {'id': 13535, 'synset': 'salad_nicoise.n.01', 'name': 'salad_nicoise'}, {'id': 13536, 'synset': 'combination_salad.n.01', 'name': 'combination_salad'}, {'id': 13537, 'synset': "chef's_salad.n.01", 'name': "chef's_salad"}, {'id': 13538, 'synset': 'potato_salad.n.01', 'name': 'potato_salad'}, {'id': 13539, 'synset': 'pasta_salad.n.01', 'name': 'pasta_salad'}, {'id': 13540, 'synset': 'macaroni_salad.n.01', 'name': 'macaroni_salad'}, {'id': 13541, 'synset': 'fruit_salad.n.01', 'name': 'fruit_salad'}, {'id': 13542, 'synset': 'waldorf_salad.n.01', 'name': 'Waldorf_salad'}, {'id': 13543, 'synset': 'crab_louis.n.01', 'name': 'crab_Louis'}, {'id': 13544, 'synset': 'herring_salad.n.01', 'name': 'herring_salad'}, {'id': 13545, 'synset': 'tuna_fish_salad.n.01', 'name': 'tuna_fish_salad'}, {'id': 13546, 'synset': 'chicken_salad.n.01', 'name': 'chicken_salad'}, {'id': 13547, 'synset': 'aspic.n.01', 'name': 'aspic'}, {'id': 13548, 'synset': 'molded_salad.n.01', 'name': 'molded_salad'}, {'id': 13549, 'synset': 'tabbouleh.n.01', 'name': 'tabbouleh'}, {'id': 13550, 'synset': 'ingredient.n.03', 'name': 'ingredient'}, {'id': 13551, 'synset': 'flavorer.n.01', 'name': 'flavorer'}, {'id': 13552, 'synset': 'bouillon_cube.n.01', 'name': 'bouillon_cube'}, {'id': 13553, 'synset': 'herb.n.02', 'name': 'herb'}, {'id': 13554, 'synset': 'fines_herbes.n.01', 'name': 'fines_herbes'}, {'id': 13555, 'synset': 'spice.n.02', 'name': 'spice'}, {'id': 13556, 'synset': 'spearmint_oil.n.01', 'name': 'spearmint_oil'}, {'id': 13557, 'synset': 'lemon_oil.n.01', 'name': 'lemon_oil'}, {'id': 13558, 'synset': 'wintergreen_oil.n.01', 'name': 'wintergreen_oil'}, {'id': 13559, 'synset': 'salt.n.02', 'name': 'salt'}, {'id': 13560, 'synset': 'celery_salt.n.01', 'name': 'celery_salt'}, {'id': 13561, 'synset': 'onion_salt.n.01', 'name': 'onion_salt'}, {'id': 13562, 'synset': 'seasoned_salt.n.01', 'name': 'seasoned_salt'}, {'id': 13563, 'synset': 'sour_salt.n.01', 'name': 'sour_salt'}, {'id': 13564, 'synset': 'five_spice_powder.n.01', 'name': 'five_spice_powder'}, {'id': 13565, 'synset': 'allspice.n.03', 'name': 'allspice'}, {'id': 13566, 'synset': 'cinnamon.n.03', 'name': 'cinnamon'}, {'id': 13567, 'synset': 'stick_cinnamon.n.01', 'name': 'stick_cinnamon'}, {'id': 13568, 'synset': 'clove.n.04', 'name': 'clove'}, {'id': 13569, 'synset': 'cumin.n.02', 'name': 'cumin'}, {'id': 13570, 'synset': 'fennel.n.04', 'name': 'fennel'}, {'id': 13571, 'synset': 'ginger.n.02', 'name': 'ginger'}, {'id': 13572, 'synset': 'mace.n.03', 'name': 'mace'}, {'id': 13573, 'synset': 'nutmeg.n.02', 'name': 'nutmeg'}, {'id': 13574, 'synset': 'black_pepper.n.02', 'name': 'black_pepper'}, {'id': 13575, 'synset': 'white_pepper.n.02', 'name': 'white_pepper'}, {'id': 13576, 'synset': 'sassafras.n.02', 'name': 'sassafras'}, {'id': 13577, 'synset': 'basil.n.03', 'name': 'basil'}, {'id': 13578, 'synset': 'bay_leaf.n.01', 'name': 'bay_leaf'}, {'id': 13579, 'synset': 'borage.n.02', 'name': 'borage'}, {'id': 13580, 'synset': 'hyssop.n.02', 'name': 'hyssop'}, {'id': 13581, 'synset': 'caraway.n.02', 'name': 'caraway'}, {'id': 13582, 'synset': 'chervil.n.02', 'name': 'chervil'}, {'id': 13583, 'synset': 'chives.n.02', 'name': 'chives'}, {'id': 13584, 'synset': 'comfrey.n.02', 'name': 'comfrey'}, {'id': 13585, 'synset': 'coriander.n.03', 'name': 'coriander'}, {'id': 13586, 'synset': 'coriander.n.02', 'name': 'coriander'}, {'id': 13587, 'synset': 'costmary.n.02', 'name': 'costmary'}, {'id': 13588, 'synset': 'fennel.n.03', 'name': 'fennel'}, {'id': 13589, 'synset': 'fennel.n.02', 'name': 'fennel'}, {'id': 13590, 'synset': 'fennel_seed.n.01', 'name': 'fennel_seed'}, {'id': 13591, 'synset': 'fenugreek.n.02', 'name': 'fenugreek'}, {'id': 13592, 'synset': 'clove.n.03', 'name': 'clove'}, {'id': 13593, 'synset': 'garlic_chive.n.02', 'name': 'garlic_chive'}, {'id': 13594, 'synset': 'lemon_balm.n.02', 'name': 'lemon_balm'}, {'id': 13595, 'synset': 'lovage.n.02', 'name': 'lovage'}, {'id': 13596, 'synset': 'marjoram.n.02', 'name': 'marjoram'}, {'id': 13597, 'synset': 'mint.n.04', 'name': 'mint'}, {'id': 13598, 'synset': 'mustard_seed.n.01', 'name': 'mustard_seed'}, {'id': 13599, 'synset': 'mustard.n.02', 'name': 'mustard'}, {'id': 13600, 'synset': 'chinese_mustard.n.02', 'name': 'Chinese_mustard'}, {'id': 13601, 'synset': 'nasturtium.n.03', 'name': 'nasturtium'}, {'id': 13602, 'synset': 'parsley.n.02', 'name': 'parsley'}, {'id': 13603, 'synset': 'salad_burnet.n.02', 'name': 'salad_burnet'}, {'id': 13604, 'synset': 'rosemary.n.02', 'name': 'rosemary'}, {'id': 13605, 'synset': 'rue.n.02', 'name': 'rue'}, {'id': 13606, 'synset': 'sage.n.02', 'name': 'sage'}, {'id': 13607, 'synset': 'clary_sage.n.02', 'name': 'clary_sage'}, {'id': 13608, 'synset': 'savory.n.03', 'name': 'savory'}, {'id': 13609, 'synset': 'summer_savory.n.02', 'name': 'summer_savory'}, {'id': 13610, 'synset': 'winter_savory.n.02', 'name': 'winter_savory'}, {'id': 13611, 'synset': 'sweet_woodruff.n.02', 'name': 'sweet_woodruff'}, {'id': 13612, 'synset': 'sweet_cicely.n.03', 'name': 'sweet_cicely'}, {'id': 13613, 'synset': 'tarragon.n.02', 'name': 'tarragon'}, {'id': 13614, 'synset': 'thyme.n.02', 'name': 'thyme'}, {'id': 13615, 'synset': 'turmeric.n.02', 'name': 'turmeric'}, {'id': 13616, 'synset': 'caper.n.02', 'name': 'caper'}, {'id': 13617, 'synset': 'catsup.n.01', 'name': 'catsup'}, {'id': 13618, 'synset': 'cardamom.n.02', 'name': 'cardamom'}, {'id': 13619, 'synset': 'chili_powder.n.01', 'name': 'chili_powder'}, {'id': 13620, 'synset': 'chili_sauce.n.01', 'name': 'chili_sauce'}, {'id': 13621, 'synset': 'chutney.n.01', 'name': 'chutney'}, {'id': 13622, 'synset': 'steak_sauce.n.01', 'name': 'steak_sauce'}, {'id': 13623, 'synset': 'taco_sauce.n.01', 'name': 'taco_sauce'}, {'id': 13624, 'synset': 'mint_sauce.n.01', 'name': 'mint_sauce'}, {'id': 13625, 'synset': 'cranberry_sauce.n.01', 'name': 'cranberry_sauce'}, {'id': 13626, 'synset': 'curry_powder.n.01', 'name': 'curry_powder'}, {'id': 13627, 'synset': 'curry.n.01', 'name': 'curry'}, {'id': 13628, 'synset': 'lamb_curry.n.01', 'name': 'lamb_curry'}, {'id': 13629, 'synset': 'duck_sauce.n.01', 'name': 'duck_sauce'}, {'id': 13630, 'synset': 'horseradish.n.03', 'name': 'horseradish'}, {'id': 13631, 'synset': 'marinade.n.01', 'name': 'marinade'}, {'id': 13632, 'synset': 'paprika.n.02', 'name': 'paprika'}, {'id': 13633, 'synset': 'spanish_paprika.n.01', 'name': 'Spanish_paprika'}, {'id': 13634, 'synset': 'dill_pickle.n.01', 'name': 'dill_pickle'}, {'id': 13635, 'synset': 'bread_and_butter_pickle.n.01', 'name': 'bread_and_butter_pickle'}, {'id': 13636, 'synset': 'pickle_relish.n.01', 'name': 'pickle_relish'}, {'id': 13637, 'synset': 'piccalilli.n.01', 'name': 'piccalilli'}, {'id': 13638, 'synset': 'sweet_pickle.n.01', 'name': 'sweet_pickle'}, {'id': 13639, 'synset': 'soy_sauce.n.01', 'name': 'soy_sauce'}, {'id': 13640, 'synset': 'tomato_paste.n.01', 'name': 'tomato_paste'}, {'id': 13641, 'synset': 'angelica.n.03', 'name': 'angelica'}, {'id': 13642, 'synset': 'angelica.n.02', 'name': 'angelica'}, {'id': 13643, 'synset': 'almond_extract.n.01', 'name': 'almond_extract'}, {'id': 13644, 'synset': 'anise.n.02', 'name': 'anise'}, {'id': 13645, 'synset': 'chinese_anise.n.02', 'name': 'Chinese_anise'}, {'id': 13646, 'synset': 'juniper_berries.n.01', 'name': 'juniper_berries'}, {'id': 13647, 'synset': 'saffron.n.02', 'name': 'saffron'}, {'id': 13648, 'synset': 'sesame_seed.n.01', 'name': 'sesame_seed'}, {'id': 13649, 'synset': 'caraway_seed.n.01', 'name': 'caraway_seed'}, {'id': 13650, 'synset': 'poppy_seed.n.01', 'name': 'poppy_seed'}, {'id': 13651, 'synset': 'dill.n.02', 'name': 'dill'}, {'id': 13652, 'synset': 'dill_seed.n.01', 'name': 'dill_seed'}, {'id': 13653, 'synset': 'celery_seed.n.01', 'name': 'celery_seed'}, {'id': 13654, 'synset': 'lemon_extract.n.01', 'name': 'lemon_extract'}, {'id': 13655, 'synset': 'monosodium_glutamate.n.01', 'name': 'monosodium_glutamate'}, {'id': 13656, 'synset': 'vanilla_bean.n.01', 'name': 'vanilla_bean'}, {'id': 13657, 'synset': 'cider_vinegar.n.01', 'name': 'cider_vinegar'}, {'id': 13658, 'synset': 'wine_vinegar.n.01', 'name': 'wine_vinegar'}, {'id': 13659, 'synset': 'sauce.n.01', 'name': 'sauce'}, {'id': 13660, 'synset': 'anchovy_sauce.n.01', 'name': 'anchovy_sauce'}, {'id': 13661, 'synset': 'hard_sauce.n.01', 'name': 'hard_sauce'}, {'id': 13662, 'synset': 'horseradish_sauce.n.01', 'name': 'horseradish_sauce'}, {'id': 13663, 'synset': 'bolognese_pasta_sauce.n.01', 'name': 'bolognese_pasta_sauce'}, {'id': 13664, 'synset': 'carbonara.n.01', 'name': 'carbonara'}, {'id': 13665, 'synset': 'tomato_sauce.n.01', 'name': 'tomato_sauce'}, {'id': 13666, 'synset': 'tartare_sauce.n.01', 'name': 'tartare_sauce'}, {'id': 13667, 'synset': 'wine_sauce.n.01', 'name': 'wine_sauce'}, {'id': 13668, 'synset': 'marchand_de_vin.n.01', 'name': 'marchand_de_vin'}, {'id': 13669, 'synset': 'bread_sauce.n.01', 'name': 'bread_sauce'}, {'id': 13670, 'synset': 'plum_sauce.n.01', 'name': 'plum_sauce'}, {'id': 13671, 'synset': 'peach_sauce.n.01', 'name': 'peach_sauce'}, {'id': 13672, 'synset': 'apricot_sauce.n.01', 'name': 'apricot_sauce'}, {'id': 13673, 'synset': 'pesto.n.01', 'name': 'pesto'}, {'id': 13674, 'synset': 'ravigote.n.01', 'name': 'ravigote'}, {'id': 13675, 'synset': 'remoulade_sauce.n.01', 'name': 'remoulade_sauce'}, {'id': 13676, 'synset': 'dressing.n.01', 'name': 'dressing'}, {'id': 13677, 'synset': 'sauce_louis.n.01', 'name': 'sauce_Louis'}, {'id': 13678, 'synset': 'bleu_cheese_dressing.n.01', 'name': 'bleu_cheese_dressing'}, {'id': 13679, 'synset': 'blue_cheese_dressing.n.01', 'name': 'blue_cheese_dressing'}, {'id': 13680, 'synset': 'french_dressing.n.01', 'name': 'French_dressing'}, {'id': 13681, 'synset': 'lorenzo_dressing.n.01', 'name': 'Lorenzo_dressing'}, {'id': 13682, 'synset': 'anchovy_dressing.n.01', 'name': 'anchovy_dressing'}, {'id': 13683, 'synset': 'italian_dressing.n.01', 'name': 'Italian_dressing'}, {'id': 13684, 'synset': 'half-and-half_dressing.n.01', 'name': 'half-and-half_dressing'}, {'id': 13685, 'synset': 'mayonnaise.n.01', 'name': 'mayonnaise'}, {'id': 13686, 'synset': 'green_mayonnaise.n.01', 'name': 'green_mayonnaise'}, {'id': 13687, 'synset': 'aioli.n.01', 'name': 'aioli'}, {'id': 13688, 'synset': 'russian_dressing.n.01', 'name': 'Russian_dressing'}, {'id': 13689, 'synset': 'salad_cream.n.01', 'name': 'salad_cream'}, {'id': 13690, 'synset': 'thousand_island_dressing.n.01', 'name': 'Thousand_Island_dressing'}, {'id': 13691, 'synset': 'barbecue_sauce.n.01', 'name': 'barbecue_sauce'}, {'id': 13692, 'synset': 'hollandaise.n.01', 'name': 'hollandaise'}, {'id': 13693, 'synset': 'bearnaise.n.01', 'name': 'bearnaise'}, {'id': 13694, 'synset': 'bercy.n.01', 'name': 'Bercy'}, {'id': 13695, 'synset': 'bordelaise.n.01', 'name': 'bordelaise'}, {'id': 13696, 'synset': 'bourguignon.n.01', 'name': 'bourguignon'}, {'id': 13697, 'synset': 'brown_sauce.n.02', 'name': 'brown_sauce'}, {'id': 13698, 'synset': 'espagnole.n.01', 'name': 'Espagnole'}, {'id': 13699, 'synset': 'chinese_brown_sauce.n.01', 'name': 'Chinese_brown_sauce'}, {'id': 13700, 'synset': 'blanc.n.01', 'name': 'blanc'}, {'id': 13701, 'synset': 'cheese_sauce.n.01', 'name': 'cheese_sauce'}, {'id': 13702, 'synset': 'chocolate_sauce.n.01', 'name': 'chocolate_sauce'}, {'id': 13703, 'synset': 'hot-fudge_sauce.n.01', 'name': 'hot-fudge_sauce'}, {'id': 13704, 'synset': 'cocktail_sauce.n.01', 'name': 'cocktail_sauce'}, {'id': 13705, 'synset': 'colbert.n.01', 'name': 'Colbert'}, {'id': 13706, 'synset': 'white_sauce.n.01', 'name': 'white_sauce'}, {'id': 13707, 'synset': 'cream_sauce.n.01', 'name': 'cream_sauce'}, {'id': 13708, 'synset': 'mornay_sauce.n.01', 'name': 'Mornay_sauce'}, {'id': 13709, 'synset': 'demiglace.n.01', 'name': 'demiglace'}, {'id': 13710, 'synset': 'gravy.n.02', 'name': 'gravy'}, {'id': 13711, 'synset': 'gravy.n.01', 'name': 'gravy'}, {'id': 13712, 'synset': 'spaghetti_sauce.n.01', 'name': 'spaghetti_sauce'}, {'id': 13713, 'synset': 'marinara.n.01', 'name': 'marinara'}, {'id': 13714, 'synset': 'mole.n.03', 'name': 'mole'}, {'id': 13715, 'synset': "hunter's_sauce.n.01", 'name': "hunter's_sauce"}, {'id': 13716, 'synset': 'mushroom_sauce.n.01', 'name': 'mushroom_sauce'}, {'id': 13717, 'synset': 'mustard_sauce.n.01', 'name': 'mustard_sauce'}, {'id': 13718, 'synset': 'nantua.n.01', 'name': 'Nantua'}, {'id': 13719, 'synset': 'hungarian_sauce.n.01', 'name': 'Hungarian_sauce'}, {'id': 13720, 'synset': 'pepper_sauce.n.01', 'name': 'pepper_sauce'}, {'id': 13721, 'synset': 'roux.n.01', 'name': 'roux'}, {'id': 13722, 'synset': 'smitane.n.01', 'name': 'Smitane'}, {'id': 13723, 'synset': 'soubise.n.01', 'name': 'Soubise'}, {'id': 13724, 'synset': 'lyonnaise_sauce.n.01', 'name': 'Lyonnaise_sauce'}, {'id': 13725, 'synset': 'veloute.n.01', 'name': 'veloute'}, {'id': 13726, 'synset': 'allemande.n.01', 'name': 'allemande'}, {'id': 13727, 'synset': 'caper_sauce.n.01', 'name': 'caper_sauce'}, {'id': 13728, 'synset': 'poulette.n.01', 'name': 'poulette'}, {'id': 13729, 'synset': 'curry_sauce.n.01', 'name': 'curry_sauce'}, {'id': 13730, 'synset': 'worcester_sauce.n.01', 'name': 'Worcester_sauce'}, {'id': 13731, 'synset': 'coconut_milk.n.01', 'name': 'coconut_milk'}, {'id': 13732, 'synset': 'egg_white.n.01', 'name': 'egg_white'}, {'id': 13733, 'synset': 'hard-boiled_egg.n.01', 'name': 'hard-boiled_egg'}, {'id': 13734, 'synset': 'easter_egg.n.02', 'name': 'Easter_egg'}, {'id': 13735, 'synset': 'easter_egg.n.01', 'name': 'Easter_egg'}, {'id': 13736, 'synset': 'chocolate_egg.n.01', 'name': 'chocolate_egg'}, {'id': 13737, 'synset': 'candy_egg.n.01', 'name': 'candy_egg'}, {'id': 13738, 'synset': 'poached_egg.n.01', 'name': 'poached_egg'}, {'id': 13739, 'synset': 'scrambled_eggs.n.01', 'name': 'scrambled_eggs'}, {'id': 13740, 'synset': 'deviled_egg.n.01', 'name': 'deviled_egg'}, {'id': 13741, 'synset': 'shirred_egg.n.01', 'name': 'shirred_egg'}, {'id': 13742, 'synset': 'firm_omelet.n.01', 'name': 'firm_omelet'}, {'id': 13743, 'synset': 'french_omelet.n.01', 'name': 'French_omelet'}, {'id': 13744, 'synset': 'fluffy_omelet.n.01', 'name': 'fluffy_omelet'}, {'id': 13745, 'synset': 'western_omelet.n.01', 'name': 'western_omelet'}, {'id': 13746, 'synset': 'souffle.n.01', 'name': 'souffle'}, {'id': 13747, 'synset': 'fried_egg.n.01', 'name': 'fried_egg'}, {'id': 13748, 'synset': 'dairy_product.n.01', 'name': 'dairy_product'}, {'id': 13749, 'synset': 'milk.n.04', 'name': 'milk'}, {'id': 13750, 'synset': 'sour_milk.n.01', 'name': 'sour_milk'}, {'id': 13751, 'synset': 'formula.n.06', 'name': 'formula'}, {'id': 13752, 'synset': 'pasteurized_milk.n.01', 'name': 'pasteurized_milk'}, {'id': 13753, 'synset': "cows'_milk.n.01", 'name': "cows'_milk"}, {'id': 13754, 'synset': "yak's_milk.n.01", 'name': "yak's_milk"}, {'id': 13755, 'synset': "goats'_milk.n.01", 'name': "goats'_milk"}, {'id': 13756, 'synset': 'acidophilus_milk.n.01', 'name': 'acidophilus_milk'}, {'id': 13757, 'synset': 'raw_milk.n.01', 'name': 'raw_milk'}, {'id': 13758, 'synset': 'scalded_milk.n.01', 'name': 'scalded_milk'}, {'id': 13759, 'synset': 'homogenized_milk.n.01', 'name': 'homogenized_milk'}, {'id': 13760, 'synset': 'certified_milk.n.01', 'name': 'certified_milk'}, {'id': 13761, 'synset': 'powdered_milk.n.01', 'name': 'powdered_milk'}, {'id': 13762, 'synset': 'nonfat_dry_milk.n.01', 'name': 'nonfat_dry_milk'}, {'id': 13763, 'synset': 'evaporated_milk.n.01', 'name': 'evaporated_milk'}, {'id': 13764, 'synset': 'condensed_milk.n.01', 'name': 'condensed_milk'}, {'id': 13765, 'synset': 'skim_milk.n.01', 'name': 'skim_milk'}, {'id': 13766, 'synset': 'semi-skimmed_milk.n.01', 'name': 'semi-skimmed_milk'}, {'id': 13767, 'synset': 'whole_milk.n.01', 'name': 'whole_milk'}, {'id': 13768, 'synset': 'low-fat_milk.n.01', 'name': 'low-fat_milk'}, {'id': 13769, 'synset': 'buttermilk.n.01', 'name': 'buttermilk'}, {'id': 13770, 'synset': 'cream.n.02', 'name': 'cream'}, {'id': 13771, 'synset': 'clotted_cream.n.01', 'name': 'clotted_cream'}, {'id': 13772, 'synset': 'double_creme.n.01', 'name': 'double_creme'}, {'id': 13773, 'synset': 'half-and-half.n.01', 'name': 'half-and-half'}, {'id': 13774, 'synset': 'heavy_cream.n.01', 'name': 'heavy_cream'}, {'id': 13775, 'synset': 'light_cream.n.01', 'name': 'light_cream'}, {'id': 13776, 'synset': 'whipping_cream.n.01', 'name': 'whipping_cream'}, {'id': 13777, 'synset': 'clarified_butter.n.01', 'name': 'clarified_butter'}, {'id': 13778, 'synset': 'ghee.n.01', 'name': 'ghee'}, {'id': 13779, 'synset': 'brown_butter.n.01', 'name': 'brown_butter'}, {'id': 13780, 'synset': 'meuniere_butter.n.01', 'name': 'Meuniere_butter'}, {'id': 13781, 'synset': 'blueberry_yogurt.n.01', 'name': 'blueberry_yogurt'}, {'id': 13782, 'synset': 'raita.n.01', 'name': 'raita'}, {'id': 13783, 'synset': 'whey.n.02', 'name': 'whey'}, {'id': 13784, 'synset': 'curd.n.02', 'name': 'curd'}, {'id': 13785, 'synset': 'curd.n.01', 'name': 'curd'}, {'id': 13786, 'synset': 'clabber.n.01', 'name': 'clabber'}, {'id': 13787, 'synset': 'cheese.n.01', 'name': 'cheese'}, {'id': 13788, 'synset': 'paring.n.02', 'name': 'paring'}, {'id': 13789, 'synset': 'cream_cheese.n.01', 'name': 'cream_cheese'}, {'id': 13790, 'synset': 'double_cream.n.01', 'name': 'double_cream'}, {'id': 13791, 'synset': 'mascarpone.n.01', 'name': 'mascarpone'}, {'id': 13792, 'synset': 'triple_cream.n.01', 'name': 'triple_cream'}, {'id': 13793, 'synset': 'cottage_cheese.n.01', 'name': 'cottage_cheese'}, {'id': 13794, 'synset': 'process_cheese.n.01', 'name': 'process_cheese'}, {'id': 13795, 'synset': 'bleu.n.01', 'name': 'bleu'}, {'id': 13796, 'synset': 'stilton.n.01', 'name': 'Stilton'}, {'id': 13797, 'synset': 'roquefort.n.01', 'name': 'Roquefort'}, {'id': 13798, 'synset': 'gorgonzola.n.01', 'name': 'gorgonzola'}, {'id': 13799, 'synset': 'danish_blue.n.01', 'name': 'Danish_blue'}, {'id': 13800, 'synset': 'bavarian_blue.n.01', 'name': 'Bavarian_blue'}, {'id': 13801, 'synset': 'brie.n.01', 'name': 'Brie'}, {'id': 13802, 'synset': 'brick_cheese.n.01', 'name': 'brick_cheese'}, {'id': 13803, 'synset': 'camembert.n.01', 'name': 'Camembert'}, {'id': 13804, 'synset': 'cheddar.n.02', 'name': 'cheddar'}, {'id': 13805, 'synset': 'rat_cheese.n.01', 'name': 'rat_cheese'}, {'id': 13806, 'synset': 'cheshire_cheese.n.01', 'name': 'Cheshire_cheese'}, {'id': 13807, 'synset': 'double_gloucester.n.01', 'name': 'double_Gloucester'}, {'id': 13808, 'synset': 'edam.n.01', 'name': 'Edam'}, {'id': 13809, 'synset': 'goat_cheese.n.01', 'name': 'goat_cheese'}, {'id': 13810, 'synset': 'gouda.n.01', 'name': 'Gouda'}, {'id': 13811, 'synset': 'grated_cheese.n.01', 'name': 'grated_cheese'}, {'id': 13812, 'synset': 'hand_cheese.n.01', 'name': 'hand_cheese'}, {'id': 13813, 'synset': 'liederkranz.n.01', 'name': 'Liederkranz'}, {'id': 13814, 'synset': 'limburger.n.01', 'name': 'Limburger'}, {'id': 13815, 'synset': 'mozzarella.n.01', 'name': 'mozzarella'}, {'id': 13816, 'synset': 'muenster.n.01', 'name': 'Muenster'}, {'id': 13817, 'synset': 'parmesan.n.01', 'name': 'Parmesan'}, {'id': 13818, 'synset': 'quark_cheese.n.01', 'name': 'quark_cheese'}, {'id': 13819, 'synset': 'ricotta.n.01', 'name': 'ricotta'}, {'id': 13820, 'synset': 'swiss_cheese.n.01', 'name': 'Swiss_cheese'}, {'id': 13821, 'synset': 'emmenthal.n.01', 'name': 'Emmenthal'}, {'id': 13822, 'synset': 'gruyere.n.01', 'name': 'Gruyere'}, {'id': 13823, 'synset': 'sapsago.n.01', 'name': 'sapsago'}, {'id': 13824, 'synset': 'velveeta.n.01', 'name': 'Velveeta'}, {'id': 13825, 'synset': 'nut_butter.n.01', 'name': 'nut_butter'}, {'id': 13826, 'synset': 'marshmallow_fluff.n.01', 'name': 'marshmallow_fluff'}, {'id': 13827, 'synset': 'onion_butter.n.01', 'name': 'onion_butter'}, {'id': 13828, 'synset': 'pimento_butter.n.01', 'name': 'pimento_butter'}, {'id': 13829, 'synset': 'shrimp_butter.n.01', 'name': 'shrimp_butter'}, {'id': 13830, 'synset': 'lobster_butter.n.01', 'name': 'lobster_butter'}, {'id': 13831, 'synset': 'yak_butter.n.01', 'name': 'yak_butter'}, {'id': 13832, 'synset': 'spread.n.05', 'name': 'spread'}, {'id': 13833, 'synset': 'cheese_spread.n.01', 'name': 'cheese_spread'}, {'id': 13834, 'synset': 'anchovy_butter.n.01', 'name': 'anchovy_butter'}, {'id': 13835, 'synset': 'fishpaste.n.01', 'name': 'fishpaste'}, {'id': 13836, 'synset': 'garlic_butter.n.01', 'name': 'garlic_butter'}, {'id': 13837, 'synset': 'miso.n.01', 'name': 'miso'}, {'id': 13838, 'synset': 'wasabi.n.02', 'name': 'wasabi'}, {'id': 13839, 'synset': 'snail_butter.n.01', 'name': 'snail_butter'}, {'id': 13840, 'synset': 'pate.n.01', 'name': 'pate'}, {'id': 13841, 'synset': 'duck_pate.n.01', 'name': 'duck_pate'}, {'id': 13842, 'synset': 'foie_gras.n.01', 'name': 'foie_gras'}, {'id': 13843, 'synset': 'tapenade.n.01', 'name': 'tapenade'}, {'id': 13844, 'synset': 'tahini.n.01', 'name': 'tahini'}, {'id': 13845, 'synset': 'sweetening.n.01', 'name': 'sweetening'}, {'id': 13846, 'synset': 'aspartame.n.01', 'name': 'aspartame'}, {'id': 13847, 'synset': 'saccharin.n.01', 'name': 'saccharin'}, {'id': 13848, 'synset': 'sugar.n.01', 'name': 'sugar'}, {'id': 13849, 'synset': 'syrup.n.01', 'name': 'syrup'}, {'id': 13850, 'synset': 'sugar_syrup.n.01', 'name': 'sugar_syrup'}, {'id': 13851, 'synset': 'molasses.n.01', 'name': 'molasses'}, {'id': 13852, 'synset': 'sorghum.n.03', 'name': 'sorghum'}, {'id': 13853, 'synset': 'treacle.n.01', 'name': 'treacle'}, {'id': 13854, 'synset': 'grenadine.n.01', 'name': 'grenadine'}, {'id': 13855, 'synset': 'maple_syrup.n.01', 'name': 'maple_syrup'}, {'id': 13856, 'synset': 'corn_syrup.n.01', 'name': 'corn_syrup'}, {'id': 13857, 'synset': 'miraculous_food.n.01', 'name': 'miraculous_food'}, {'id': 13858, 'synset': 'dough.n.01', 'name': 'dough'}, {'id': 13859, 'synset': 'bread_dough.n.01', 'name': 'bread_dough'}, {'id': 13860, 'synset': 'pancake_batter.n.01', 'name': 'pancake_batter'}, {'id': 13861, 'synset': 'fritter_batter.n.01', 'name': 'fritter_batter'}, {'id': 13862, 'synset': 'coq_au_vin.n.01', 'name': 'coq_au_vin'}, {'id': 13863, 'synset': 'chicken_provencale.n.01', 'name': 'chicken_provencale'}, {'id': 13864, 'synset': 'chicken_and_rice.n.01', 'name': 'chicken_and_rice'}, {'id': 13865, 'synset': 'moo_goo_gai_pan.n.01', 'name': 'moo_goo_gai_pan'}, {'id': 13866, 'synset': 'arroz_con_pollo.n.01', 'name': 'arroz_con_pollo'}, {'id': 13867, 'synset': 'bacon_and_eggs.n.02', 'name': 'bacon_and_eggs'}, {'id': 13868, 'synset': 'barbecued_spareribs.n.01', 'name': 'barbecued_spareribs'}, {'id': 13869, 'synset': 'beef_bourguignonne.n.01', 'name': 'beef_Bourguignonne'}, {'id': 13870, 'synset': 'beef_wellington.n.01', 'name': 'beef_Wellington'}, {'id': 13871, 'synset': 'bitok.n.01', 'name': 'bitok'}, {'id': 13872, 'synset': 'boiled_dinner.n.01', 'name': 'boiled_dinner'}, {'id': 13873, 'synset': 'boston_baked_beans.n.01', 'name': 'Boston_baked_beans'}, {'id': 13874, 'synset': 'bubble_and_squeak.n.01', 'name': 'bubble_and_squeak'}, {'id': 13875, 'synset': 'pasta.n.01', 'name': 'pasta'}, {'id': 13876, 'synset': 'cannelloni.n.01', 'name': 'cannelloni'}, {'id': 13877, 'synset': 'carbonnade_flamande.n.01', 'name': 'carbonnade_flamande'}, {'id': 13878, 'synset': 'cheese_souffle.n.01', 'name': 'cheese_souffle'}, {'id': 13879, 'synset': 'chicken_marengo.n.01', 'name': 'chicken_Marengo'}, {'id': 13880, 'synset': 'chicken_cordon_bleu.n.01', 'name': 'chicken_cordon_bleu'}, {'id': 13881, 'synset': 'maryland_chicken.n.01', 'name': 'Maryland_chicken'}, {'id': 13882, 'synset': 'chicken_paprika.n.01', 'name': 'chicken_paprika'}, {'id': 13883, 'synset': 'chicken_tetrazzini.n.01', 'name': 'chicken_Tetrazzini'}, {'id': 13884, 'synset': 'tetrazzini.n.01', 'name': 'Tetrazzini'}, {'id': 13885, 'synset': 'chicken_kiev.n.01', 'name': 'chicken_Kiev'}, {'id': 13886, 'synset': 'chili.n.01', 'name': 'chili'}, {'id': 13887, 'synset': 'chili_dog.n.01', 'name': 'chili_dog'}, {'id': 13888, 'synset': 'chop_suey.n.01', 'name': 'chop_suey'}, {'id': 13889, 'synset': 'chow_mein.n.01', 'name': 'chow_mein'}, {'id': 13890, 'synset': 'codfish_ball.n.01', 'name': 'codfish_ball'}, {'id': 13891, 'synset': 'coquille.n.01', 'name': 'coquille'}, {'id': 13892, 'synset': 'coquilles_saint-jacques.n.01', 'name': 'coquilles_Saint-Jacques'}, {'id': 13893, 'synset': 'croquette.n.01', 'name': 'croquette'}, {'id': 13894, 'synset': 'cottage_pie.n.01', 'name': 'cottage_pie'}, {'id': 13895, 'synset': 'rissole.n.01', 'name': 'rissole'}, {'id': 13896, 'synset': 'dolmas.n.01', 'name': 'dolmas'}, {'id': 13897, 'synset': 'egg_foo_yong.n.01', 'name': 'egg_foo_yong'}, {'id': 13898, 'synset': 'eggs_benedict.n.01', 'name': 'eggs_Benedict'}, {'id': 13899, 'synset': 'enchilada.n.01', 'name': 'enchilada'}, {'id': 13900, 'synset': 'falafel.n.01', 'name': 'falafel'}, {'id': 13901, 'synset': 'fish_and_chips.n.01', 'name': 'fish_and_chips'}, {'id': 13902, 'synset': 'fondue.n.02', 'name': 'fondue'}, {'id': 13903, 'synset': 'cheese_fondue.n.01', 'name': 'cheese_fondue'}, {'id': 13904, 'synset': 'chocolate_fondue.n.01', 'name': 'chocolate_fondue'}, {'id': 13905, 'synset': 'fondue.n.01', 'name': 'fondue'}, {'id': 13906, 'synset': 'beef_fondue.n.01', 'name': 'beef_fondue'}, {'id': 13907, 'synset': 'fried_rice.n.01', 'name': 'fried_rice'}, {'id': 13908, 'synset': 'frittata.n.01', 'name': 'frittata'}, {'id': 13909, 'synset': 'frog_legs.n.01', 'name': 'frog_legs'}, {'id': 13910, 'synset': 'galantine.n.01', 'name': 'galantine'}, {'id': 13911, 'synset': 'gefilte_fish.n.01', 'name': 'gefilte_fish'}, {'id': 13912, 'synset': 'haggis.n.01', 'name': 'haggis'}, {'id': 13913, 'synset': 'ham_and_eggs.n.01', 'name': 'ham_and_eggs'}, {'id': 13914, 'synset': 'hash.n.01', 'name': 'hash'}, {'id': 13915, 'synset': 'corned_beef_hash.n.01', 'name': 'corned_beef_hash'}, {'id': 13916, 'synset': 'jambalaya.n.01', 'name': 'jambalaya'}, {'id': 13917, 'synset': 'kabob.n.01', 'name': 'kabob'}, {'id': 13918, 'synset': 'kedgeree.n.01', 'name': 'kedgeree'}, {'id': 13919, 'synset': 'souvlaki.n.01', 'name': 'souvlaki'}, {'id': 13920, 'synset': 'seafood_newburg.n.01', 'name': 'seafood_Newburg'}, {'id': 13921, 'synset': 'lobster_newburg.n.01', 'name': 'lobster_Newburg'}, {'id': 13922, 'synset': 'shrimp_newburg.n.01', 'name': 'shrimp_Newburg'}, {'id': 13923, 'synset': 'newburg_sauce.n.01', 'name': 'Newburg_sauce'}, {'id': 13924, 'synset': 'lobster_thermidor.n.01', 'name': 'lobster_thermidor'}, {'id': 13925, 'synset': 'lutefisk.n.01', 'name': 'lutefisk'}, {'id': 13926, 'synset': 'macaroni_and_cheese.n.01', 'name': 'macaroni_and_cheese'}, {'id': 13927, 'synset': 'macedoine.n.01', 'name': 'macedoine'}, {'id': 13928, 'synset': 'porcupine_ball.n.01', 'name': 'porcupine_ball'}, {'id': 13929, 'synset': 'swedish_meatball.n.01', 'name': 'Swedish_meatball'}, {'id': 13930, 'synset': 'meat_loaf.n.01', 'name': 'meat_loaf'}, {'id': 13931, 'synset': 'moussaka.n.01', 'name': 'moussaka'}, {'id': 13932, 'synset': 'osso_buco.n.01', 'name': 'osso_buco'}, {'id': 13933, 'synset': 'marrow.n.03', 'name': 'marrow'}, {'id': 13934, 'synset': 'pheasant_under_glass.n.01', 'name': 'pheasant_under_glass'}, {'id': 13935, 'synset': 'pigs_in_blankets.n.01', 'name': 'pigs_in_blankets'}, {'id': 13936, 'synset': 'pilaf.n.01', 'name': 'pilaf'}, {'id': 13937, 'synset': 'bulgur_pilaf.n.01', 'name': 'bulgur_pilaf'}, {'id': 13938, 'synset': 'sausage_pizza.n.01', 'name': 'sausage_pizza'}, {'id': 13939, 'synset': 'pepperoni_pizza.n.01', 'name': 'pepperoni_pizza'}, {'id': 13940, 'synset': 'cheese_pizza.n.01', 'name': 'cheese_pizza'}, {'id': 13941, 'synset': 'anchovy_pizza.n.01', 'name': 'anchovy_pizza'}, {'id': 13942, 'synset': 'sicilian_pizza.n.01', 'name': 'Sicilian_pizza'}, {'id': 13943, 'synset': 'poi.n.01', 'name': 'poi'}, {'id': 13944, 'synset': 'pork_and_beans.n.01', 'name': 'pork_and_beans'}, {'id': 13945, 'synset': 'porridge.n.01', 'name': 'porridge'}, {'id': 13946, 'synset': 'oatmeal.n.01', 'name': 'oatmeal'}, {'id': 13947, 'synset': 'loblolly.n.01', 'name': 'loblolly'}, {'id': 13948, 'synset': 'potpie.n.01', 'name': 'potpie'}, {'id': 13949, 'synset': 'rijsttaffel.n.01', 'name': 'rijsttaffel'}, {'id': 13950, 'synset': 'risotto.n.01', 'name': 'risotto'}, {'id': 13951, 'synset': 'roulade.n.01', 'name': 'roulade'}, {'id': 13952, 'synset': 'fish_loaf.n.01', 'name': 'fish_loaf'}, {'id': 13953, 'synset': 'salmon_loaf.n.01', 'name': 'salmon_loaf'}, {'id': 13954, 'synset': 'salisbury_steak.n.01', 'name': 'Salisbury_steak'}, {'id': 13955, 'synset': 'sauerbraten.n.01', 'name': 'sauerbraten'}, {'id': 13956, 'synset': 'sauerkraut.n.01', 'name': 'sauerkraut'}, {'id': 13957, 'synset': 'scallopine.n.01', 'name': 'scallopine'}, {'id': 13958, 'synset': 'veal_scallopini.n.01', 'name': 'veal_scallopini'}, {'id': 13959, 'synset': 'scampi.n.01', 'name': 'scampi'}, {'id': 13960, 'synset': 'scotch_egg.n.01', 'name': 'Scotch_egg'}, {'id': 13961, 'synset': 'scotch_woodcock.n.01', 'name': 'Scotch_woodcock'}, {'id': 13962, 'synset': 'scrapple.n.01', 'name': 'scrapple'}, {'id': 13963, 'synset': 'spaghetti_and_meatballs.n.01', 'name': 'spaghetti_and_meatballs'}, {'id': 13964, 'synset': 'spanish_rice.n.01', 'name': 'Spanish_rice'}, {'id': 13965, 'synset': 'steak_tartare.n.01', 'name': 'steak_tartare'}, {'id': 13966, 'synset': 'pepper_steak.n.02', 'name': 'pepper_steak'}, {'id': 13967, 'synset': 'steak_au_poivre.n.01', 'name': 'steak_au_poivre'}, {'id': 13968, 'synset': 'beef_stroganoff.n.01', 'name': 'beef_Stroganoff'}, {'id': 13969, 'synset': 'stuffed_cabbage.n.01', 'name': 'stuffed_cabbage'}, {'id': 13970, 'synset': 'kishke.n.01', 'name': 'kishke'}, {'id': 13971, 'synset': 'stuffed_peppers.n.01', 'name': 'stuffed_peppers'}, {'id': 13972, 'synset': 'stuffed_tomato.n.02', 'name': 'stuffed_tomato'}, {'id': 13973, 'synset': 'stuffed_tomato.n.01', 'name': 'stuffed_tomato'}, {'id': 13974, 'synset': 'succotash.n.01', 'name': 'succotash'}, {'id': 13975, 'synset': 'sukiyaki.n.01', 'name': 'sukiyaki'}, {'id': 13976, 'synset': 'sashimi.n.01', 'name': 'sashimi'}, {'id': 13977, 'synset': 'swiss_steak.n.01', 'name': 'Swiss_steak'}, {'id': 13978, 'synset': 'tamale.n.02', 'name': 'tamale'}, {'id': 13979, 'synset': 'tamale_pie.n.01', 'name': 'tamale_pie'}, {'id': 13980, 'synset': 'tempura.n.01', 'name': 'tempura'}, {'id': 13981, 'synset': 'teriyaki.n.01', 'name': 'teriyaki'}, {'id': 13982, 'synset': 'terrine.n.01', 'name': 'terrine'}, {'id': 13983, 'synset': 'welsh_rarebit.n.01', 'name': 'Welsh_rarebit'}, {'id': 13984, 'synset': 'schnitzel.n.01', 'name': 'schnitzel'}, {'id': 13985, 'synset': 'chicken_taco.n.01', 'name': 'chicken_taco'}, {'id': 13986, 'synset': 'beef_burrito.n.01', 'name': 'beef_burrito'}, {'id': 13987, 'synset': 'tostada.n.01', 'name': 'tostada'}, {'id': 13988, 'synset': 'bean_tostada.n.01', 'name': 'bean_tostada'}, {'id': 13989, 'synset': 'refried_beans.n.01', 'name': 'refried_beans'}, {'id': 13990, 'synset': 'beverage.n.01', 'name': 'beverage'}, {'id': 13991, 'synset': 'wish-wash.n.01', 'name': 'wish-wash'}, {'id': 13992, 'synset': 'concoction.n.01', 'name': 'concoction'}, {'id': 13993, 'synset': 'mix.n.01', 'name': 'mix'}, {'id': 13994, 'synset': 'filling.n.03', 'name': 'filling'}, {'id': 13995, 'synset': 'lekvar.n.01', 'name': 'lekvar'}, {'id': 13996, 'synset': 'potion.n.01', 'name': 'potion'}, {'id': 13997, 'synset': 'elixir.n.03', 'name': 'elixir'}, {'id': 13998, 'synset': 'elixir_of_life.n.01', 'name': 'elixir_of_life'}, {'id': 13999, 'synset': 'philter.n.01', 'name': 'philter'}, {'id': 14000, 'synset': 'proof_spirit.n.01', 'name': 'proof_spirit'}, {'id': 14001, 'synset': 'home_brew.n.01', 'name': 'home_brew'}, {'id': 14002, 'synset': 'hooch.n.01', 'name': 'hooch'}, {'id': 14003, 'synset': 'kava.n.01', 'name': 'kava'}, {'id': 14004, 'synset': 'aperitif.n.01', 'name': 'aperitif'}, {'id': 14005, 'synset': 'brew.n.01', 'name': 'brew'}, {'id': 14006, 'synset': 'beer.n.01', 'name': 'beer'}, {'id': 14007, 'synset': 'draft_beer.n.01', 'name': 'draft_beer'}, {'id': 14008, 'synset': 'suds.n.02', 'name': 'suds'}, {'id': 14009, 'synset': 'munich_beer.n.01', 'name': 'Munich_beer'}, {'id': 14010, 'synset': 'bock.n.01', 'name': 'bock'}, {'id': 14011, 'synset': 'lager.n.02', 'name': 'lager'}, {'id': 14012, 'synset': 'light_beer.n.01', 'name': 'light_beer'}, {'id': 14013, 'synset': 'oktoberfest.n.01', 'name': 'Oktoberfest'}, {'id': 14014, 'synset': 'pilsner.n.01', 'name': 'Pilsner'}, {'id': 14015, 'synset': 'shebeen.n.01', 'name': 'shebeen'}, {'id': 14016, 'synset': 'weissbier.n.01', 'name': 'Weissbier'}, {'id': 14017, 'synset': 'weizenbock.n.01', 'name': 'Weizenbock'}, {'id': 14018, 'synset': 'malt.n.03', 'name': 'malt'}, {'id': 14019, 'synset': 'wort.n.02', 'name': 'wort'}, {'id': 14020, 'synset': 'malt.n.02', 'name': 'malt'}, {'id': 14021, 'synset': 'ale.n.01', 'name': 'ale'}, {'id': 14022, 'synset': 'bitter.n.01', 'name': 'bitter'}, {'id': 14023, 'synset': 'burton.n.03', 'name': 'Burton'}, {'id': 14024, 'synset': 'pale_ale.n.01', 'name': 'pale_ale'}, {'id': 14025, 'synset': 'porter.n.07', 'name': 'porter'}, {'id': 14026, 'synset': 'stout.n.01', 'name': 'stout'}, {'id': 14027, 'synset': 'guinness.n.02', 'name': 'Guinness'}, {'id': 14028, 'synset': 'kvass.n.01', 'name': 'kvass'}, {'id': 14029, 'synset': 'mead.n.03', 'name': 'mead'}, {'id': 14030, 'synset': 'metheglin.n.01', 'name': 'metheglin'}, {'id': 14031, 'synset': 'hydromel.n.01', 'name': 'hydromel'}, {'id': 14032, 'synset': 'oenomel.n.01', 'name': 'oenomel'}, {'id': 14033, 'synset': 'near_beer.n.01', 'name': 'near_beer'}, {'id': 14034, 'synset': 'ginger_beer.n.01', 'name': 'ginger_beer'}, {'id': 14035, 'synset': 'sake.n.02', 'name': 'sake'}, {'id': 14036, 'synset': 'wine.n.01', 'name': 'wine'}, {'id': 14037, 'synset': 'vintage.n.01', 'name': 'vintage'}, {'id': 14038, 'synset': 'red_wine.n.01', 'name': 'red_wine'}, {'id': 14039, 'synset': 'white_wine.n.01', 'name': 'white_wine'}, {'id': 14040, 'synset': 'blush_wine.n.01', 'name': 'blush_wine'}, {'id': 14041, 'synset': 'altar_wine.n.01', 'name': 'altar_wine'}, {'id': 14042, 'synset': 'sparkling_wine.n.01', 'name': 'sparkling_wine'}, {'id': 14043, 'synset': 'champagne.n.01', 'name': 'champagne'}, {'id': 14044, 'synset': 'cold_duck.n.01', 'name': 'cold_duck'}, {'id': 14045, 'synset': 'burgundy.n.02', 'name': 'Burgundy'}, {'id': 14046, 'synset': 'beaujolais.n.01', 'name': 'Beaujolais'}, {'id': 14047, 'synset': 'medoc.n.01', 'name': 'Medoc'}, {'id': 14048, 'synset': 'canary_wine.n.01', 'name': 'Canary_wine'}, {'id': 14049, 'synset': 'chablis.n.02', 'name': 'Chablis'}, {'id': 14050, 'synset': 'montrachet.n.01', 'name': 'Montrachet'}, {'id': 14051, 'synset': 'chardonnay.n.02', 'name': 'Chardonnay'}, {'id': 14052, 'synset': 'pinot_noir.n.02', 'name': 'Pinot_noir'}, {'id': 14053, 'synset': 'pinot_blanc.n.02', 'name': 'Pinot_blanc'}, {'id': 14054, 'synset': 'bordeaux.n.02', 'name': 'Bordeaux'}, {'id': 14055, 'synset': 'claret.n.02', 'name': 'claret'}, {'id': 14056, 'synset': 'chianti.n.01', 'name': 'Chianti'}, {'id': 14057, 'synset': 'cabernet.n.01', 'name': 'Cabernet'}, {'id': 14058, 'synset': 'merlot.n.02', 'name': 'Merlot'}, {'id': 14059, 'synset': 'sauvignon_blanc.n.02', 'name': 'Sauvignon_blanc'}, {'id': 14060, 'synset': 'california_wine.n.01', 'name': 'California_wine'}, {'id': 14061, 'synset': 'cotes_de_provence.n.01', 'name': 'Cotes_de_Provence'}, {'id': 14062, 'synset': 'dessert_wine.n.01', 'name': 'dessert_wine'}, {'id': 14063, 'synset': 'dubonnet.n.01', 'name': 'Dubonnet'}, {'id': 14064, 'synset': 'jug_wine.n.01', 'name': 'jug_wine'}, {'id': 14065, 'synset': 'macon.n.02', 'name': 'macon'}, {'id': 14066, 'synset': 'moselle.n.01', 'name': 'Moselle'}, {'id': 14067, 'synset': 'muscadet.n.02', 'name': 'Muscadet'}, {'id': 14068, 'synset': 'plonk.n.01', 'name': 'plonk'}, {'id': 14069, 'synset': 'retsina.n.01', 'name': 'retsina'}, {'id': 14070, 'synset': 'rhine_wine.n.01', 'name': 'Rhine_wine'}, {'id': 14071, 'synset': 'riesling.n.02', 'name': 'Riesling'}, {'id': 14072, 'synset': 'liebfraumilch.n.01', 'name': 'liebfraumilch'}, {'id': 14073, 'synset': 'rhone_wine.n.01', 'name': 'Rhone_wine'}, {'id': 14074, 'synset': 'rioja.n.01', 'name': 'Rioja'}, {'id': 14075, 'synset': 'sack.n.04', 'name': 'sack'}, {'id': 14076, 'synset': 'saint_emilion.n.01', 'name': 'Saint_Emilion'}, {'id': 14077, 'synset': 'soave.n.01', 'name': 'Soave'}, {'id': 14078, 'synset': 'zinfandel.n.02', 'name': 'zinfandel'}, {'id': 14079, 'synset': 'sauterne.n.01', 'name': 'Sauterne'}, {'id': 14080, 'synset': 'straw_wine.n.01', 'name': 'straw_wine'}, {'id': 14081, 'synset': 'table_wine.n.01', 'name': 'table_wine'}, {'id': 14082, 'synset': 'tokay.n.01', 'name': 'Tokay'}, {'id': 14083, 'synset': 'vin_ordinaire.n.01', 'name': 'vin_ordinaire'}, {'id': 14084, 'synset': 'vermouth.n.01', 'name': 'vermouth'}, {'id': 14085, 'synset': 'sweet_vermouth.n.01', 'name': 'sweet_vermouth'}, {'id': 14086, 'synset': 'dry_vermouth.n.01', 'name': 'dry_vermouth'}, {'id': 14087, 'synset': 'chenin_blanc.n.02', 'name': 'Chenin_blanc'}, {'id': 14088, 'synset': 'verdicchio.n.02', 'name': 'Verdicchio'}, {'id': 14089, 'synset': 'vouvray.n.01', 'name': 'Vouvray'}, {'id': 14090, 'synset': 'yquem.n.01', 'name': 'Yquem'}, {'id': 14091, 'synset': 'generic.n.01', 'name': 'generic'}, {'id': 14092, 'synset': 'varietal.n.01', 'name': 'varietal'}, {'id': 14093, 'synset': 'fortified_wine.n.01', 'name': 'fortified_wine'}, {'id': 14094, 'synset': 'madeira.n.03', 'name': 'Madeira'}, {'id': 14095, 'synset': 'malmsey.n.01', 'name': 'malmsey'}, {'id': 14096, 'synset': 'port.n.02', 'name': 'port'}, {'id': 14097, 'synset': 'sherry.n.01', 'name': 'sherry'}, {'id': 14098, 'synset': 'marsala.n.01', 'name': 'Marsala'}, {'id': 14099, 'synset': 'muscat.n.03', 'name': 'muscat'}, {'id': 14100, 'synset': 'neutral_spirits.n.01', 'name': 'neutral_spirits'}, {'id': 14101, 'synset': 'aqua_vitae.n.01', 'name': 'aqua_vitae'}, {'id': 14102, 'synset': 'eau_de_vie.n.01', 'name': 'eau_de_vie'}, {'id': 14103, 'synset': 'moonshine.n.02', 'name': 'moonshine'}, {'id': 14104, 'synset': 'bathtub_gin.n.01', 'name': 'bathtub_gin'}, {'id': 14105, 'synset': 'aquavit.n.01', 'name': 'aquavit'}, {'id': 14106, 'synset': 'arrack.n.01', 'name': 'arrack'}, {'id': 14107, 'synset': 'bitters.n.01', 'name': 'bitters'}, {'id': 14108, 'synset': 'brandy.n.01', 'name': 'brandy'}, {'id': 14109, 'synset': 'applejack.n.01', 'name': 'applejack'}, {'id': 14110, 'synset': 'calvados.n.01', 'name': 'Calvados'}, {'id': 14111, 'synset': 'armagnac.n.01', 'name': 'Armagnac'}, {'id': 14112, 'synset': 'cognac.n.01', 'name': 'Cognac'}, {'id': 14113, 'synset': 'grappa.n.01', 'name': 'grappa'}, {'id': 14114, 'synset': 'kirsch.n.01', 'name': 'kirsch'}, {'id': 14115, 'synset': 'slivovitz.n.01', 'name': 'slivovitz'}, {'id': 14116, 'synset': 'gin.n.01', 'name': 'gin'}, {'id': 14117, 'synset': 'sloe_gin.n.01', 'name': 'sloe_gin'}, {'id': 14118, 'synset': 'geneva.n.02', 'name': 'geneva'}, {'id': 14119, 'synset': 'grog.n.01', 'name': 'grog'}, {'id': 14120, 'synset': 'ouzo.n.01', 'name': 'ouzo'}, {'id': 14121, 'synset': 'rum.n.01', 'name': 'rum'}, {'id': 14122, 'synset': 'demerara.n.04', 'name': 'demerara'}, {'id': 14123, 'synset': 'jamaica_rum.n.01', 'name': 'Jamaica_rum'}, {'id': 14124, 'synset': 'schnapps.n.01', 'name': 'schnapps'}, {'id': 14125, 'synset': 'pulque.n.01', 'name': 'pulque'}, {'id': 14126, 'synset': 'mescal.n.02', 'name': 'mescal'}, {'id': 14127, 'synset': 'whiskey.n.01', 'name': 'whiskey'}, {'id': 14128, 'synset': 'blended_whiskey.n.01', 'name': 'blended_whiskey'}, {'id': 14129, 'synset': 'bourbon.n.02', 'name': 'bourbon'}, {'id': 14130, 'synset': 'corn_whiskey.n.01', 'name': 'corn_whiskey'}, {'id': 14131, 'synset': 'firewater.n.01', 'name': 'firewater'}, {'id': 14132, 'synset': 'irish.n.02', 'name': 'Irish'}, {'id': 14133, 'synset': 'poteen.n.01', 'name': 'poteen'}, {'id': 14134, 'synset': 'rye.n.03', 'name': 'rye'}, {'id': 14135, 'synset': 'scotch.n.02', 'name': 'Scotch'}, {'id': 14136, 'synset': 'sour_mash.n.02', 'name': 'sour_mash'}, {'id': 14137, 'synset': 'liqueur.n.01', 'name': 'liqueur'}, {'id': 14138, 'synset': 'absinth.n.01', 'name': 'absinth'}, {'id': 14139, 'synset': 'amaretto.n.01', 'name': 'amaretto'}, {'id': 14140, 'synset': 'anisette.n.01', 'name': 'anisette'}, {'id': 14141, 'synset': 'benedictine.n.02', 'name': 'benedictine'}, {'id': 14142, 'synset': 'chartreuse.n.01', 'name': 'Chartreuse'}, {'id': 14143, 'synset': 'coffee_liqueur.n.01', 'name': 'coffee_liqueur'}, {'id': 14144, 'synset': 'creme_de_cacao.n.01', 'name': 'creme_de_cacao'}, {'id': 14145, 'synset': 'creme_de_menthe.n.01', 'name': 'creme_de_menthe'}, {'id': 14146, 'synset': 'creme_de_fraise.n.01', 'name': 'creme_de_fraise'}, {'id': 14147, 'synset': 'drambuie.n.01', 'name': 'Drambuie'}, {'id': 14148, 'synset': 'galliano.n.01', 'name': 'Galliano'}, {'id': 14149, 'synset': 'orange_liqueur.n.01', 'name': 'orange_liqueur'}, {'id': 14150, 'synset': 'curacao.n.02', 'name': 'curacao'}, {'id': 14151, 'synset': 'triple_sec.n.01', 'name': 'triple_sec'}, {'id': 14152, 'synset': 'grand_marnier.n.01', 'name': 'Grand_Marnier'}, {'id': 14153, 'synset': 'kummel.n.01', 'name': 'kummel'}, {'id': 14154, 'synset': 'maraschino.n.01', 'name': 'maraschino'}, {'id': 14155, 'synset': 'pastis.n.01', 'name': 'pastis'}, {'id': 14156, 'synset': 'pernod.n.01', 'name': 'Pernod'}, {'id': 14157, 'synset': 'pousse-cafe.n.01', 'name': 'pousse-cafe'}, {'id': 14158, 'synset': 'kahlua.n.01', 'name': 'Kahlua'}, {'id': 14159, 'synset': 'ratafia.n.01', 'name': 'ratafia'}, {'id': 14160, 'synset': 'sambuca.n.01', 'name': 'sambuca'}, {'id': 14161, 'synset': 'mixed_drink.n.01', 'name': 'mixed_drink'}, {'id': 14162, 'synset': 'cocktail.n.01', 'name': 'cocktail'}, {'id': 14163, 'synset': 'dom_pedro.n.01', 'name': 'Dom_Pedro'}, {'id': 14164, 'synset': 'highball.n.01', 'name': 'highball'}, {'id': 14165, 'synset': 'mixer.n.02', 'name': 'mixer'}, {'id': 14166, 'synset': 'bishop.n.02', 'name': 'bishop'}, {'id': 14167, 'synset': 'bloody_mary.n.02', 'name': 'Bloody_Mary'}, {'id': 14168, 'synset': 'virgin_mary.n.02', 'name': 'Virgin_Mary'}, {'id': 14169, 'synset': 'bullshot.n.01', 'name': 'bullshot'}, {'id': 14170, 'synset': 'cobbler.n.02', 'name': 'cobbler'}, {'id': 14171, 'synset': 'collins.n.02', 'name': 'collins'}, {'id': 14172, 'synset': 'cooler.n.02', 'name': 'cooler'}, {'id': 14173, 'synset': 'refresher.n.02', 'name': 'refresher'}, {'id': 14174, 'synset': 'daiquiri.n.01', 'name': 'daiquiri'}, {'id': 14175, 'synset': 'strawberry_daiquiri.n.01', 'name': 'strawberry_daiquiri'}, {'id': 14176, 'synset': 'nada_daiquiri.n.01', 'name': 'NADA_daiquiri'}, {'id': 14177, 'synset': 'spritzer.n.01', 'name': 'spritzer'}, {'id': 14178, 'synset': 'flip.n.02', 'name': 'flip'}, {'id': 14179, 'synset': 'gimlet.n.01', 'name': 'gimlet'}, {'id': 14180, 'synset': 'gin_and_tonic.n.01', 'name': 'gin_and_tonic'}, {'id': 14181, 'synset': 'grasshopper.n.02', 'name': 'grasshopper'}, {'id': 14182, 'synset': 'harvey_wallbanger.n.01', 'name': 'Harvey_Wallbanger'}, {'id': 14183, 'synset': 'julep.n.01', 'name': 'julep'}, {'id': 14184, 'synset': 'manhattan.n.02', 'name': 'manhattan'}, {'id': 14185, 'synset': 'rob_roy.n.02', 'name': 'Rob_Roy'}, {'id': 14186, 'synset': 'margarita.n.01', 'name': 'margarita'}, {'id': 14187, 'synset': 'gin_and_it.n.01', 'name': 'gin_and_it'}, {'id': 14188, 'synset': 'vodka_martini.n.01', 'name': 'vodka_martini'}, {'id': 14189, 'synset': 'old_fashioned.n.01', 'name': 'old_fashioned'}, {'id': 14190, 'synset': 'pink_lady.n.01', 'name': 'pink_lady'}, {'id': 14191, 'synset': 'sazerac.n.01', 'name': 'Sazerac'}, {'id': 14192, 'synset': 'screwdriver.n.02', 'name': 'screwdriver'}, {'id': 14193, 'synset': 'sidecar.n.01', 'name': 'sidecar'}, {'id': 14194, 'synset': 'scotch_and_soda.n.01', 'name': 'Scotch_and_soda'}, {'id': 14195, 'synset': 'sling.n.01', 'name': 'sling'}, {'id': 14196, 'synset': 'brandy_sling.n.01', 'name': 'brandy_sling'}, {'id': 14197, 'synset': 'gin_sling.n.01', 'name': 'gin_sling'}, {'id': 14198, 'synset': 'rum_sling.n.01', 'name': 'rum_sling'}, {'id': 14199, 'synset': 'sour.n.01', 'name': 'sour'}, {'id': 14200, 'synset': 'whiskey_sour.n.01', 'name': 'whiskey_sour'}, {'id': 14201, 'synset': 'stinger.n.01', 'name': 'stinger'}, {'id': 14202, 'synset': 'swizzle.n.01', 'name': 'swizzle'}, {'id': 14203, 'synset': 'hot_toddy.n.01', 'name': 'hot_toddy'}, {'id': 14204, 'synset': 'zombie.n.05', 'name': 'zombie'}, {'id': 14205, 'synset': 'fizz.n.01', 'name': 'fizz'}, {'id': 14206, 'synset': 'irish_coffee.n.01', 'name': 'Irish_coffee'}, {'id': 14207, 'synset': 'cafe_au_lait.n.01', 'name': 'cafe_au_lait'}, {'id': 14208, 'synset': 'cafe_noir.n.01', 'name': 'cafe_noir'}, {'id': 14209, 'synset': 'decaffeinated_coffee.n.01', 'name': 'decaffeinated_coffee'}, {'id': 14210, 'synset': 'drip_coffee.n.01', 'name': 'drip_coffee'}, {'id': 14211, 'synset': 'espresso.n.01', 'name': 'espresso'}, {'id': 14212, 'synset': 'caffe_latte.n.01', 'name': 'caffe_latte'}, {'id': 14213, 'synset': 'iced_coffee.n.01', 'name': 'iced_coffee'}, {'id': 14214, 'synset': 'instant_coffee.n.01', 'name': 'instant_coffee'}, {'id': 14215, 'synset': 'mocha.n.03', 'name': 'mocha'}, {'id': 14216, 'synset': 'mocha.n.02', 'name': 'mocha'}, {'id': 14217, 'synset': 'cassareep.n.01', 'name': 'cassareep'}, {'id': 14218, 'synset': 'turkish_coffee.n.01', 'name': 'Turkish_coffee'}, {'id': 14219, 'synset': 'hard_cider.n.01', 'name': 'hard_cider'}, {'id': 14220, 'synset': 'scrumpy.n.01', 'name': 'scrumpy'}, {'id': 14221, 'synset': 'sweet_cider.n.01', 'name': 'sweet_cider'}, {'id': 14222, 'synset': 'mulled_cider.n.01', 'name': 'mulled_cider'}, {'id': 14223, 'synset': 'perry.n.04', 'name': 'perry'}, {'id': 14224, 'synset': 'rotgut.n.01', 'name': 'rotgut'}, {'id': 14225, 'synset': 'slug.n.05', 'name': 'slug'}, {'id': 14226, 'synset': 'criollo.n.02', 'name': 'criollo'}, {'id': 14227, 'synset': 'juice.n.01', 'name': 'juice'}, {'id': 14228, 'synset': 'nectar.n.02', 'name': 'nectar'}, {'id': 14229, 'synset': 'apple_juice.n.01', 'name': 'apple_juice'}, {'id': 14230, 'synset': 'cranberry_juice.n.01', 'name': 'cranberry_juice'}, {'id': 14231, 'synset': 'grape_juice.n.01', 'name': 'grape_juice'}, {'id': 14232, 'synset': 'must.n.02', 'name': 'must'}, {'id': 14233, 'synset': 'grapefruit_juice.n.01', 'name': 'grapefruit_juice'}, {'id': 14234, 'synset': 'frozen_orange_juice.n.01', 'name': 'frozen_orange_juice'}, {'id': 14235, 'synset': 'pineapple_juice.n.01', 'name': 'pineapple_juice'}, {'id': 14236, 'synset': 'lemon_juice.n.01', 'name': 'lemon_juice'}, {'id': 14237, 'synset': 'lime_juice.n.01', 'name': 'lime_juice'}, {'id': 14238, 'synset': 'papaya_juice.n.01', 'name': 'papaya_juice'}, {'id': 14239, 'synset': 'tomato_juice.n.01', 'name': 'tomato_juice'}, {'id': 14240, 'synset': 'carrot_juice.n.01', 'name': 'carrot_juice'}, {'id': 14241, 'synset': 'v-8_juice.n.01', 'name': 'V-8_juice'}, {'id': 14242, 'synset': 'koumiss.n.01', 'name': 'koumiss'}, {'id': 14243, 'synset': 'fruit_drink.n.01', 'name': 'fruit_drink'}, {'id': 14244, 'synset': 'limeade.n.01', 'name': 'limeade'}, {'id': 14245, 'synset': 'orangeade.n.01', 'name': 'orangeade'}, {'id': 14246, 'synset': 'malted_milk.n.02', 'name': 'malted_milk'}, {'id': 14247, 'synset': 'mate.n.09', 'name': 'mate'}, {'id': 14248, 'synset': 'mulled_wine.n.01', 'name': 'mulled_wine'}, {'id': 14249, 'synset': 'negus.n.01', 'name': 'negus'}, {'id': 14250, 'synset': 'soft_drink.n.01', 'name': 'soft_drink'}, {'id': 14251, 'synset': 'birch_beer.n.01', 'name': 'birch_beer'}, {'id': 14252, 'synset': 'bitter_lemon.n.01', 'name': 'bitter_lemon'}, {'id': 14253, 'synset': 'cola.n.02', 'name': 'cola'}, {'id': 14254, 'synset': 'cream_soda.n.01', 'name': 'cream_soda'}, {'id': 14255, 'synset': 'egg_cream.n.01', 'name': 'egg_cream'}, {'id': 14256, 'synset': 'ginger_ale.n.01', 'name': 'ginger_ale'}, {'id': 14257, 'synset': 'orange_soda.n.01', 'name': 'orange_soda'}, {'id': 14258, 'synset': 'phosphate.n.02', 'name': 'phosphate'}, {'id': 14259, 'synset': 'coca_cola.n.01', 'name': 'Coca_Cola'}, {'id': 14260, 'synset': 'pepsi.n.01', 'name': 'Pepsi'}, {'id': 14261, 'synset': 'sarsaparilla.n.02', 'name': 'sarsaparilla'}, {'id': 14262, 'synset': 'tonic.n.01', 'name': 'tonic'}, {'id': 14263, 'synset': 'coffee_bean.n.01', 'name': 'coffee_bean'}, {'id': 14264, 'synset': 'coffee.n.01', 'name': 'coffee'}, {'id': 14265, 'synset': 'cafe_royale.n.01', 'name': 'cafe_royale'}, {'id': 14266, 'synset': 'fruit_punch.n.01', 'name': 'fruit_punch'}, {'id': 14267, 'synset': 'milk_punch.n.01', 'name': 'milk_punch'}, {'id': 14268, 'synset': 'mimosa.n.03', 'name': 'mimosa'}, {'id': 14269, 'synset': 'pina_colada.n.01', 'name': 'pina_colada'}, {'id': 14270, 'synset': 'punch.n.02', 'name': 'punch'}, {'id': 14271, 'synset': 'cup.n.06', 'name': 'cup'}, {'id': 14272, 'synset': 'champagne_cup.n.01', 'name': 'champagne_cup'}, {'id': 14273, 'synset': 'claret_cup.n.01', 'name': 'claret_cup'}, {'id': 14274, 'synset': 'wassail.n.01', 'name': 'wassail'}, {'id': 14275, 'synset': "planter's_punch.n.01", 'name': "planter's_punch"}, {'id': 14276, 'synset': 'white_russian.n.02', 'name': 'White_Russian'}, {'id': 14277, 'synset': 'fish_house_punch.n.01', 'name': 'fish_house_punch'}, {'id': 14278, 'synset': 'may_wine.n.01', 'name': 'May_wine'}, {'id': 14279, 'synset': 'eggnog.n.01', 'name': 'eggnog'}, {'id': 14280, 'synset': 'cassiri.n.01', 'name': 'cassiri'}, {'id': 14281, 'synset': 'spruce_beer.n.01', 'name': 'spruce_beer'}, {'id': 14282, 'synset': 'rickey.n.01', 'name': 'rickey'}, {'id': 14283, 'synset': 'gin_rickey.n.01', 'name': 'gin_rickey'}, {'id': 14284, 'synset': 'tea.n.05', 'name': 'tea'}, {'id': 14285, 'synset': 'tea.n.01', 'name': 'tea'}, {'id': 14286, 'synset': 'tea-like_drink.n.01', 'name': 'tea-like_drink'}, {'id': 14287, 'synset': 'cambric_tea.n.01', 'name': 'cambric_tea'}, {'id': 14288, 'synset': 'cuppa.n.01', 'name': 'cuppa'}, {'id': 14289, 'synset': 'herb_tea.n.01', 'name': 'herb_tea'}, {'id': 14290, 'synset': 'tisane.n.01', 'name': 'tisane'}, {'id': 14291, 'synset': 'camomile_tea.n.01', 'name': 'camomile_tea'}, {'id': 14292, 'synset': 'ice_tea.n.01', 'name': 'ice_tea'}, {'id': 14293, 'synset': 'sun_tea.n.01', 'name': 'sun_tea'}, {'id': 14294, 'synset': 'black_tea.n.01', 'name': 'black_tea'}, {'id': 14295, 'synset': 'congou.n.01', 'name': 'congou'}, {'id': 14296, 'synset': 'darjeeling.n.01', 'name': 'Darjeeling'}, {'id': 14297, 'synset': 'orange_pekoe.n.01', 'name': 'orange_pekoe'}, {'id': 14298, 'synset': 'souchong.n.01', 'name': 'souchong'}, {'id': 14299, 'synset': 'green_tea.n.01', 'name': 'green_tea'}, {'id': 14300, 'synset': 'hyson.n.01', 'name': 'hyson'}, {'id': 14301, 'synset': 'oolong.n.01', 'name': 'oolong'}, {'id': 14302, 'synset': 'water.n.06', 'name': 'water'}, {'id': 14303, 'synset': 'bottled_water.n.01', 'name': 'bottled_water'}, {'id': 14304, 'synset': 'branch_water.n.01', 'name': 'branch_water'}, {'id': 14305, 'synset': 'spring_water.n.02', 'name': 'spring_water'}, {'id': 14306, 'synset': 'sugar_water.n.01', 'name': 'sugar_water'}, {'id': 14307, 'synset': 'drinking_water.n.01', 'name': 'drinking_water'}, {'id': 14308, 'synset': 'ice_water.n.01', 'name': 'ice_water'}, {'id': 14309, 'synset': 'soda_water.n.01', 'name': 'soda_water'}, {'id': 14310, 'synset': 'mineral_water.n.01', 'name': 'mineral_water'}, {'id': 14311, 'synset': 'seltzer.n.01', 'name': 'seltzer'}, {'id': 14312, 'synset': 'vichy_water.n.01', 'name': 'Vichy_water'}, {'id': 14313, 'synset': 'perishable.n.01', 'name': 'perishable'}, {'id': 14314, 'synset': 'couscous.n.01', 'name': 'couscous'}, {'id': 14315, 'synset': 'ramekin.n.01', 'name': 'ramekin'}, {'id': 14316, 'synset': 'multivitamin.n.01', 'name': 'multivitamin'}, {'id': 14317, 'synset': 'vitamin_pill.n.01', 'name': 'vitamin_pill'}, {'id': 14318, 'synset': 'soul_food.n.01', 'name': 'soul_food'}, {'id': 14319, 'synset': 'mold.n.06', 'name': 'mold'}, {'id': 14320, 'synset': 'people.n.01', 'name': 'people'}, {'id': 14321, 'synset': 'collection.n.01', 'name': 'collection'}, {'id': 14322, 'synset': 'book.n.07', 'name': 'book'}, {'id': 14323, 'synset': 'library.n.02', 'name': 'library'}, {'id': 14324, 'synset': 'baseball_club.n.01', 'name': 'baseball_club'}, {'id': 14325, 'synset': 'crowd.n.01', 'name': 'crowd'}, {'id': 14326, 'synset': 'class.n.02', 'name': 'class'}, {'id': 14327, 'synset': 'core.n.01', 'name': 'core'}, {'id': 14328, 'synset': 'concert_band.n.01', 'name': 'concert_band'}, {'id': 14329, 'synset': 'dance.n.02', 'name': 'dance'}, {'id': 14330, 'synset': 'wedding.n.03', 'name': 'wedding'}, {'id': 14331, 'synset': 'chain.n.01', 'name': 'chain'}, {'id': 14332, 'synset': 'power_breakfast.n.01', 'name': 'power_breakfast'}, {'id': 14333, 'synset': 'aerie.n.02', 'name': 'aerie'}, {'id': 14334, 'synset': 'agora.n.02', 'name': 'agora'}, {'id': 14335, 'synset': 'amusement_park.n.01', 'name': 'amusement_park'}, {'id': 14336, 'synset': 'aphelion.n.01', 'name': 'aphelion'}, {'id': 14337, 'synset': 'apron.n.02', 'name': 'apron'}, {'id': 14338, 'synset': 'interplanetary_space.n.01', 'name': 'interplanetary_space'}, {'id': 14339, 'synset': 'interstellar_space.n.01', 'name': 'interstellar_space'}, {'id': 14340, 'synset': 'intergalactic_space.n.01', 'name': 'intergalactic_space'}, {'id': 14341, 'synset': 'bush.n.02', 'name': 'bush'}, {'id': 14342, 'synset': 'semidesert.n.01', 'name': 'semidesert'}, {'id': 14343, 'synset': 'beam-ends.n.01', 'name': 'beam-ends'}, {'id': 14344, 'synset': 'bridgehead.n.02', 'name': 'bridgehead'}, {'id': 14345, 'synset': 'bus_stop.n.01', 'name': 'bus_stop'}, {'id': 14346, 'synset': 'campsite.n.01', 'name': 'campsite'}, {'id': 14347, 'synset': 'detention_basin.n.01', 'name': 'detention_basin'}, {'id': 14348, 'synset': 'cemetery.n.01', 'name': 'cemetery'}, {'id': 14349, 'synset': 'trichion.n.01', 'name': 'trichion'}, {'id': 14350, 'synset': 'city.n.01', 'name': 'city'}, {'id': 14351, 'synset': 'business_district.n.01', 'name': 'business_district'}, {'id': 14352, 'synset': 'outskirts.n.01', 'name': 'outskirts'}, {'id': 14353, 'synset': 'borough.n.01', 'name': 'borough'}, {'id': 14354, 'synset': 'cow_pasture.n.01', 'name': 'cow_pasture'}, {'id': 14355, 'synset': 'crest.n.01', 'name': 'crest'}, {'id': 14356, 'synset': 'eparchy.n.02', 'name': 'eparchy'}, {'id': 14357, 'synset': 'suburb.n.01', 'name': 'suburb'}, {'id': 14358, 'synset': 'stockbroker_belt.n.01', 'name': 'stockbroker_belt'}, {'id': 14359, 'synset': 'crawlspace.n.01', 'name': 'crawlspace'}, {'id': 14360, 'synset': 'sheikdom.n.01', 'name': 'sheikdom'}, {'id': 14361, 'synset': 'residence.n.01', 'name': 'residence'}, {'id': 14362, 'synset': 'domicile.n.01', 'name': 'domicile'}, {'id': 14363, 'synset': 'dude_ranch.n.01', 'name': 'dude_ranch'}, {'id': 14364, 'synset': 'farmland.n.01', 'name': 'farmland'}, {'id': 14365, 'synset': 'midfield.n.01', 'name': 'midfield'}, {'id': 14366, 'synset': 'firebreak.n.01', 'name': 'firebreak'}, {'id': 14367, 'synset': 'flea_market.n.01', 'name': 'flea_market'}, {'id': 14368, 'synset': 'battlefront.n.01', 'name': 'battlefront'}, {'id': 14369, 'synset': 'garbage_heap.n.01', 'name': 'garbage_heap'}, {'id': 14370, 'synset': 'benthos.n.01', 'name': 'benthos'}, {'id': 14371, 'synset': 'goldfield.n.01', 'name': 'goldfield'}, {'id': 14372, 'synset': 'grainfield.n.01', 'name': 'grainfield'}, {'id': 14373, 'synset': 'half-mast.n.01', 'name': 'half-mast'}, {'id': 14374, 'synset': 'hemline.n.01', 'name': 'hemline'}, {'id': 14375, 'synset': 'heronry.n.01', 'name': 'heronry'}, {'id': 14376, 'synset': 'hipline.n.02', 'name': 'hipline'}, {'id': 14377, 'synset': 'hipline.n.01', 'name': 'hipline'}, {'id': 14378, 'synset': 'hole-in-the-wall.n.01', 'name': 'hole-in-the-wall'}, {'id': 14379, 'synset': 'junkyard.n.01', 'name': 'junkyard'}, {'id': 14380, 'synset': 'isoclinic_line.n.01', 'name': 'isoclinic_line'}, {'id': 14381, 'synset': 'littoral.n.01', 'name': 'littoral'}, {'id': 14382, 'synset': 'magnetic_pole.n.01', 'name': 'magnetic_pole'}, {'id': 14383, 'synset': 'grassland.n.01', 'name': 'grassland'}, {'id': 14384, 'synset': 'mecca.n.02', 'name': 'mecca'}, {'id': 14385, 'synset': "observer's_meridian.n.01", 'name': "observer's_meridian"}, {'id': 14386, 'synset': 'prime_meridian.n.01', 'name': 'prime_meridian'}, {'id': 14387, 'synset': 'nombril.n.01', 'name': 'nombril'}, {'id': 14388, 'synset': 'no-parking_zone.n.01', 'name': 'no-parking_zone'}, {'id': 14389, 'synset': 'outdoors.n.01', 'name': 'outdoors'}, {'id': 14390, 'synset': 'fairground.n.01', 'name': 'fairground'}, {'id': 14391, 'synset': 'pasture.n.01', 'name': 'pasture'}, {'id': 14392, 'synset': 'perihelion.n.01', 'name': 'perihelion'}, {'id': 14393, 'synset': 'periselene.n.01', 'name': 'periselene'}, {'id': 14394, 'synset': 'locus_of_infection.n.01', 'name': 'locus_of_infection'}, {'id': 14395, 'synset': 'kasbah.n.01', 'name': 'kasbah'}, {'id': 14396, 'synset': 'waterfront.n.01', 'name': 'waterfront'}, {'id': 14397, 'synset': 'resort.n.01', 'name': 'resort'}, {'id': 14398, 'synset': 'resort_area.n.01', 'name': 'resort_area'}, {'id': 14399, 'synset': 'rough.n.01', 'name': 'rough'}, {'id': 14400, 'synset': 'ashram.n.02', 'name': 'ashram'}, {'id': 14401, 'synset': 'harborage.n.01', 'name': 'harborage'}, {'id': 14402, 'synset': 'scrubland.n.01', 'name': 'scrubland'}, {'id': 14403, 'synset': 'weald.n.01', 'name': 'weald'}, {'id': 14404, 'synset': 'wold.n.01', 'name': 'wold'}, {'id': 14405, 'synset': 'schoolyard.n.01', 'name': 'schoolyard'}, {'id': 14406, 'synset': 'showplace.n.01', 'name': 'showplace'}, {'id': 14407, 'synset': 'bedside.n.01', 'name': 'bedside'}, {'id': 14408, 'synset': 'sideline.n.01', 'name': 'sideline'}, {'id': 14409, 'synset': 'ski_resort.n.01', 'name': 'ski_resort'}, {'id': 14410, 'synset': 'soil_horizon.n.01', 'name': 'soil_horizon'}, {'id': 14411, 'synset': 'geological_horizon.n.01', 'name': 'geological_horizon'}, {'id': 14412, 'synset': 'coal_seam.n.01', 'name': 'coal_seam'}, {'id': 14413, 'synset': 'coalface.n.01', 'name': 'coalface'}, {'id': 14414, 'synset': 'field.n.14', 'name': 'field'}, {'id': 14415, 'synset': 'oilfield.n.01', 'name': 'oilfield'}, {'id': 14416, 'synset': 'temperate_zone.n.01', 'name': 'Temperate_Zone'}, {'id': 14417, 'synset': 'terreplein.n.01', 'name': 'terreplein'}, {'id': 14418, 'synset': 'three-mile_limit.n.01', 'name': 'three-mile_limit'}, {'id': 14419, 'synset': 'desktop.n.01', 'name': 'desktop'}, {'id': 14420, 'synset': 'top.n.01', 'name': 'top'}, {'id': 14421, 'synset': 'kampong.n.01', 'name': 'kampong'}, {'id': 14422, 'synset': 'subtropics.n.01', 'name': 'subtropics'}, {'id': 14423, 'synset': 'barrio.n.02', 'name': 'barrio'}, {'id': 14424, 'synset': 'veld.n.01', 'name': 'veld'}, {'id': 14425, 'synset': 'vertex.n.02', 'name': 'vertex'}, {'id': 14426, 'synset': 'waterline.n.01', 'name': 'waterline'}, {'id': 14427, 'synset': 'high-water_mark.n.01', 'name': 'high-water_mark'}, {'id': 14428, 'synset': 'low-water_mark.n.02', 'name': 'low-water_mark'}, {'id': 14429, 'synset': 'continental_divide.n.01', 'name': 'continental_divide'}, {'id': 14430, 'synset': 'zodiac.n.01', 'name': 'zodiac'}, {'id': 14431, 'synset': 'aegean_island.n.01', 'name': 'Aegean_island'}, {'id': 14432, 'synset': 'sultanate.n.01', 'name': 'sultanate'}, {'id': 14433, 'synset': 'swiss_canton.n.01', 'name': 'Swiss_canton'}, {'id': 14434, 'synset': 'abyssal_zone.n.01', 'name': 'abyssal_zone'}, {'id': 14435, 'synset': 'aerie.n.01', 'name': 'aerie'}, {'id': 14436, 'synset': 'air_bubble.n.01', 'name': 'air_bubble'}, {'id': 14437, 'synset': 'alluvial_flat.n.01', 'name': 'alluvial_flat'}, {'id': 14438, 'synset': 'alp.n.01', 'name': 'alp'}, {'id': 14439, 'synset': 'alpine_glacier.n.01', 'name': 'Alpine_glacier'}, {'id': 14440, 'synset': 'anthill.n.01', 'name': 'anthill'}, {'id': 14441, 'synset': 'aquifer.n.01', 'name': 'aquifer'}, {'id': 14442, 'synset': 'archipelago.n.01', 'name': 'archipelago'}, {'id': 14443, 'synset': 'arete.n.01', 'name': 'arete'}, {'id': 14444, 'synset': 'arroyo.n.01', 'name': 'arroyo'}, {'id': 14445, 'synset': 'ascent.n.01', 'name': 'ascent'}, {'id': 14446, 'synset': 'asterism.n.02', 'name': 'asterism'}, {'id': 14447, 'synset': 'asthenosphere.n.01', 'name': 'asthenosphere'}, {'id': 14448, 'synset': 'atoll.n.01', 'name': 'atoll'}, {'id': 14449, 'synset': 'bank.n.03', 'name': 'bank'}, {'id': 14450, 'synset': 'bank.n.01', 'name': 'bank'}, {'id': 14451, 'synset': 'bar.n.08', 'name': 'bar'}, {'id': 14452, 'synset': 'barbecue_pit.n.01', 'name': 'barbecue_pit'}, {'id': 14453, 'synset': 'barrier_reef.n.01', 'name': 'barrier_reef'}, {'id': 14454, 'synset': 'baryon.n.01', 'name': 'baryon'}, {'id': 14455, 'synset': 'basin.n.03', 'name': 'basin'}, {'id': 14456, 'synset': 'beach.n.01', 'name': 'beach'}, {'id': 14457, 'synset': 'honeycomb.n.01', 'name': 'honeycomb'}, {'id': 14458, 'synset': 'belay.n.01', 'name': 'belay'}, {'id': 14459, 'synset': 'ben.n.01', 'name': 'ben'}, {'id': 14460, 'synset': 'berm.n.01', 'name': 'berm'}, {'id': 14461, 'synset': 'bladder_stone.n.01', 'name': 'bladder_stone'}, {'id': 14462, 'synset': 'bluff.n.01', 'name': 'bluff'}, {'id': 14463, 'synset': 'borrow_pit.n.01', 'name': 'borrow_pit'}, {'id': 14464, 'synset': 'brae.n.01', 'name': 'brae'}, {'id': 14465, 'synset': 'bubble.n.01', 'name': 'bubble'}, {'id': 14466, 'synset': 'burrow.n.01', 'name': 'burrow'}, {'id': 14467, 'synset': 'butte.n.01', 'name': 'butte'}, {'id': 14468, 'synset': 'caldera.n.01', 'name': 'caldera'}, {'id': 14469, 'synset': 'canyon.n.01', 'name': 'canyon'}, {'id': 14470, 'synset': 'canyonside.n.01', 'name': 'canyonside'}, {'id': 14471, 'synset': 'cave.n.01', 'name': 'cave'}, {'id': 14472, 'synset': 'cavern.n.02', 'name': 'cavern'}, {'id': 14473, 'synset': 'chasm.n.01', 'name': 'chasm'}, {'id': 14474, 'synset': 'cirque.n.01', 'name': 'cirque'}, {'id': 14475, 'synset': 'cliff.n.01', 'name': 'cliff'}, {'id': 14476, 'synset': 'cloud.n.02', 'name': 'cloud'}, {'id': 14477, 'synset': 'coast.n.02', 'name': 'coast'}, {'id': 14478, 'synset': 'coastland.n.01', 'name': 'coastland'}, {'id': 14479, 'synset': 'col.n.01', 'name': 'col'}, {'id': 14480, 'synset': 'collector.n.03', 'name': 'collector'}, {'id': 14481, 'synset': 'comet.n.01', 'name': 'comet'}, {'id': 14482, 'synset': 'continental_glacier.n.01', 'name': 'continental_glacier'}, {'id': 14483, 'synset': 'coral_reef.n.01', 'name': 'coral_reef'}, {'id': 14484, 'synset': 'cove.n.02', 'name': 'cove'}, {'id': 14485, 'synset': 'crag.n.01', 'name': 'crag'}, {'id': 14486, 'synset': 'crater.n.03', 'name': 'crater'}, {'id': 14487, 'synset': 'cultivated_land.n.01', 'name': 'cultivated_land'}, {'id': 14488, 'synset': 'dale.n.01', 'name': 'dale'}, {'id': 14489, 'synset': 'defile.n.01', 'name': 'defile'}, {'id': 14490, 'synset': 'delta.n.01', 'name': 'delta'}, {'id': 14491, 'synset': 'descent.n.05', 'name': 'descent'}, {'id': 14492, 'synset': 'diapir.n.01', 'name': 'diapir'}, {'id': 14493, 'synset': 'divot.n.02', 'name': 'divot'}, {'id': 14494, 'synset': 'divot.n.01', 'name': 'divot'}, {'id': 14495, 'synset': 'down.n.04', 'name': 'down'}, {'id': 14496, 'synset': 'downhill.n.01', 'name': 'downhill'}, {'id': 14497, 'synset': 'draw.n.01', 'name': 'draw'}, {'id': 14498, 'synset': 'drey.n.01', 'name': 'drey'}, {'id': 14499, 'synset': 'drumlin.n.01', 'name': 'drumlin'}, {'id': 14500, 'synset': 'dune.n.01', 'name': 'dune'}, {'id': 14501, 'synset': 'escarpment.n.01', 'name': 'escarpment'}, {'id': 14502, 'synset': 'esker.n.01', 'name': 'esker'}, {'id': 14503, 'synset': 'fireball.n.03', 'name': 'fireball'}, {'id': 14504, 'synset': 'flare_star.n.01', 'name': 'flare_star'}, {'id': 14505, 'synset': 'floor.n.04', 'name': 'floor'}, {'id': 14506, 'synset': 'fomite.n.01', 'name': 'fomite'}, {'id': 14507, 'synset': 'foothill.n.01', 'name': 'foothill'}, {'id': 14508, 'synset': 'footwall.n.01', 'name': 'footwall'}, {'id': 14509, 'synset': 'foreland.n.02', 'name': 'foreland'}, {'id': 14510, 'synset': 'foreshore.n.01', 'name': 'foreshore'}, {'id': 14511, 'synset': 'gauge_boson.n.01', 'name': 'gauge_boson'}, {'id': 14512, 'synset': 'geological_formation.n.01', 'name': 'geological_formation'}, {'id': 14513, 'synset': 'geyser.n.01', 'name': 'geyser'}, {'id': 14514, 'synset': 'glacier.n.01', 'name': 'glacier'}, {'id': 14515, 'synset': 'glen.n.01', 'name': 'glen'}, {'id': 14516, 'synset': 'gopher_hole.n.01', 'name': 'gopher_hole'}, {'id': 14517, 'synset': 'gorge.n.01', 'name': 'gorge'}, {'id': 14518, 'synset': 'grotto.n.01', 'name': 'grotto'}, {'id': 14519, 'synset': 'growler.n.02', 'name': 'growler'}, {'id': 14520, 'synset': 'gulch.n.01', 'name': 'gulch'}, {'id': 14521, 'synset': 'gully.n.01', 'name': 'gully'}, {'id': 14522, 'synset': 'hail.n.02', 'name': 'hail'}, {'id': 14523, 'synset': 'highland.n.01', 'name': 'highland'}, {'id': 14524, 'synset': 'hill.n.01', 'name': 'hill'}, {'id': 14525, 'synset': 'hillside.n.01', 'name': 'hillside'}, {'id': 14526, 'synset': 'hole.n.05', 'name': 'hole'}, {'id': 14527, 'synset': 'hollow.n.02', 'name': 'hollow'}, {'id': 14528, 'synset': 'hot_spring.n.01', 'name': 'hot_spring'}, {'id': 14529, 'synset': 'iceberg.n.01', 'name': 'iceberg'}, {'id': 14530, 'synset': 'icecap.n.01', 'name': 'icecap'}, {'id': 14531, 'synset': 'ice_field.n.01', 'name': 'ice_field'}, {'id': 14532, 'synset': 'ice_floe.n.01', 'name': 'ice_floe'}, {'id': 14533, 'synset': 'ice_mass.n.01', 'name': 'ice_mass'}, {'id': 14534, 'synset': 'inclined_fault.n.01', 'name': 'inclined_fault'}, {'id': 14535, 'synset': 'ion.n.01', 'name': 'ion'}, {'id': 14536, 'synset': 'isthmus.n.01', 'name': 'isthmus'}, {'id': 14537, 'synset': 'kidney_stone.n.01', 'name': 'kidney_stone'}, {'id': 14538, 'synset': 'knoll.n.01', 'name': 'knoll'}, {'id': 14539, 'synset': 'kopje.n.01', 'name': 'kopje'}, {'id': 14540, 'synset': 'kuiper_belt.n.01', 'name': 'Kuiper_belt'}, {'id': 14541, 'synset': 'lake_bed.n.01', 'name': 'lake_bed'}, {'id': 14542, 'synset': 'lakefront.n.01', 'name': 'lakefront'}, {'id': 14543, 'synset': 'lakeside.n.01', 'name': 'lakeside'}, {'id': 14544, 'synset': 'landfall.n.01', 'name': 'landfall'}, {'id': 14545, 'synset': 'landfill.n.01', 'name': 'landfill'}, {'id': 14546, 'synset': 'lather.n.04', 'name': 'lather'}, {'id': 14547, 'synset': 'leak.n.01', 'name': 'leak'}, {'id': 14548, 'synset': 'ledge.n.01', 'name': 'ledge'}, {'id': 14549, 'synset': 'lepton.n.02', 'name': 'lepton'}, {'id': 14550, 'synset': 'lithosphere.n.01', 'name': 'lithosphere'}, {'id': 14551, 'synset': 'lowland.n.01', 'name': 'lowland'}, {'id': 14552, 'synset': 'lunar_crater.n.01', 'name': 'lunar_crater'}, {'id': 14553, 'synset': 'maar.n.01', 'name': 'maar'}, {'id': 14554, 'synset': 'massif.n.01', 'name': 'massif'}, {'id': 14555, 'synset': 'meander.n.01', 'name': 'meander'}, {'id': 14556, 'synset': 'mesa.n.01', 'name': 'mesa'}, {'id': 14557, 'synset': 'meteorite.n.01', 'name': 'meteorite'}, {'id': 14558, 'synset': 'microfossil.n.01', 'name': 'microfossil'}, {'id': 14559, 'synset': 'midstream.n.01', 'name': 'midstream'}, {'id': 14560, 'synset': 'molehill.n.01', 'name': 'molehill'}, {'id': 14561, 'synset': 'monocline.n.01', 'name': 'monocline'}, {'id': 14562, 'synset': 'mountain.n.01', 'name': 'mountain'}, {'id': 14563, 'synset': 'mountainside.n.01', 'name': 'mountainside'}, {'id': 14564, 'synset': 'mouth.n.04', 'name': 'mouth'}, {'id': 14565, 'synset': 'mull.n.01', 'name': 'mull'}, {'id': 14566, 'synset': 'natural_depression.n.01', 'name': 'natural_depression'}, {'id': 14567, 'synset': 'natural_elevation.n.01', 'name': 'natural_elevation'}, {'id': 14568, 'synset': 'nullah.n.01', 'name': 'nullah'}, {'id': 14569, 'synset': 'ocean.n.01', 'name': 'ocean'}, {'id': 14570, 'synset': 'ocean_floor.n.01', 'name': 'ocean_floor'}, {'id': 14571, 'synset': 'oceanfront.n.01', 'name': 'oceanfront'}, {'id': 14572, 'synset': 'outcrop.n.01', 'name': 'outcrop'}, {'id': 14573, 'synset': 'oxbow.n.01', 'name': 'oxbow'}, {'id': 14574, 'synset': 'pallasite.n.01', 'name': 'pallasite'}, {'id': 14575, 'synset': 'perforation.n.02', 'name': 'perforation'}, {'id': 14576, 'synset': 'photosphere.n.01', 'name': 'photosphere'}, {'id': 14577, 'synset': 'piedmont.n.02', 'name': 'piedmont'}, {'id': 14578, 'synset': 'piedmont_glacier.n.01', 'name': 'Piedmont_glacier'}, {'id': 14579, 'synset': 'pinetum.n.01', 'name': 'pinetum'}, {'id': 14580, 'synset': 'plage.n.01', 'name': 'plage'}, {'id': 14581, 'synset': 'plain.n.01', 'name': 'plain'}, {'id': 14582, 'synset': 'point.n.11', 'name': 'point'}, {'id': 14583, 'synset': 'polar_glacier.n.01', 'name': 'polar_glacier'}, {'id': 14584, 'synset': 'pothole.n.01', 'name': 'pothole'}, {'id': 14585, 'synset': 'precipice.n.01', 'name': 'precipice'}, {'id': 14586, 'synset': 'promontory.n.01', 'name': 'promontory'}, {'id': 14587, 'synset': 'ptyalith.n.01', 'name': 'ptyalith'}, {'id': 14588, 'synset': 'pulsar.n.01', 'name': 'pulsar'}, {'id': 14589, 'synset': 'quicksand.n.02', 'name': 'quicksand'}, {'id': 14590, 'synset': 'rabbit_burrow.n.01', 'name': 'rabbit_burrow'}, {'id': 14591, 'synset': 'radiator.n.01', 'name': 'radiator'}, {'id': 14592, 'synset': 'rainbow.n.01', 'name': 'rainbow'}, {'id': 14593, 'synset': 'range.n.04', 'name': 'range'}, {'id': 14594, 'synset': 'rangeland.n.01', 'name': 'rangeland'}, {'id': 14595, 'synset': 'ravine.n.01', 'name': 'ravine'}, {'id': 14596, 'synset': 'reef.n.01', 'name': 'reef'}, {'id': 14597, 'synset': 'ridge.n.01', 'name': 'ridge'}, {'id': 14598, 'synset': 'ridge.n.04', 'name': 'ridge'}, {'id': 14599, 'synset': 'rift_valley.n.01', 'name': 'rift_valley'}, {'id': 14600, 'synset': 'riparian_forest.n.01', 'name': 'riparian_forest'}, {'id': 14601, 'synset': 'ripple_mark.n.01', 'name': 'ripple_mark'}, {'id': 14602, 'synset': 'riverbank.n.01', 'name': 'riverbank'}, {'id': 14603, 'synset': 'riverbed.n.01', 'name': 'riverbed'}, {'id': 14604, 'synset': 'rock.n.01', 'name': 'rock'}, {'id': 14605, 'synset': 'roof.n.03', 'name': 'roof'}, {'id': 14606, 'synset': 'saltpan.n.01', 'name': 'saltpan'}, {'id': 14607, 'synset': 'sandbank.n.01', 'name': 'sandbank'}, {'id': 14608, 'synset': 'sandbar.n.01', 'name': 'sandbar'}, {'id': 14609, 'synset': 'sandpit.n.01', 'name': 'sandpit'}, {'id': 14610, 'synset': 'sanitary_landfill.n.01', 'name': 'sanitary_landfill'}, {'id': 14611, 'synset': 'sawpit.n.01', 'name': 'sawpit'}, {'id': 14612, 'synset': 'scablands.n.01', 'name': 'scablands'}, {'id': 14613, 'synset': 'seashore.n.01', 'name': 'seashore'}, {'id': 14614, 'synset': 'seaside.n.01', 'name': 'seaside'}, {'id': 14615, 'synset': 'seif_dune.n.01', 'name': 'seif_dune'}, {'id': 14616, 'synset': 'shell.n.06', 'name': 'shell'}, {'id': 14617, 'synset': 'shiner.n.02', 'name': 'shiner'}, {'id': 14618, 'synset': 'shoal.n.01', 'name': 'shoal'}, {'id': 14619, 'synset': 'shore.n.01', 'name': 'shore'}, {'id': 14620, 'synset': 'shoreline.n.01', 'name': 'shoreline'}, {'id': 14621, 'synset': 'sinkhole.n.01', 'name': 'sinkhole'}, {'id': 14622, 'synset': 'ski_slope.n.01', 'name': 'ski_slope'}, {'id': 14623, 'synset': 'sky.n.01', 'name': 'sky'}, {'id': 14624, 'synset': 'slope.n.01', 'name': 'slope'}, {'id': 14625, 'synset': 'snowcap.n.01', 'name': 'snowcap'}, {'id': 14626, 'synset': 'snowdrift.n.01', 'name': 'snowdrift'}, {'id': 14627, 'synset': 'snowfield.n.01', 'name': 'snowfield'}, {'id': 14628, 'synset': 'soapsuds.n.01', 'name': 'soapsuds'}, {'id': 14629, 'synset': 'spit.n.01', 'name': 'spit'}, {'id': 14630, 'synset': 'spoor.n.01', 'name': 'spoor'}, {'id': 14631, 'synset': 'spume.n.01', 'name': 'spume'}, {'id': 14632, 'synset': 'star.n.03', 'name': 'star'}, {'id': 14633, 'synset': 'steep.n.01', 'name': 'steep'}, {'id': 14634, 'synset': 'steppe.n.01', 'name': 'steppe'}, {'id': 14635, 'synset': 'strand.n.05', 'name': 'strand'}, {'id': 14636, 'synset': 'streambed.n.01', 'name': 'streambed'}, {'id': 14637, 'synset': 'sun.n.01', 'name': 'sun'}, {'id': 14638, 'synset': 'supernova.n.01', 'name': 'supernova'}, {'id': 14639, 'synset': 'swale.n.01', 'name': 'swale'}, {'id': 14640, 'synset': 'swamp.n.01', 'name': 'swamp'}, {'id': 14641, 'synset': 'swell.n.02', 'name': 'swell'}, {'id': 14642, 'synset': 'tableland.n.01', 'name': 'tableland'}, {'id': 14643, 'synset': 'talus.n.01', 'name': 'talus'}, {'id': 14644, 'synset': 'tangle.n.01', 'name': 'tangle'}, {'id': 14645, 'synset': 'tar_pit.n.01', 'name': 'tar_pit'}, {'id': 14646, 'synset': 'terrace.n.02', 'name': 'terrace'}, {'id': 14647, 'synset': 'tidal_basin.n.01', 'name': 'tidal_basin'}, {'id': 14648, 'synset': 'tideland.n.01', 'name': 'tideland'}, {'id': 14649, 'synset': 'tor.n.02', 'name': 'tor'}, {'id': 14650, 'synset': 'tor.n.01', 'name': 'tor'}, {'id': 14651, 'synset': 'trapezium.n.02', 'name': 'Trapezium'}, {'id': 14652, 'synset': 'troposphere.n.01', 'name': 'troposphere'}, {'id': 14653, 'synset': 'tundra.n.01', 'name': 'tundra'}, {'id': 14654, 'synset': 'twinkler.n.01', 'name': 'twinkler'}, {'id': 14655, 'synset': 'uphill.n.01', 'name': 'uphill'}, {'id': 14656, 'synset': 'urolith.n.01', 'name': 'urolith'}, {'id': 14657, 'synset': 'valley.n.01', 'name': 'valley'}, {'id': 14658, 'synset': 'vehicle-borne_transmission.n.01', 'name': 'vehicle-borne_transmission'}, {'id': 14659, 'synset': 'vein.n.04', 'name': 'vein'}, {'id': 14660, 'synset': 'volcanic_crater.n.01', 'name': 'volcanic_crater'}, {'id': 14661, 'synset': 'volcano.n.02', 'name': 'volcano'}, {'id': 14662, 'synset': 'wadi.n.01', 'name': 'wadi'}, {'id': 14663, 'synset': 'wall.n.05', 'name': 'wall'}, {'id': 14664, 'synset': 'warren.n.03', 'name': 'warren'}, {'id': 14665, 'synset': "wasp's_nest.n.01", 'name': "wasp's_nest"}, {'id': 14666, 'synset': 'watercourse.n.01', 'name': 'watercourse'}, {'id': 14667, 'synset': 'waterside.n.01', 'name': 'waterside'}, {'id': 14668, 'synset': 'water_table.n.01', 'name': 'water_table'}, {'id': 14669, 'synset': 'whinstone.n.01', 'name': 'whinstone'}, {'id': 14670, 'synset': 'wormcast.n.02', 'name': 'wormcast'}, {'id': 14671, 'synset': 'xenolith.n.01', 'name': 'xenolith'}, {'id': 14672, 'synset': 'circe.n.01', 'name': 'Circe'}, {'id': 14673, 'synset': 'gryphon.n.01', 'name': 'gryphon'}, {'id': 14674, 'synset': 'spiritual_leader.n.01', 'name': 'spiritual_leader'}, {'id': 14675, 'synset': 'messiah.n.01', 'name': 'messiah'}, {'id': 14676, 'synset': 'rhea_silvia.n.01', 'name': 'Rhea_Silvia'}, {'id': 14677, 'synset': 'number_one.n.01', 'name': 'number_one'}, {'id': 14678, 'synset': 'adventurer.n.01', 'name': 'adventurer'}, {'id': 14679, 'synset': 'anomaly.n.02', 'name': 'anomaly'}, {'id': 14680, 'synset': 'appointee.n.02', 'name': 'appointee'}, {'id': 14681, 'synset': 'argonaut.n.01', 'name': 'argonaut'}, {'id': 14682, 'synset': 'ashkenazi.n.01', 'name': 'Ashkenazi'}, {'id': 14683, 'synset': 'benefactor.n.01', 'name': 'benefactor'}, {'id': 14684, 'synset': 'color-blind_person.n.01', 'name': 'color-blind_person'}, {'id': 14685, 'synset': 'commoner.n.01', 'name': 'commoner'}, {'id': 14686, 'synset': 'conservator.n.02', 'name': 'conservator'}, {'id': 14687, 'synset': 'contrarian.n.01', 'name': 'contrarian'}, {'id': 14688, 'synset': 'contadino.n.01', 'name': 'contadino'}, {'id': 14689, 'synset': 'contestant.n.01', 'name': 'contestant'}, {'id': 14690, 'synset': 'cosigner.n.01', 'name': 'cosigner'}, {'id': 14691, 'synset': 'discussant.n.01', 'name': 'discussant'}, {'id': 14692, 'synset': 'enologist.n.01', 'name': 'enologist'}, {'id': 14693, 'synset': 'entertainer.n.01', 'name': 'entertainer'}, {'id': 14694, 'synset': 'eulogist.n.01', 'name': 'eulogist'}, {'id': 14695, 'synset': 'ex-gambler.n.01', 'name': 'ex-gambler'}, {'id': 14696, 'synset': 'experimenter.n.01', 'name': 'experimenter'}, {'id': 14697, 'synset': 'experimenter.n.02', 'name': 'experimenter'}, {'id': 14698, 'synset': 'exponent.n.02', 'name': 'exponent'}, {'id': 14699, 'synset': 'ex-president.n.01', 'name': 'ex-president'}, {'id': 14700, 'synset': 'face.n.05', 'name': 'face'}, {'id': 14701, 'synset': 'female.n.02', 'name': 'female'}, {'id': 14702, 'synset': 'finisher.n.04', 'name': 'finisher'}, {'id': 14703, 'synset': 'inhabitant.n.01', 'name': 'inhabitant'}, {'id': 14704, 'synset': 'native.n.01', 'name': 'native'}, {'id': 14705, 'synset': 'native.n.02', 'name': 'native'}, {'id': 14706, 'synset': 'juvenile.n.01', 'name': 'juvenile'}, {'id': 14707, 'synset': 'lover.n.01', 'name': 'lover'}, {'id': 14708, 'synset': 'male.n.02', 'name': 'male'}, {'id': 14709, 'synset': 'mediator.n.01', 'name': 'mediator'}, {'id': 14710, 'synset': 'mediatrix.n.01', 'name': 'mediatrix'}, {'id': 14711, 'synset': 'national.n.01', 'name': 'national'}, {'id': 14712, 'synset': 'peer.n.01', 'name': 'peer'}, {'id': 14713, 'synset': 'prize_winner.n.01', 'name': 'prize_winner'}, {'id': 14714, 'synset': 'recipient.n.01', 'name': 'recipient'}, {'id': 14715, 'synset': 'religionist.n.01', 'name': 'religionist'}, {'id': 14716, 'synset': 'sensualist.n.01', 'name': 'sensualist'}, {'id': 14717, 'synset': 'traveler.n.01', 'name': 'traveler'}, {'id': 14718, 'synset': 'unwelcome_person.n.01', 'name': 'unwelcome_person'}, {'id': 14719, 'synset': 'unskilled_person.n.01', 'name': 'unskilled_person'}, {'id': 14720, 'synset': 'worker.n.01', 'name': 'worker'}, {'id': 14721, 'synset': 'wrongdoer.n.01', 'name': 'wrongdoer'}, {'id': 14722, 'synset': 'black_african.n.01', 'name': 'Black_African'}, {'id': 14723, 'synset': 'afrikaner.n.01', 'name': 'Afrikaner'}, {'id': 14724, 'synset': 'aryan.n.01', 'name': 'Aryan'}, {'id': 14725, 'synset': 'black.n.05', 'name': 'Black'}, {'id': 14726, 'synset': 'black_woman.n.01', 'name': 'Black_woman'}, {'id': 14727, 'synset': 'mulatto.n.01', 'name': 'mulatto'}, {'id': 14728, 'synset': 'white.n.01', 'name': 'White'}, {'id': 14729, 'synset': 'circassian.n.01', 'name': 'Circassian'}, {'id': 14730, 'synset': 'semite.n.01', 'name': 'Semite'}, {'id': 14731, 'synset': 'chaldean.n.02', 'name': 'Chaldean'}, {'id': 14732, 'synset': 'elamite.n.01', 'name': 'Elamite'}, {'id': 14733, 'synset': 'white_man.n.01', 'name': 'white_man'}, {'id': 14734, 'synset': 'wasp.n.01', 'name': 'WASP'}, {'id': 14735, 'synset': 'gook.n.02', 'name': 'gook'}, {'id': 14736, 'synset': 'mongol.n.01', 'name': 'Mongol'}, {'id': 14737, 'synset': 'tatar.n.01', 'name': 'Tatar'}, {'id': 14738, 'synset': 'nahuatl.n.01', 'name': 'Nahuatl'}, {'id': 14739, 'synset': 'aztec.n.01', 'name': 'Aztec'}, {'id': 14740, 'synset': 'olmec.n.01', 'name': 'Olmec'}, {'id': 14741, 'synset': 'biloxi.n.01', 'name': 'Biloxi'}, {'id': 14742, 'synset': 'blackfoot.n.01', 'name': 'Blackfoot'}, {'id': 14743, 'synset': 'brule.n.01', 'name': 'Brule'}, {'id': 14744, 'synset': 'caddo.n.01', 'name': 'Caddo'}, {'id': 14745, 'synset': 'cheyenne.n.03', 'name': 'Cheyenne'}, {'id': 14746, 'synset': 'chickasaw.n.01', 'name': 'Chickasaw'}, {'id': 14747, 'synset': 'cocopa.n.01', 'name': 'Cocopa'}, {'id': 14748, 'synset': 'comanche.n.01', 'name': 'Comanche'}, {'id': 14749, 'synset': 'creek.n.02', 'name': 'Creek'}, {'id': 14750, 'synset': 'delaware.n.02', 'name': 'Delaware'}, {'id': 14751, 'synset': 'diegueno.n.01', 'name': 'Diegueno'}, {'id': 14752, 'synset': 'esselen.n.01', 'name': 'Esselen'}, {'id': 14753, 'synset': 'eyeish.n.01', 'name': 'Eyeish'}, {'id': 14754, 'synset': 'havasupai.n.01', 'name': 'Havasupai'}, {'id': 14755, 'synset': 'hunkpapa.n.01', 'name': 'Hunkpapa'}, {'id': 14756, 'synset': 'iowa.n.01', 'name': 'Iowa'}, {'id': 14757, 'synset': 'kalapooia.n.01', 'name': 'Kalapooia'}, {'id': 14758, 'synset': 'kamia.n.01', 'name': 'Kamia'}, {'id': 14759, 'synset': 'kekchi.n.01', 'name': 'Kekchi'}, {'id': 14760, 'synset': 'kichai.n.01', 'name': 'Kichai'}, {'id': 14761, 'synset': 'kickapoo.n.01', 'name': 'Kickapoo'}, {'id': 14762, 'synset': 'kiliwa.n.01', 'name': 'Kiliwa'}, {'id': 14763, 'synset': 'malecite.n.01', 'name': 'Malecite'}, {'id': 14764, 'synset': 'maricopa.n.01', 'name': 'Maricopa'}, {'id': 14765, 'synset': 'mohican.n.01', 'name': 'Mohican'}, {'id': 14766, 'synset': 'muskhogean.n.01', 'name': 'Muskhogean'}, {'id': 14767, 'synset': 'navaho.n.01', 'name': 'Navaho'}, {'id': 14768, 'synset': 'nootka.n.01', 'name': 'Nootka'}, {'id': 14769, 'synset': 'oglala.n.01', 'name': 'Oglala'}, {'id': 14770, 'synset': 'osage.n.01', 'name': 'Osage'}, {'id': 14771, 'synset': 'oneida.n.01', 'name': 'Oneida'}, {'id': 14772, 'synset': 'paiute.n.01', 'name': 'Paiute'}, {'id': 14773, 'synset': 'passamaquody.n.01', 'name': 'Passamaquody'}, {'id': 14774, 'synset': 'penobscot.n.01', 'name': 'Penobscot'}, {'id': 14775, 'synset': 'penutian.n.02', 'name': 'Penutian'}, {'id': 14776, 'synset': 'potawatomi.n.01', 'name': 'Potawatomi'}, {'id': 14777, 'synset': 'powhatan.n.02', 'name': 'Powhatan'}, {'id': 14778, 'synset': 'kachina.n.02', 'name': 'kachina'}, {'id': 14779, 'synset': 'salish.n.02', 'name': 'Salish'}, {'id': 14780, 'synset': 'shahaptian.n.01', 'name': 'Shahaptian'}, {'id': 14781, 'synset': 'shasta.n.01', 'name': 'Shasta'}, {'id': 14782, 'synset': 'shawnee.n.01', 'name': 'Shawnee'}, {'id': 14783, 'synset': 'sihasapa.n.01', 'name': 'Sihasapa'}, {'id': 14784, 'synset': 'teton.n.01', 'name': 'Teton'}, {'id': 14785, 'synset': 'taracahitian.n.01', 'name': 'Taracahitian'}, {'id': 14786, 'synset': 'tarahumara.n.01', 'name': 'Tarahumara'}, {'id': 14787, 'synset': 'tuscarora.n.01', 'name': 'Tuscarora'}, {'id': 14788, 'synset': 'tutelo.n.01', 'name': 'Tutelo'}, {'id': 14789, 'synset': 'yana.n.01', 'name': 'Yana'}, {'id': 14790, 'synset': 'yavapai.n.01', 'name': 'Yavapai'}, {'id': 14791, 'synset': 'yokuts.n.02', 'name': 'Yokuts'}, {'id': 14792, 'synset': 'yuma.n.01', 'name': 'Yuma'}, {'id': 14793, 'synset': 'gadaba.n.01', 'name': 'Gadaba'}, {'id': 14794, 'synset': 'kolam.n.01', 'name': 'Kolam'}, {'id': 14795, 'synset': 'kui.n.01', 'name': 'Kui'}, {'id': 14796, 'synset': 'toda.n.01', 'name': 'Toda'}, {'id': 14797, 'synset': 'tulu.n.01', 'name': 'Tulu'}, {'id': 14798, 'synset': 'gujarati.n.01', 'name': 'Gujarati'}, {'id': 14799, 'synset': 'kashmiri.n.01', 'name': 'Kashmiri'}, {'id': 14800, 'synset': 'punjabi.n.01', 'name': 'Punjabi'}, {'id': 14801, 'synset': 'slav.n.01', 'name': 'Slav'}, {'id': 14802, 'synset': 'anabaptist.n.01', 'name': 'Anabaptist'}, {'id': 14803, 'synset': 'adventist.n.01', 'name': 'Adventist'}, {'id': 14804, 'synset': 'gentile.n.03', 'name': 'gentile'}, {'id': 14805, 'synset': 'gentile.n.02', 'name': 'gentile'}, {'id': 14806, 'synset': 'catholic.n.01', 'name': 'Catholic'}, {'id': 14807, 'synset': 'old_catholic.n.01', 'name': 'Old_Catholic'}, {'id': 14808, 'synset': 'uniat.n.01', 'name': 'Uniat'}, {'id': 14809, 'synset': 'copt.n.02', 'name': 'Copt'}, {'id': 14810, 'synset': 'jewess.n.01', 'name': 'Jewess'}, {'id': 14811, 'synset': 'jihadist.n.01', 'name': 'Jihadist'}, {'id': 14812, 'synset': 'buddhist.n.01', 'name': 'Buddhist'}, {'id': 14813, 'synset': 'zen_buddhist.n.01', 'name': 'Zen_Buddhist'}, {'id': 14814, 'synset': 'mahayanist.n.01', 'name': 'Mahayanist'}, {'id': 14815, 'synset': 'swami.n.01', 'name': 'swami'}, {'id': 14816, 'synset': 'hare_krishna.n.01', 'name': 'Hare_Krishna'}, {'id': 14817, 'synset': 'shintoist.n.01', 'name': 'Shintoist'}, {'id': 14818, 'synset': 'eurafrican.n.01', 'name': 'Eurafrican'}, {'id': 14819, 'synset': 'eurasian.n.01', 'name': 'Eurasian'}, {'id': 14820, 'synset': 'gael.n.01', 'name': 'Gael'}, {'id': 14821, 'synset': 'frank.n.01', 'name': 'Frank'}, {'id': 14822, 'synset': 'afghan.n.02', 'name': 'Afghan'}, {'id': 14823, 'synset': 'albanian.n.01', 'name': 'Albanian'}, {'id': 14824, 'synset': 'algerian.n.01', 'name': 'Algerian'}, {'id': 14825, 'synset': 'altaic.n.01', 'name': 'Altaic'}, {'id': 14826, 'synset': 'andorran.n.01', 'name': 'Andorran'}, {'id': 14827, 'synset': 'angolan.n.01', 'name': 'Angolan'}, {'id': 14828, 'synset': 'anguillan.n.01', 'name': 'Anguillan'}, {'id': 14829, 'synset': 'austrian.n.01', 'name': 'Austrian'}, {'id': 14830, 'synset': 'bahamian.n.01', 'name': 'Bahamian'}, {'id': 14831, 'synset': 'bahraini.n.01', 'name': 'Bahraini'}, {'id': 14832, 'synset': 'basotho.n.01', 'name': 'Basotho'}, {'id': 14833, 'synset': 'herero.n.01', 'name': 'Herero'}, {'id': 14834, 'synset': 'luba.n.01', 'name': 'Luba'}, {'id': 14835, 'synset': 'barbadian.n.01', 'name': 'Barbadian'}, {'id': 14836, 'synset': 'bolivian.n.01', 'name': 'Bolivian'}, {'id': 14837, 'synset': 'bornean.n.01', 'name': 'Bornean'}, {'id': 14838, 'synset': 'carioca.n.01', 'name': 'Carioca'}, {'id': 14839, 'synset': 'tupi.n.01', 'name': 'Tupi'}, {'id': 14840, 'synset': 'bruneian.n.01', 'name': 'Bruneian'}, {'id': 14841, 'synset': 'bulgarian.n.01', 'name': 'Bulgarian'}, {'id': 14842, 'synset': 'byelorussian.n.01', 'name': 'Byelorussian'}, {'id': 14843, 'synset': 'cameroonian.n.01', 'name': 'Cameroonian'}, {'id': 14844, 'synset': 'canadian.n.01', 'name': 'Canadian'}, {'id': 14845, 'synset': 'french_canadian.n.01', 'name': 'French_Canadian'}, {'id': 14846, 'synset': 'central_american.n.01', 'name': 'Central_American'}, {'id': 14847, 'synset': 'chilean.n.01', 'name': 'Chilean'}, {'id': 14848, 'synset': 'congolese.n.01', 'name': 'Congolese'}, {'id': 14849, 'synset': 'cypriot.n.01', 'name': 'Cypriot'}, {'id': 14850, 'synset': 'dane.n.01', 'name': 'Dane'}, {'id': 14851, 'synset': 'djiboutian.n.01', 'name': 'Djiboutian'}, {'id': 14852, 'synset': 'britisher.n.01', 'name': 'Britisher'}, {'id': 14853, 'synset': 'english_person.n.01', 'name': 'English_person'}, {'id': 14854, 'synset': 'englishwoman.n.01', 'name': 'Englishwoman'}, {'id': 14855, 'synset': 'anglo-saxon.n.02', 'name': 'Anglo-Saxon'}, {'id': 14856, 'synset': 'angle.n.03', 'name': 'Angle'}, {'id': 14857, 'synset': 'west_saxon.n.01', 'name': 'West_Saxon'}, {'id': 14858, 'synset': 'lombard.n.01', 'name': 'Lombard'}, {'id': 14859, 'synset': 'limey.n.01', 'name': 'limey'}, {'id': 14860, 'synset': 'cantabrigian.n.01', 'name': 'Cantabrigian'}, {'id': 14861, 'synset': 'cornishman.n.01', 'name': 'Cornishman'}, {'id': 14862, 'synset': 'cornishwoman.n.01', 'name': 'Cornishwoman'}, {'id': 14863, 'synset': 'lancastrian.n.02', 'name': 'Lancastrian'}, {'id': 14864, 'synset': 'lancastrian.n.01', 'name': 'Lancastrian'}, {'id': 14865, 'synset': 'geordie.n.01', 'name': 'Geordie'}, {'id': 14866, 'synset': 'oxonian.n.01', 'name': 'Oxonian'}, {'id': 14867, 'synset': 'ethiopian.n.01', 'name': 'Ethiopian'}, {'id': 14868, 'synset': 'amhara.n.01', 'name': 'Amhara'}, {'id': 14869, 'synset': 'eritrean.n.01', 'name': 'Eritrean'}, {'id': 14870, 'synset': 'finn.n.01', 'name': 'Finn'}, {'id': 14871, 'synset': 'komi.n.01', 'name': 'Komi'}, {'id': 14872, 'synset': 'livonian.n.01', 'name': 'Livonian'}, {'id': 14873, 'synset': 'lithuanian.n.01', 'name': 'Lithuanian'}, {'id': 14874, 'synset': 'selkup.n.01', 'name': 'Selkup'}, {'id': 14875, 'synset': 'parisian.n.01', 'name': 'Parisian'}, {'id': 14876, 'synset': 'parisienne.n.01', 'name': 'Parisienne'}, {'id': 14877, 'synset': 'creole.n.02', 'name': 'Creole'}, {'id': 14878, 'synset': 'creole.n.01', 'name': 'Creole'}, {'id': 14879, 'synset': 'gabonese.n.01', 'name': 'Gabonese'}, {'id': 14880, 'synset': 'greek.n.02', 'name': 'Greek'}, {'id': 14881, 'synset': 'dorian.n.01', 'name': 'Dorian'}, {'id': 14882, 'synset': 'athenian.n.01', 'name': 'Athenian'}, {'id': 14883, 'synset': 'laconian.n.01', 'name': 'Laconian'}, {'id': 14884, 'synset': 'guyanese.n.01', 'name': 'Guyanese'}, {'id': 14885, 'synset': 'haitian.n.01', 'name': 'Haitian'}, {'id': 14886, 'synset': 'malay.n.01', 'name': 'Malay'}, {'id': 14887, 'synset': 'moro.n.01', 'name': 'Moro'}, {'id': 14888, 'synset': 'netherlander.n.01', 'name': 'Netherlander'}, {'id': 14889, 'synset': 'icelander.n.01', 'name': 'Icelander'}, {'id': 14890, 'synset': 'iraqi.n.01', 'name': 'Iraqi'}, {'id': 14891, 'synset': 'irishman.n.01', 'name': 'Irishman'}, {'id': 14892, 'synset': 'irishwoman.n.01', 'name': 'Irishwoman'}, {'id': 14893, 'synset': 'dubliner.n.01', 'name': 'Dubliner'}, {'id': 14894, 'synset': 'italian.n.01', 'name': 'Italian'}, {'id': 14895, 'synset': 'roman.n.01', 'name': 'Roman'}, {'id': 14896, 'synset': 'sabine.n.02', 'name': 'Sabine'}, {'id': 14897, 'synset': 'japanese.n.01', 'name': 'Japanese'}, {'id': 14898, 'synset': 'jordanian.n.01', 'name': 'Jordanian'}, {'id': 14899, 'synset': 'korean.n.01', 'name': 'Korean'}, {'id': 14900, 'synset': 'kenyan.n.01', 'name': 'Kenyan'}, {'id': 14901, 'synset': 'lao.n.01', 'name': 'Lao'}, {'id': 14902, 'synset': 'lapp.n.01', 'name': 'Lapp'}, {'id': 14903, 'synset': 'latin_american.n.01', 'name': 'Latin_American'}, {'id': 14904, 'synset': 'lebanese.n.01', 'name': 'Lebanese'}, {'id': 14905, 'synset': 'levantine.n.01', 'name': 'Levantine'}, {'id': 14906, 'synset': 'liberian.n.01', 'name': 'Liberian'}, {'id': 14907, 'synset': 'luxemburger.n.01', 'name': 'Luxemburger'}, {'id': 14908, 'synset': 'macedonian.n.01', 'name': 'Macedonian'}, {'id': 14909, 'synset': 'sabahan.n.01', 'name': 'Sabahan'}, {'id': 14910, 'synset': 'mexican.n.01', 'name': 'Mexican'}, {'id': 14911, 'synset': 'chicano.n.01', 'name': 'Chicano'}, {'id': 14912, 'synset': 'mexican-american.n.01', 'name': 'Mexican-American'}, {'id': 14913, 'synset': 'namibian.n.01', 'name': 'Namibian'}, {'id': 14914, 'synset': 'nauruan.n.01', 'name': 'Nauruan'}, {'id': 14915, 'synset': 'gurkha.n.02', 'name': 'Gurkha'}, {'id': 14916, 'synset': 'new_zealander.n.01', 'name': 'New_Zealander'}, {'id': 14917, 'synset': 'nicaraguan.n.01', 'name': 'Nicaraguan'}, {'id': 14918, 'synset': 'nigerian.n.01', 'name': 'Nigerian'}, {'id': 14919, 'synset': 'hausa.n.01', 'name': 'Hausa'}, {'id': 14920, 'synset': 'north_american.n.01', 'name': 'North_American'}, {'id': 14921, 'synset': 'nova_scotian.n.01', 'name': 'Nova_Scotian'}, {'id': 14922, 'synset': 'omani.n.01', 'name': 'Omani'}, {'id': 14923, 'synset': 'pakistani.n.01', 'name': 'Pakistani'}, {'id': 14924, 'synset': 'brahui.n.01', 'name': 'Brahui'}, {'id': 14925, 'synset': 'south_american_indian.n.01', 'name': 'South_American_Indian'}, {'id': 14926, 'synset': 'carib.n.01', 'name': 'Carib'}, {'id': 14927, 'synset': 'filipino.n.01', 'name': 'Filipino'}, {'id': 14928, 'synset': 'polynesian.n.01', 'name': 'Polynesian'}, {'id': 14929, 'synset': 'qatari.n.01', 'name': 'Qatari'}, {'id': 14930, 'synset': 'romanian.n.01', 'name': 'Romanian'}, {'id': 14931, 'synset': 'muscovite.n.02', 'name': 'Muscovite'}, {'id': 14932, 'synset': 'georgian.n.02', 'name': 'Georgian'}, {'id': 14933, 'synset': 'sarawakian.n.01', 'name': 'Sarawakian'}, {'id': 14934, 'synset': 'scandinavian.n.01', 'name': 'Scandinavian'}, {'id': 14935, 'synset': 'senegalese.n.01', 'name': 'Senegalese'}, {'id': 14936, 'synset': 'slovene.n.01', 'name': 'Slovene'}, {'id': 14937, 'synset': 'south_african.n.01', 'name': 'South_African'}, {'id': 14938, 'synset': 'south_american.n.01', 'name': 'South_American'}, {'id': 14939, 'synset': 'sudanese.n.01', 'name': 'Sudanese'}, {'id': 14940, 'synset': 'syrian.n.01', 'name': 'Syrian'}, {'id': 14941, 'synset': 'tahitian.n.01', 'name': 'Tahitian'}, {'id': 14942, 'synset': 'tanzanian.n.01', 'name': 'Tanzanian'}, {'id': 14943, 'synset': 'tibetan.n.02', 'name': 'Tibetan'}, {'id': 14944, 'synset': 'togolese.n.01', 'name': 'Togolese'}, {'id': 14945, 'synset': 'tuareg.n.01', 'name': 'Tuareg'}, {'id': 14946, 'synset': 'turki.n.01', 'name': 'Turki'}, {'id': 14947, 'synset': 'chuvash.n.01', 'name': 'Chuvash'}, {'id': 14948, 'synset': 'turkoman.n.01', 'name': 'Turkoman'}, {'id': 14949, 'synset': 'uzbek.n.01', 'name': 'Uzbek'}, {'id': 14950, 'synset': 'ugandan.n.01', 'name': 'Ugandan'}, {'id': 14951, 'synset': 'ukranian.n.01', 'name': 'Ukranian'}, {'id': 14952, 'synset': 'yakut.n.01', 'name': 'Yakut'}, {'id': 14953, 'synset': 'tungus.n.01', 'name': 'Tungus'}, {'id': 14954, 'synset': 'igbo.n.01', 'name': 'Igbo'}, {'id': 14955, 'synset': 'american.n.03', 'name': 'American'}, {'id': 14956, 'synset': 'anglo-american.n.01', 'name': 'Anglo-American'}, {'id': 14957, 'synset': 'alaska_native.n.01', 'name': 'Alaska_Native'}, {'id': 14958, 'synset': 'arkansan.n.01', 'name': 'Arkansan'}, {'id': 14959, 'synset': 'carolinian.n.01', 'name': 'Carolinian'}, {'id': 14960, 'synset': 'coloradan.n.01', 'name': 'Coloradan'}, {'id': 14961, 'synset': 'connecticuter.n.01', 'name': 'Connecticuter'}, {'id': 14962, 'synset': 'delawarean.n.01', 'name': 'Delawarean'}, {'id': 14963, 'synset': 'floridian.n.01', 'name': 'Floridian'}, {'id': 14964, 'synset': 'german_american.n.01', 'name': 'German_American'}, {'id': 14965, 'synset': 'illinoisan.n.01', 'name': 'Illinoisan'}, {'id': 14966, 'synset': 'mainer.n.01', 'name': 'Mainer'}, {'id': 14967, 'synset': 'marylander.n.01', 'name': 'Marylander'}, {'id': 14968, 'synset': 'minnesotan.n.01', 'name': 'Minnesotan'}, {'id': 14969, 'synset': 'nebraskan.n.01', 'name': 'Nebraskan'}, {'id': 14970, 'synset': 'new_hampshirite.n.01', 'name': 'New_Hampshirite'}, {'id': 14971, 'synset': 'new_jerseyan.n.01', 'name': 'New_Jerseyan'}, {'id': 14972, 'synset': 'new_yorker.n.01', 'name': 'New_Yorker'}, {'id': 14973, 'synset': 'north_carolinian.n.01', 'name': 'North_Carolinian'}, {'id': 14974, 'synset': 'oregonian.n.01', 'name': 'Oregonian'}, {'id': 14975, 'synset': 'pennsylvanian.n.02', 'name': 'Pennsylvanian'}, {'id': 14976, 'synset': 'texan.n.01', 'name': 'Texan'}, {'id': 14977, 'synset': 'utahan.n.01', 'name': 'Utahan'}, {'id': 14978, 'synset': 'uruguayan.n.01', 'name': 'Uruguayan'}, {'id': 14979, 'synset': 'vietnamese.n.01', 'name': 'Vietnamese'}, {'id': 14980, 'synset': 'gambian.n.01', 'name': 'Gambian'}, {'id': 14981, 'synset': 'east_german.n.01', 'name': 'East_German'}, {'id': 14982, 'synset': 'berliner.n.01', 'name': 'Berliner'}, {'id': 14983, 'synset': 'prussian.n.01', 'name': 'Prussian'}, {'id': 14984, 'synset': 'ghanian.n.01', 'name': 'Ghanian'}, {'id': 14985, 'synset': 'guinean.n.01', 'name': 'Guinean'}, {'id': 14986, 'synset': 'papuan.n.01', 'name': 'Papuan'}, {'id': 14987, 'synset': 'walloon.n.01', 'name': 'Walloon'}, {'id': 14988, 'synset': 'yemeni.n.01', 'name': 'Yemeni'}, {'id': 14989, 'synset': 'yugoslav.n.01', 'name': 'Yugoslav'}, {'id': 14990, 'synset': 'serbian.n.01', 'name': 'Serbian'}, {'id': 14991, 'synset': 'xhosa.n.01', 'name': 'Xhosa'}, {'id': 14992, 'synset': 'zairese.n.01', 'name': 'Zairese'}, {'id': 14993, 'synset': 'zimbabwean.n.01', 'name': 'Zimbabwean'}, {'id': 14994, 'synset': 'zulu.n.01', 'name': 'Zulu'}, {'id': 14995, 'synset': 'gemini.n.01', 'name': 'Gemini'}, {'id': 14996, 'synset': 'sagittarius.n.01', 'name': 'Sagittarius'}, {'id': 14997, 'synset': 'pisces.n.02', 'name': 'Pisces'}, {'id': 14998, 'synset': 'abbe.n.01', 'name': 'abbe'}, {'id': 14999, 'synset': 'abbess.n.01', 'name': 'abbess'}, {'id': 15000, 'synset': 'abnegator.n.01', 'name': 'abnegator'}, {'id': 15001, 'synset': 'abridger.n.01', 'name': 'abridger'}, {'id': 15002, 'synset': 'abstractor.n.01', 'name': 'abstractor'}, {'id': 15003, 'synset': 'absconder.n.01', 'name': 'absconder'}, {'id': 15004, 'synset': 'absolver.n.01', 'name': 'absolver'}, {'id': 15005, 'synset': 'abecedarian.n.01', 'name': 'abecedarian'}, {'id': 15006, 'synset': 'aberrant.n.01', 'name': 'aberrant'}, {'id': 15007, 'synset': 'abettor.n.01', 'name': 'abettor'}, {'id': 15008, 'synset': 'abhorrer.n.01', 'name': 'abhorrer'}, {'id': 15009, 'synset': 'abomination.n.01', 'name': 'abomination'}, {'id': 15010, 'synset': 'abseiler.n.01', 'name': 'abseiler'}, {'id': 15011, 'synset': 'abstainer.n.01', 'name': 'abstainer'}, {'id': 15012, 'synset': 'academic_administrator.n.01', 'name': 'academic_administrator'}, {'id': 15013, 'synset': 'academician.n.01', 'name': 'academician'}, {'id': 15014, 'synset': 'accessory_before_the_fact.n.01', 'name': 'accessory_before_the_fact'}, {'id': 15015, 'synset': 'companion.n.03', 'name': 'companion'}, {'id': 15016, 'synset': 'accompanist.n.01', 'name': 'accompanist'}, {'id': 15017, 'synset': 'accomplice.n.01', 'name': 'accomplice'}, {'id': 15018, 'synset': 'account_executive.n.01', 'name': 'account_executive'}, {'id': 15019, 'synset': 'accused.n.01', 'name': 'accused'}, {'id': 15020, 'synset': 'accuser.n.01', 'name': 'accuser'}, {'id': 15021, 'synset': 'acid_head.n.01', 'name': 'acid_head'}, {'id': 15022, 'synset': 'acquaintance.n.03', 'name': 'acquaintance'}, {'id': 15023, 'synset': 'acquirer.n.01', 'name': 'acquirer'}, {'id': 15024, 'synset': 'aerialist.n.01', 'name': 'aerialist'}, {'id': 15025, 'synset': 'action_officer.n.01', 'name': 'action_officer'}, {'id': 15026, 'synset': 'active.n.03', 'name': 'active'}, {'id': 15027, 'synset': 'active_citizen.n.01', 'name': 'active_citizen'}, {'id': 15028, 'synset': 'actor.n.01', 'name': 'actor'}, {'id': 15029, 'synset': 'actor.n.02', 'name': 'actor'}, {'id': 15030, 'synset': 'addict.n.01', 'name': 'addict'}, {'id': 15031, 'synset': 'adducer.n.01', 'name': 'adducer'}, {'id': 15032, 'synset': 'adjuster.n.01', 'name': 'adjuster'}, {'id': 15033, 'synset': 'adjutant.n.01', 'name': 'adjutant'}, {'id': 15034, 'synset': 'adjutant_general.n.01', 'name': 'adjutant_general'}, {'id': 15035, 'synset': 'admirer.n.03', 'name': 'admirer'}, {'id': 15036, 'synset': 'adoptee.n.01', 'name': 'adoptee'}, {'id': 15037, 'synset': 'adulterer.n.01', 'name': 'adulterer'}, {'id': 15038, 'synset': 'adulteress.n.01', 'name': 'adulteress'}, {'id': 15039, 'synset': 'advertiser.n.01', 'name': 'advertiser'}, {'id': 15040, 'synset': 'advisee.n.01', 'name': 'advisee'}, {'id': 15041, 'synset': 'advocate.n.01', 'name': 'advocate'}, {'id': 15042, 'synset': 'aeronautical_engineer.n.01', 'name': 'aeronautical_engineer'}, {'id': 15043, 'synset': 'affiliate.n.01', 'name': 'affiliate'}, {'id': 15044, 'synset': 'affluent.n.01', 'name': 'affluent'}, {'id': 15045, 'synset': 'aficionado.n.02', 'name': 'aficionado'}, {'id': 15046, 'synset': 'buck_sergeant.n.01', 'name': 'buck_sergeant'}, {'id': 15047, 'synset': 'agent-in-place.n.01', 'name': 'agent-in-place'}, {'id': 15048, 'synset': 'aggravator.n.01', 'name': 'aggravator'}, {'id': 15049, 'synset': 'agitator.n.01', 'name': 'agitator'}, {'id': 15050, 'synset': 'agnostic.n.02', 'name': 'agnostic'}, {'id': 15051, 'synset': 'agnostic.n.01', 'name': 'agnostic'}, {'id': 15052, 'synset': 'agonist.n.02', 'name': 'agonist'}, {'id': 15053, 'synset': 'agony_aunt.n.01', 'name': 'agony_aunt'}, {'id': 15054, 'synset': 'agriculturist.n.01', 'name': 'agriculturist'}, {'id': 15055, 'synset': 'air_attache.n.01', 'name': 'air_attache'}, {'id': 15056, 'synset': 'air_force_officer.n.01', 'name': 'air_force_officer'}, {'id': 15057, 'synset': 'airhead.n.01', 'name': 'airhead'}, {'id': 15058, 'synset': 'air_traveler.n.01', 'name': 'air_traveler'}, {'id': 15059, 'synset': 'alarmist.n.01', 'name': 'alarmist'}, {'id': 15060, 'synset': 'albino.n.01', 'name': 'albino'}, {'id': 15061, 'synset': 'alcoholic.n.01', 'name': 'alcoholic'}, {'id': 15062, 'synset': 'alderman.n.01', 'name': 'alderman'}, {'id': 15063, 'synset': 'alexic.n.01', 'name': 'alexic'}, {'id': 15064, 'synset': 'alienee.n.01', 'name': 'alienee'}, {'id': 15065, 'synset': 'alienor.n.01', 'name': 'alienor'}, {'id': 15066, 'synset': 'aliterate.n.01', 'name': 'aliterate'}, {'id': 15067, 'synset': 'algebraist.n.01', 'name': 'algebraist'}, {'id': 15068, 'synset': 'allegorizer.n.01', 'name': 'allegorizer'}, {'id': 15069, 'synset': 'alliterator.n.01', 'name': 'alliterator'}, {'id': 15070, 'synset': 'almoner.n.01', 'name': 'almoner'}, {'id': 15071, 'synset': 'alpinist.n.01', 'name': 'alpinist'}, {'id': 15072, 'synset': 'altar_boy.n.01', 'name': 'altar_boy'}, {'id': 15073, 'synset': 'alto.n.01', 'name': 'alto'}, {'id': 15074, 'synset': 'ambassador.n.01', 'name': 'ambassador'}, {'id': 15075, 'synset': 'ambassador.n.02', 'name': 'ambassador'}, {'id': 15076, 'synset': 'ambusher.n.01', 'name': 'ambusher'}, {'id': 15077, 'synset': 'amicus_curiae.n.01', 'name': 'amicus_curiae'}, {'id': 15078, 'synset': 'amoralist.n.01', 'name': 'amoralist'}, {'id': 15079, 'synset': 'amputee.n.01', 'name': 'amputee'}, {'id': 15080, 'synset': 'analogist.n.01', 'name': 'analogist'}, {'id': 15081, 'synset': 'analphabet.n.01', 'name': 'analphabet'}, {'id': 15082, 'synset': 'analyst.n.01', 'name': 'analyst'}, {'id': 15083, 'synset': 'industry_analyst.n.01', 'name': 'industry_analyst'}, {'id': 15084, 'synset': 'market_strategist.n.01', 'name': 'market_strategist'}, {'id': 15085, 'synset': 'anarchist.n.01', 'name': 'anarchist'}, {'id': 15086, 'synset': 'anathema.n.01', 'name': 'anathema'}, {'id': 15087, 'synset': 'ancestor.n.01', 'name': 'ancestor'}, {'id': 15088, 'synset': 'anchor.n.03', 'name': 'anchor'}, {'id': 15089, 'synset': 'ancient.n.02', 'name': 'ancient'}, {'id': 15090, 'synset': 'anecdotist.n.01', 'name': 'anecdotist'}, {'id': 15091, 'synset': 'angler.n.02', 'name': 'angler'}, {'id': 15092, 'synset': 'animator.n.02', 'name': 'animator'}, {'id': 15093, 'synset': 'animist.n.01', 'name': 'animist'}, {'id': 15094, 'synset': 'annotator.n.01', 'name': 'annotator'}, {'id': 15095, 'synset': 'announcer.n.02', 'name': 'announcer'}, {'id': 15096, 'synset': 'announcer.n.01', 'name': 'announcer'}, {'id': 15097, 'synset': 'anti.n.01', 'name': 'anti'}, {'id': 15098, 'synset': 'anti-american.n.01', 'name': 'anti-American'}, {'id': 15099, 'synset': 'anti-semite.n.01', 'name': 'anti-Semite'}, {'id': 15100, 'synset': 'anzac.n.01', 'name': 'Anzac'}, {'id': 15101, 'synset': 'ape-man.n.02', 'name': 'ape-man'}, {'id': 15102, 'synset': 'aphakic.n.01', 'name': 'aphakic'}, {'id': 15103, 'synset': 'appellant.n.01', 'name': 'appellant'}, {'id': 15104, 'synset': 'appointee.n.01', 'name': 'appointee'}, {'id': 15105, 'synset': 'apprehender.n.02', 'name': 'apprehender'}, {'id': 15106, 'synset': 'april_fool.n.01', 'name': 'April_fool'}, {'id': 15107, 'synset': 'aspirant.n.01', 'name': 'aspirant'}, {'id': 15108, 'synset': 'appreciator.n.01', 'name': 'appreciator'}, {'id': 15109, 'synset': 'appropriator.n.01', 'name': 'appropriator'}, {'id': 15110, 'synset': 'arabist.n.01', 'name': 'Arabist'}, {'id': 15111, 'synset': 'archaist.n.01', 'name': 'archaist'}, {'id': 15112, 'synset': 'archbishop.n.01', 'name': 'archbishop'}, {'id': 15113, 'synset': 'archer.n.01', 'name': 'archer'}, {'id': 15114, 'synset': 'architect.n.01', 'name': 'architect'}, {'id': 15115, 'synset': 'archivist.n.01', 'name': 'archivist'}, {'id': 15116, 'synset': 'archpriest.n.01', 'name': 'archpriest'}, {'id': 15117, 'synset': 'aristotelian.n.01', 'name': 'Aristotelian'}, {'id': 15118, 'synset': 'armiger.n.02', 'name': 'armiger'}, {'id': 15119, 'synset': 'army_attache.n.01', 'name': 'army_attache'}, {'id': 15120, 'synset': 'army_engineer.n.01', 'name': 'army_engineer'}, {'id': 15121, 'synset': 'army_officer.n.01', 'name': 'army_officer'}, {'id': 15122, 'synset': 'arranger.n.02', 'name': 'arranger'}, {'id': 15123, 'synset': 'arrival.n.03', 'name': 'arrival'}, {'id': 15124, 'synset': 'arthritic.n.01', 'name': 'arthritic'}, {'id': 15125, 'synset': 'articulator.n.01', 'name': 'articulator'}, {'id': 15126, 'synset': 'artilleryman.n.01', 'name': 'artilleryman'}, {'id': 15127, 'synset': "artist's_model.n.01", 'name': "artist's_model"}, {'id': 15128, 'synset': 'assayer.n.01', 'name': 'assayer'}, {'id': 15129, 'synset': 'assemblyman.n.01', 'name': 'assemblyman'}, {'id': 15130, 'synset': 'assemblywoman.n.01', 'name': 'assemblywoman'}, {'id': 15131, 'synset': 'assenter.n.01', 'name': 'assenter'}, {'id': 15132, 'synset': 'asserter.n.01', 'name': 'asserter'}, {'id': 15133, 'synset': 'assignee.n.01', 'name': 'assignee'}, {'id': 15134, 'synset': 'assistant.n.01', 'name': 'assistant'}, {'id': 15135, 'synset': 'assistant_professor.n.01', 'name': 'assistant_professor'}, {'id': 15136, 'synset': 'associate.n.01', 'name': 'associate'}, {'id': 15137, 'synset': 'associate.n.03', 'name': 'associate'}, {'id': 15138, 'synset': 'associate_professor.n.01', 'name': 'associate_professor'}, {'id': 15139, 'synset': 'astronaut.n.01', 'name': 'astronaut'}, {'id': 15140, 'synset': 'cosmographer.n.01', 'name': 'cosmographer'}, {'id': 15141, 'synset': 'atheist.n.01', 'name': 'atheist'}, {'id': 15142, 'synset': 'athlete.n.01', 'name': 'athlete'}, {'id': 15143, 'synset': 'attendant.n.01', 'name': 'attendant'}, {'id': 15144, 'synset': 'attorney_general.n.01', 'name': 'attorney_general'}, {'id': 15145, 'synset': 'auditor.n.02', 'name': 'auditor'}, {'id': 15146, 'synset': 'augur.n.01', 'name': 'augur'}, {'id': 15147, 'synset': 'aunt.n.01', 'name': 'aunt'}, {'id': 15148, 'synset': 'au_pair_girl.n.01', 'name': 'au_pair_girl'}, {'id': 15149, 'synset': 'authoritarian.n.01', 'name': 'authoritarian'}, {'id': 15150, 'synset': 'authority.n.02', 'name': 'authority'}, {'id': 15151, 'synset': 'authorizer.n.01', 'name': 'authorizer'}, {'id': 15152, 'synset': 'automobile_mechanic.n.01', 'name': 'automobile_mechanic'}, {'id': 15153, 'synset': 'aviator.n.01', 'name': 'aviator'}, {'id': 15154, 'synset': 'aviatrix.n.01', 'name': 'aviatrix'}, {'id': 15155, 'synset': 'ayah.n.01', 'name': 'ayah'}, {'id': 15156, 'synset': 'babu.n.01', 'name': 'babu'}, {'id': 15157, 'synset': 'baby.n.05', 'name': 'baby'}, {'id': 15158, 'synset': 'baby.n.04', 'name': 'baby'}, {'id': 15159, 'synset': 'baby_boomer.n.01', 'name': 'baby_boomer'}, {'id': 15160, 'synset': 'baby_farmer.n.01', 'name': 'baby_farmer'}, {'id': 15161, 'synset': 'back.n.04', 'name': 'back'}, {'id': 15162, 'synset': 'backbencher.n.01', 'name': 'backbencher'}, {'id': 15163, 'synset': 'backpacker.n.01', 'name': 'backpacker'}, {'id': 15164, 'synset': 'backroom_boy.n.01', 'name': 'backroom_boy'}, {'id': 15165, 'synset': 'backscratcher.n.01', 'name': 'backscratcher'}, {'id': 15166, 'synset': 'bad_person.n.01', 'name': 'bad_person'}, {'id': 15167, 'synset': 'baggage.n.02', 'name': 'baggage'}, {'id': 15168, 'synset': 'bag_lady.n.01', 'name': 'bag_lady'}, {'id': 15169, 'synset': 'bailee.n.01', 'name': 'bailee'}, {'id': 15170, 'synset': 'bailiff.n.01', 'name': 'bailiff'}, {'id': 15171, 'synset': 'bailor.n.01', 'name': 'bailor'}, {'id': 15172, 'synset': 'bairn.n.01', 'name': 'bairn'}, {'id': 15173, 'synset': 'baker.n.02', 'name': 'baker'}, {'id': 15174, 'synset': 'balancer.n.01', 'name': 'balancer'}, {'id': 15175, 'synset': 'balker.n.01', 'name': 'balker'}, {'id': 15176, 'synset': 'ball-buster.n.01', 'name': 'ball-buster'}, {'id': 15177, 'synset': 'ball_carrier.n.01', 'name': 'ball_carrier'}, {'id': 15178, 'synset': 'ballet_dancer.n.01', 'name': 'ballet_dancer'}, {'id': 15179, 'synset': 'ballet_master.n.01', 'name': 'ballet_master'}, {'id': 15180, 'synset': 'ballet_mistress.n.01', 'name': 'ballet_mistress'}, {'id': 15181, 'synset': 'balletomane.n.01', 'name': 'balletomane'}, {'id': 15182, 'synset': 'ball_hawk.n.01', 'name': 'ball_hawk'}, {'id': 15183, 'synset': 'balloonist.n.01', 'name': 'balloonist'}, {'id': 15184, 'synset': 'ballplayer.n.01', 'name': 'ballplayer'}, {'id': 15185, 'synset': 'bullfighter.n.01', 'name': 'bullfighter'}, {'id': 15186, 'synset': 'banderillero.n.01', 'name': 'banderillero'}, {'id': 15187, 'synset': 'matador.n.01', 'name': 'matador'}, {'id': 15188, 'synset': 'picador.n.01', 'name': 'picador'}, {'id': 15189, 'synset': 'bandsman.n.01', 'name': 'bandsman'}, {'id': 15190, 'synset': 'banker.n.02', 'name': 'banker'}, {'id': 15191, 'synset': 'bank_robber.n.01', 'name': 'bank_robber'}, {'id': 15192, 'synset': 'bankrupt.n.01', 'name': 'bankrupt'}, {'id': 15193, 'synset': 'bantamweight.n.01', 'name': 'bantamweight'}, {'id': 15194, 'synset': 'barmaid.n.01', 'name': 'barmaid'}, {'id': 15195, 'synset': 'baron.n.03', 'name': 'baron'}, {'id': 15196, 'synset': 'baron.n.02', 'name': 'baron'}, {'id': 15197, 'synset': 'baron.n.01', 'name': 'baron'}, {'id': 15198, 'synset': 'bartender.n.01', 'name': 'bartender'}, {'id': 15199, 'synset': 'baseball_coach.n.01', 'name': 'baseball_coach'}, {'id': 15200, 'synset': 'base_runner.n.01', 'name': 'base_runner'}, {'id': 15201, 'synset': 'basketball_player.n.01', 'name': 'basketball_player'}, {'id': 15202, 'synset': 'basketweaver.n.01', 'name': 'basketweaver'}, {'id': 15203, 'synset': 'basket_maker.n.01', 'name': 'Basket_Maker'}, {'id': 15204, 'synset': 'bass.n.03', 'name': 'bass'}, {'id': 15205, 'synset': 'bastard.n.02', 'name': 'bastard'}, {'id': 15206, 'synset': 'bat_boy.n.01', 'name': 'bat_boy'}, {'id': 15207, 'synset': 'bather.n.02', 'name': 'bather'}, {'id': 15208, 'synset': 'batman.n.01', 'name': 'batman'}, {'id': 15209, 'synset': 'baton_twirler.n.01', 'name': 'baton_twirler'}, {'id': 15210, 'synset': 'bavarian.n.01', 'name': 'Bavarian'}, {'id': 15211, 'synset': 'beadsman.n.01', 'name': 'beadsman'}, {'id': 15212, 'synset': 'beard.n.03', 'name': 'beard'}, {'id': 15213, 'synset': 'beatnik.n.01', 'name': 'beatnik'}, {'id': 15214, 'synset': 'beauty_consultant.n.01', 'name': 'beauty_consultant'}, {'id': 15215, 'synset': 'bedouin.n.01', 'name': 'Bedouin'}, {'id': 15216, 'synset': 'bedwetter.n.01', 'name': 'bedwetter'}, {'id': 15217, 'synset': 'beekeeper.n.01', 'name': 'beekeeper'}, {'id': 15218, 'synset': 'beer_drinker.n.01', 'name': 'beer_drinker'}, {'id': 15219, 'synset': 'beggarman.n.01', 'name': 'beggarman'}, {'id': 15220, 'synset': 'beggarwoman.n.01', 'name': 'beggarwoman'}, {'id': 15221, 'synset': 'beldam.n.02', 'name': 'beldam'}, {'id': 15222, 'synset': 'theist.n.01', 'name': 'theist'}, {'id': 15223, 'synset': 'believer.n.01', 'name': 'believer'}, {'id': 15224, 'synset': 'bell_founder.n.01', 'name': 'bell_founder'}, {'id': 15225, 'synset': 'benedick.n.01', 'name': 'benedick'}, {'id': 15226, 'synset': 'berserker.n.01', 'name': 'berserker'}, {'id': 15227, 'synset': 'besieger.n.01', 'name': 'besieger'}, {'id': 15228, 'synset': 'best.n.02', 'name': 'best'}, {'id': 15229, 'synset': 'betrothed.n.01', 'name': 'betrothed'}, {'id': 15230, 'synset': 'big_brother.n.01', 'name': 'Big_Brother'}, {'id': 15231, 'synset': 'bigot.n.01', 'name': 'bigot'}, {'id': 15232, 'synset': 'big_shot.n.01', 'name': 'big_shot'}, {'id': 15233, 'synset': 'big_sister.n.01', 'name': 'big_sister'}, {'id': 15234, 'synset': 'billiard_player.n.01', 'name': 'billiard_player'}, {'id': 15235, 'synset': 'biochemist.n.01', 'name': 'biochemist'}, {'id': 15236, 'synset': 'biographer.n.01', 'name': 'biographer'}, {'id': 15237, 'synset': 'bird_fancier.n.01', 'name': 'bird_fancier'}, {'id': 15238, 'synset': 'birth.n.05', 'name': 'birth'}, {'id': 15239, 'synset': 'birth-control_campaigner.n.01', 'name': 'birth-control_campaigner'}, {'id': 15240, 'synset': 'bisexual.n.01', 'name': 'bisexual'}, {'id': 15241, 'synset': 'black_belt.n.01', 'name': 'black_belt'}, {'id': 15242, 'synset': 'blackmailer.n.01', 'name': 'blackmailer'}, {'id': 15243, 'synset': 'black_muslim.n.01', 'name': 'Black_Muslim'}, {'id': 15244, 'synset': 'blacksmith.n.01', 'name': 'blacksmith'}, {'id': 15245, 'synset': 'blade.n.02', 'name': 'blade'}, {'id': 15246, 'synset': 'blind_date.n.01', 'name': 'blind_date'}, {'id': 15247, 'synset': 'bluecoat.n.01', 'name': 'bluecoat'}, {'id': 15248, 'synset': 'bluestocking.n.01', 'name': 'bluestocking'}, {'id': 15249, 'synset': 'boatbuilder.n.01', 'name': 'boatbuilder'}, {'id': 15250, 'synset': 'boatman.n.01', 'name': 'boatman'}, {'id': 15251, 'synset': 'boatswain.n.01', 'name': 'boatswain'}, {'id': 15252, 'synset': 'bobby.n.01', 'name': 'bobby'}, {'id': 15253, 'synset': 'bodyguard.n.01', 'name': 'bodyguard'}, {'id': 15254, 'synset': 'boffin.n.01', 'name': 'boffin'}, {'id': 15255, 'synset': 'bolshevik.n.01', 'name': 'Bolshevik'}, {'id': 15256, 'synset': 'bolshevik.n.02', 'name': 'Bolshevik'}, {'id': 15257, 'synset': 'bombshell.n.01', 'name': 'bombshell'}, {'id': 15258, 'synset': 'bondman.n.01', 'name': 'bondman'}, {'id': 15259, 'synset': 'bondwoman.n.02', 'name': 'bondwoman'}, {'id': 15260, 'synset': 'bondwoman.n.01', 'name': 'bondwoman'}, {'id': 15261, 'synset': 'bond_servant.n.01', 'name': 'bond_servant'}, {'id': 15262, 'synset': 'book_agent.n.01', 'name': 'book_agent'}, {'id': 15263, 'synset': 'bookbinder.n.01', 'name': 'bookbinder'}, {'id': 15264, 'synset': 'bookkeeper.n.01', 'name': 'bookkeeper'}, {'id': 15265, 'synset': 'bookmaker.n.01', 'name': 'bookmaker'}, {'id': 15266, 'synset': 'bookworm.n.02', 'name': 'bookworm'}, {'id': 15267, 'synset': 'booster.n.03', 'name': 'booster'}, {'id': 15268, 'synset': 'bootblack.n.01', 'name': 'bootblack'}, {'id': 15269, 'synset': 'bootlegger.n.01', 'name': 'bootlegger'}, {'id': 15270, 'synset': 'bootmaker.n.01', 'name': 'bootmaker'}, {'id': 15271, 'synset': 'borderer.n.01', 'name': 'borderer'}, {'id': 15272, 'synset': 'border_patrolman.n.01', 'name': 'border_patrolman'}, {'id': 15273, 'synset': 'botanist.n.01', 'name': 'botanist'}, {'id': 15274, 'synset': 'bottom_feeder.n.01', 'name': 'bottom_feeder'}, {'id': 15275, 'synset': 'boulevardier.n.01', 'name': 'boulevardier'}, {'id': 15276, 'synset': 'bounty_hunter.n.02', 'name': 'bounty_hunter'}, {'id': 15277, 'synset': 'bounty_hunter.n.01', 'name': 'bounty_hunter'}, {'id': 15278, 'synset': 'bourbon.n.03', 'name': 'Bourbon'}, {'id': 15279, 'synset': 'bowler.n.01', 'name': 'bowler'}, {'id': 15280, 'synset': 'slugger.n.02', 'name': 'slugger'}, {'id': 15281, 'synset': 'cub.n.02', 'name': 'cub'}, {'id': 15282, 'synset': 'boy_scout.n.01', 'name': 'Boy_Scout'}, {'id': 15283, 'synset': 'boy_scout.n.02', 'name': 'boy_scout'}, {'id': 15284, 'synset': 'boy_wonder.n.01', 'name': 'boy_wonder'}, {'id': 15285, 'synset': 'bragger.n.01', 'name': 'bragger'}, {'id': 15286, 'synset': 'brahman.n.02', 'name': 'brahman'}, {'id': 15287, 'synset': 'brawler.n.01', 'name': 'brawler'}, {'id': 15288, 'synset': 'breadwinner.n.01', 'name': 'breadwinner'}, {'id': 15289, 'synset': 'breaststroker.n.01', 'name': 'breaststroker'}, {'id': 15290, 'synset': 'breeder.n.01', 'name': 'breeder'}, {'id': 15291, 'synset': 'brick.n.02', 'name': 'brick'}, {'id': 15292, 'synset': 'bride.n.03', 'name': 'bride'}, {'id': 15293, 'synset': 'bridesmaid.n.01', 'name': 'bridesmaid'}, {'id': 15294, 'synset': 'bridge_agent.n.01', 'name': 'bridge_agent'}, {'id': 15295, 'synset': 'broadcast_journalist.n.01', 'name': 'broadcast_journalist'}, {'id': 15296, 'synset': 'brother.n.05', 'name': 'Brother'}, {'id': 15297, 'synset': 'brother-in-law.n.01', 'name': 'brother-in-law'}, {'id': 15298, 'synset': 'browser.n.01', 'name': 'browser'}, {'id': 15299, 'synset': 'brummie.n.01', 'name': 'Brummie'}, {'id': 15300, 'synset': 'buddy.n.01', 'name': 'buddy'}, {'id': 15301, 'synset': 'bull.n.06', 'name': 'bull'}, {'id': 15302, 'synset': 'bully.n.02', 'name': 'bully'}, {'id': 15303, 'synset': 'bunny.n.01', 'name': 'bunny'}, {'id': 15304, 'synset': 'burglar.n.01', 'name': 'burglar'}, {'id': 15305, 'synset': 'bursar.n.01', 'name': 'bursar'}, {'id': 15306, 'synset': 'busboy.n.01', 'name': 'busboy'}, {'id': 15307, 'synset': 'business_editor.n.01', 'name': 'business_editor'}, {'id': 15308, 'synset': 'business_traveler.n.01', 'name': 'business_traveler'}, {'id': 15309, 'synset': 'buster.n.04', 'name': 'buster'}, {'id': 15310, 'synset': 'busybody.n.01', 'name': 'busybody'}, {'id': 15311, 'synset': 'buttinsky.n.01', 'name': 'buttinsky'}, {'id': 15312, 'synset': 'cabinetmaker.n.01', 'name': 'cabinetmaker'}, {'id': 15313, 'synset': 'caddie.n.01', 'name': 'caddie'}, {'id': 15314, 'synset': 'cadet.n.01', 'name': 'cadet'}, {'id': 15315, 'synset': 'caller.n.04', 'name': 'caller'}, {'id': 15316, 'synset': 'call_girl.n.01', 'name': 'call_girl'}, {'id': 15317, 'synset': 'calligrapher.n.01', 'name': 'calligrapher'}, {'id': 15318, 'synset': 'campaigner.n.01', 'name': 'campaigner'}, {'id': 15319, 'synset': 'camper.n.01', 'name': 'camper'}, {'id': 15320, 'synset': 'camp_follower.n.02', 'name': 'camp_follower'}, {'id': 15321, 'synset': 'candidate.n.02', 'name': 'candidate'}, {'id': 15322, 'synset': 'canonist.n.01', 'name': 'canonist'}, {'id': 15323, 'synset': 'capitalist.n.01', 'name': 'capitalist'}, {'id': 15324, 'synset': 'captain.n.07', 'name': 'captain'}, {'id': 15325, 'synset': 'captain.n.06', 'name': 'captain'}, {'id': 15326, 'synset': 'captain.n.01', 'name': 'captain'}, {'id': 15327, 'synset': 'captain.n.05', 'name': 'captain'}, {'id': 15328, 'synset': 'captive.n.02', 'name': 'captive'}, {'id': 15329, 'synset': 'captive.n.03', 'name': 'captive'}, {'id': 15330, 'synset': 'cardinal.n.01', 'name': 'cardinal'}, {'id': 15331, 'synset': 'cardiologist.n.01', 'name': 'cardiologist'}, {'id': 15332, 'synset': 'card_player.n.01', 'name': 'card_player'}, {'id': 15333, 'synset': 'cardsharp.n.01', 'name': 'cardsharp'}, {'id': 15334, 'synset': 'careerist.n.01', 'name': 'careerist'}, {'id': 15335, 'synset': 'career_man.n.01', 'name': 'career_man'}, {'id': 15336, 'synset': 'caregiver.n.02', 'name': 'caregiver'}, {'id': 15337, 'synset': 'caretaker.n.01', 'name': 'caretaker'}, {'id': 15338, 'synset': 'caretaker.n.02', 'name': 'caretaker'}, {'id': 15339, 'synset': 'caricaturist.n.01', 'name': 'caricaturist'}, {'id': 15340, 'synset': 'carillonneur.n.01', 'name': 'carillonneur'}, {'id': 15341, 'synset': 'caroler.n.01', 'name': 'caroler'}, {'id': 15342, 'synset': 'carpenter.n.01', 'name': 'carpenter'}, {'id': 15343, 'synset': 'carper.n.01', 'name': 'carper'}, {'id': 15344, 'synset': 'cartesian.n.01', 'name': 'Cartesian'}, {'id': 15345, 'synset': 'cashier.n.02', 'name': 'cashier'}, {'id': 15346, 'synset': 'casualty.n.02', 'name': 'casualty'}, {'id': 15347, 'synset': 'casualty.n.01', 'name': 'casualty'}, {'id': 15348, 'synset': 'casuist.n.01', 'name': 'casuist'}, {'id': 15349, 'synset': 'catechist.n.01', 'name': 'catechist'}, {'id': 15350, 'synset': 'catechumen.n.01', 'name': 'catechumen'}, {'id': 15351, 'synset': 'caterer.n.01', 'name': 'caterer'}, {'id': 15352, 'synset': 'catholicos.n.01', 'name': 'Catholicos'}, {'id': 15353, 'synset': 'cat_fancier.n.01', 'name': 'cat_fancier'}, {'id': 15354, 'synset': 'cavalier.n.02', 'name': 'Cavalier'}, {'id': 15355, 'synset': 'cavalryman.n.02', 'name': 'cavalryman'}, {'id': 15356, 'synset': 'caveman.n.01', 'name': 'caveman'}, {'id': 15357, 'synset': 'celebrant.n.02', 'name': 'celebrant'}, {'id': 15358, 'synset': 'celebrant.n.01', 'name': 'celebrant'}, {'id': 15359, 'synset': 'celebrity.n.01', 'name': 'celebrity'}, {'id': 15360, 'synset': 'cellist.n.01', 'name': 'cellist'}, {'id': 15361, 'synset': 'censor.n.02', 'name': 'censor'}, {'id': 15362, 'synset': 'censor.n.01', 'name': 'censor'}, {'id': 15363, 'synset': 'centenarian.n.01', 'name': 'centenarian'}, {'id': 15364, 'synset': 'centrist.n.01', 'name': 'centrist'}, {'id': 15365, 'synset': 'centurion.n.01', 'name': 'centurion'}, {'id': 15366, 'synset': 'certified_public_accountant.n.01', 'name': 'certified_public_accountant'}, {'id': 15367, 'synset': 'chachka.n.01', 'name': 'chachka'}, {'id': 15368, 'synset': 'chambermaid.n.01', 'name': 'chambermaid'}, {'id': 15369, 'synset': 'chameleon.n.01', 'name': 'chameleon'}, {'id': 15370, 'synset': 'champion.n.01', 'name': 'champion'}, {'id': 15371, 'synset': 'chandler.n.02', 'name': 'chandler'}, {'id': 15372, 'synset': 'prison_chaplain.n.01', 'name': 'prison_chaplain'}, {'id': 15373, 'synset': 'charcoal_burner.n.01', 'name': 'charcoal_burner'}, {'id': 15374, 'synset': "charge_d'affaires.n.01", 'name': "charge_d'affaires"}, {'id': 15375, 'synset': 'charioteer.n.01', 'name': 'charioteer'}, {'id': 15376, 'synset': 'charmer.n.02', 'name': 'charmer'}, {'id': 15377, 'synset': 'chartered_accountant.n.01', 'name': 'chartered_accountant'}, {'id': 15378, 'synset': 'chartist.n.02', 'name': 'chartist'}, {'id': 15379, 'synset': 'charwoman.n.01', 'name': 'charwoman'}, {'id': 15380, 'synset': 'male_chauvinist.n.01', 'name': 'male_chauvinist'}, {'id': 15381, 'synset': 'cheapskate.n.01', 'name': 'cheapskate'}, {'id': 15382, 'synset': 'chechen.n.01', 'name': 'Chechen'}, {'id': 15383, 'synset': 'checker.n.02', 'name': 'checker'}, {'id': 15384, 'synset': 'cheerer.n.01', 'name': 'cheerer'}, {'id': 15385, 'synset': 'cheerleader.n.02', 'name': 'cheerleader'}, {'id': 15386, 'synset': 'cheerleader.n.01', 'name': 'cheerleader'}, {'id': 15387, 'synset': 'cheops.n.01', 'name': 'Cheops'}, {'id': 15388, 'synset': 'chess_master.n.01', 'name': 'chess_master'}, {'id': 15389, 'synset': 'chief_executive_officer.n.01', 'name': 'chief_executive_officer'}, {'id': 15390, 'synset': 'chief_of_staff.n.01', 'name': 'chief_of_staff'}, {'id': 15391, 'synset': 'chief_petty_officer.n.01', 'name': 'chief_petty_officer'}, {'id': 15392, 'synset': 'chief_secretary.n.01', 'name': 'Chief_Secretary'}, {'id': 15393, 'synset': 'child.n.01', 'name': 'child'}, {'id': 15394, 'synset': 'child.n.02', 'name': 'child'}, {'id': 15395, 'synset': 'child.n.03', 'name': 'child'}, {'id': 15396, 'synset': 'child_prodigy.n.01', 'name': 'child_prodigy'}, {'id': 15397, 'synset': 'chimneysweeper.n.01', 'name': 'chimneysweeper'}, {'id': 15398, 'synset': 'chiropractor.n.01', 'name': 'chiropractor'}, {'id': 15399, 'synset': 'chit.n.01', 'name': 'chit'}, {'id': 15400, 'synset': 'choker.n.02', 'name': 'choker'}, {'id': 15401, 'synset': 'choragus.n.01', 'name': 'choragus'}, {'id': 15402, 'synset': 'choreographer.n.01', 'name': 'choreographer'}, {'id': 15403, 'synset': 'chorus_girl.n.01', 'name': 'chorus_girl'}, {'id': 15404, 'synset': 'chosen.n.01', 'name': 'chosen'}, {'id': 15405, 'synset': 'cicerone.n.01', 'name': 'cicerone'}, {'id': 15406, 'synset': 'cigar_smoker.n.01', 'name': 'cigar_smoker'}, {'id': 15407, 'synset': 'cipher.n.04', 'name': 'cipher'}, {'id': 15408, 'synset': 'circus_acrobat.n.01', 'name': 'circus_acrobat'}, {'id': 15409, 'synset': 'citizen.n.01', 'name': 'citizen'}, {'id': 15410, 'synset': 'city_editor.n.01', 'name': 'city_editor'}, {'id': 15411, 'synset': 'city_father.n.01', 'name': 'city_father'}, {'id': 15412, 'synset': 'city_man.n.01', 'name': 'city_man'}, {'id': 15413, 'synset': 'city_slicker.n.01', 'name': 'city_slicker'}, {'id': 15414, 'synset': 'civic_leader.n.01', 'name': 'civic_leader'}, {'id': 15415, 'synset': 'civil_rights_leader.n.01', 'name': 'civil_rights_leader'}, {'id': 15416, 'synset': 'cleaner.n.03', 'name': 'cleaner'}, {'id': 15417, 'synset': 'clergyman.n.01', 'name': 'clergyman'}, {'id': 15418, 'synset': 'cleric.n.01', 'name': 'cleric'}, {'id': 15419, 'synset': 'clerk.n.01', 'name': 'clerk'}, {'id': 15420, 'synset': 'clever_dick.n.01', 'name': 'clever_Dick'}, {'id': 15421, 'synset': 'climatologist.n.01', 'name': 'climatologist'}, {'id': 15422, 'synset': 'climber.n.04', 'name': 'climber'}, {'id': 15423, 'synset': 'clinician.n.01', 'name': 'clinician'}, {'id': 15424, 'synset': 'closer.n.02', 'name': 'closer'}, {'id': 15425, 'synset': 'closet_queen.n.01', 'name': 'closet_queen'}, {'id': 15426, 'synset': 'clown.n.02', 'name': 'clown'}, {'id': 15427, 'synset': 'clown.n.01', 'name': 'clown'}, {'id': 15428, 'synset': 'coach.n.02', 'name': 'coach'}, {'id': 15429, 'synset': 'coach.n.01', 'name': 'coach'}, {'id': 15430, 'synset': 'pitching_coach.n.01', 'name': 'pitching_coach'}, {'id': 15431, 'synset': 'coachman.n.01', 'name': 'coachman'}, {'id': 15432, 'synset': 'coal_miner.n.01', 'name': 'coal_miner'}, {'id': 15433, 'synset': 'coastguardsman.n.01', 'name': 'coastguardsman'}, {'id': 15434, 'synset': 'cobber.n.01', 'name': 'cobber'}, {'id': 15435, 'synset': 'cobbler.n.01', 'name': 'cobbler'}, {'id': 15436, 'synset': 'codger.n.01', 'name': 'codger'}, {'id': 15437, 'synset': 'co-beneficiary.n.01', 'name': 'co-beneficiary'}, {'id': 15438, 'synset': 'cog.n.01', 'name': 'cog'}, {'id': 15439, 'synset': 'cognitive_neuroscientist.n.01', 'name': 'cognitive_neuroscientist'}, {'id': 15440, 'synset': 'coiffeur.n.01', 'name': 'coiffeur'}, {'id': 15441, 'synset': 'coiner.n.02', 'name': 'coiner'}, {'id': 15442, 'synset': 'collaborator.n.03', 'name': 'collaborator'}, {'id': 15443, 'synset': 'colleen.n.01', 'name': 'colleen'}, {'id': 15444, 'synset': 'college_student.n.01', 'name': 'college_student'}, {'id': 15445, 'synset': 'collegian.n.01', 'name': 'collegian'}, {'id': 15446, 'synset': 'colonial.n.01', 'name': 'colonial'}, {'id': 15447, 'synset': 'colonialist.n.01', 'name': 'colonialist'}, {'id': 15448, 'synset': 'colonizer.n.01', 'name': 'colonizer'}, {'id': 15449, 'synset': 'coloratura.n.01', 'name': 'coloratura'}, {'id': 15450, 'synset': 'color_guard.n.01', 'name': 'color_guard'}, {'id': 15451, 'synset': 'colossus.n.02', 'name': 'colossus'}, {'id': 15452, 'synset': 'comedian.n.02', 'name': 'comedian'}, {'id': 15453, 'synset': 'comedienne.n.02', 'name': 'comedienne'}, {'id': 15454, 'synset': 'comer.n.01', 'name': 'comer'}, {'id': 15455, 'synset': 'commander.n.03', 'name': 'commander'}, {'id': 15456, 'synset': 'commander_in_chief.n.01', 'name': 'commander_in_chief'}, {'id': 15457, 'synset': 'commanding_officer.n.01', 'name': 'commanding_officer'}, {'id': 15458, 'synset': 'commissar.n.01', 'name': 'commissar'}, {'id': 15459, 'synset': 'commissioned_officer.n.01', 'name': 'commissioned_officer'}, {'id': 15460, 'synset': 'commissioned_military_officer.n.01', 'name': 'commissioned_military_officer'}, {'id': 15461, 'synset': 'commissioner.n.01', 'name': 'commissioner'}, {'id': 15462, 'synset': 'commissioner.n.02', 'name': 'commissioner'}, {'id': 15463, 'synset': 'committee_member.n.01', 'name': 'committee_member'}, {'id': 15464, 'synset': 'committeewoman.n.01', 'name': 'committeewoman'}, {'id': 15465, 'synset': 'commodore.n.01', 'name': 'commodore'}, {'id': 15466, 'synset': 'communicant.n.01', 'name': 'communicant'}, {'id': 15467, 'synset': 'communist.n.02', 'name': 'communist'}, {'id': 15468, 'synset': 'communist.n.01', 'name': 'Communist'}, {'id': 15469, 'synset': 'commuter.n.02', 'name': 'commuter'}, {'id': 15470, 'synset': 'compere.n.01', 'name': 'compere'}, {'id': 15471, 'synset': 'complexifier.n.01', 'name': 'complexifier'}, {'id': 15472, 'synset': 'compulsive.n.01', 'name': 'compulsive'}, {'id': 15473, 'synset': 'computational_linguist.n.01', 'name': 'computational_linguist'}, {'id': 15474, 'synset': 'computer_scientist.n.01', 'name': 'computer_scientist'}, {'id': 15475, 'synset': 'computer_user.n.01', 'name': 'computer_user'}, {'id': 15476, 'synset': 'comrade.n.02', 'name': 'Comrade'}, {'id': 15477, 'synset': 'concert-goer.n.01', 'name': 'concert-goer'}, {'id': 15478, 'synset': 'conciliator.n.01', 'name': 'conciliator'}, {'id': 15479, 'synset': 'conductor.n.03', 'name': 'conductor'}, {'id': 15480, 'synset': 'confectioner.n.01', 'name': 'confectioner'}, {'id': 15481, 'synset': 'confederate.n.01', 'name': 'Confederate'}, {'id': 15482, 'synset': 'confessor.n.01', 'name': 'confessor'}, {'id': 15483, 'synset': 'confidant.n.01', 'name': 'confidant'}, {'id': 15484, 'synset': 'confucian.n.01', 'name': 'Confucian'}, {'id': 15485, 'synset': 'rep.n.01', 'name': 'rep'}, {'id': 15486, 'synset': 'conqueror.n.01', 'name': 'conqueror'}, {'id': 15487, 'synset': 'conservative.n.02', 'name': 'Conservative'}, {'id': 15488, 'synset': 'nonconformist.n.01', 'name': 'Nonconformist'}, {'id': 15489, 'synset': 'anglican.n.01', 'name': 'Anglican'}, {'id': 15490, 'synset': 'consignee.n.01', 'name': 'consignee'}, {'id': 15491, 'synset': 'consigner.n.01', 'name': 'consigner'}, {'id': 15492, 'synset': 'constable.n.01', 'name': 'constable'}, {'id': 15493, 'synset': 'constructivist.n.01', 'name': 'constructivist'}, {'id': 15494, 'synset': 'contractor.n.01', 'name': 'contractor'}, {'id': 15495, 'synset': 'contralto.n.01', 'name': 'contralto'}, {'id': 15496, 'synset': 'contributor.n.02', 'name': 'contributor'}, {'id': 15497, 'synset': 'control_freak.n.01', 'name': 'control_freak'}, {'id': 15498, 'synset': 'convalescent.n.01', 'name': 'convalescent'}, {'id': 15499, 'synset': 'convener.n.01', 'name': 'convener'}, {'id': 15500, 'synset': 'convict.n.01', 'name': 'convict'}, {'id': 15501, 'synset': 'copilot.n.01', 'name': 'copilot'}, {'id': 15502, 'synset': 'copycat.n.01', 'name': 'copycat'}, {'id': 15503, 'synset': 'coreligionist.n.01', 'name': 'coreligionist'}, {'id': 15504, 'synset': 'cornerback.n.01', 'name': 'cornerback'}, {'id': 15505, 'synset': 'corporatist.n.01', 'name': 'corporatist'}, {'id': 15506, 'synset': 'correspondent.n.01', 'name': 'correspondent'}, {'id': 15507, 'synset': 'cosmetician.n.01', 'name': 'cosmetician'}, {'id': 15508, 'synset': 'cosmopolitan.n.01', 'name': 'cosmopolitan'}, {'id': 15509, 'synset': 'cossack.n.01', 'name': 'Cossack'}, {'id': 15510, 'synset': 'cost_accountant.n.01', 'name': 'cost_accountant'}, {'id': 15511, 'synset': 'co-star.n.01', 'name': 'co-star'}, {'id': 15512, 'synset': 'costumier.n.01', 'name': 'costumier'}, {'id': 15513, 'synset': 'cotter.n.02', 'name': 'cotter'}, {'id': 15514, 'synset': 'cotter.n.01', 'name': 'cotter'}, {'id': 15515, 'synset': 'counselor.n.01', 'name': 'counselor'}, {'id': 15516, 'synset': 'counterterrorist.n.01', 'name': 'counterterrorist'}, {'id': 15517, 'synset': 'counterspy.n.01', 'name': 'counterspy'}, {'id': 15518, 'synset': 'countess.n.01', 'name': 'countess'}, {'id': 15519, 'synset': 'compromiser.n.01', 'name': 'compromiser'}, {'id': 15520, 'synset': 'countrywoman.n.01', 'name': 'countrywoman'}, {'id': 15521, 'synset': 'county_agent.n.01', 'name': 'county_agent'}, {'id': 15522, 'synset': 'courtier.n.01', 'name': 'courtier'}, {'id': 15523, 'synset': 'cousin.n.01', 'name': 'cousin'}, {'id': 15524, 'synset': 'cover_girl.n.01', 'name': 'cover_girl'}, {'id': 15525, 'synset': 'cow.n.03', 'name': 'cow'}, {'id': 15526, 'synset': 'craftsman.n.03', 'name': 'craftsman'}, {'id': 15527, 'synset': 'craftsman.n.02', 'name': 'craftsman'}, {'id': 15528, 'synset': 'crapshooter.n.01', 'name': 'crapshooter'}, {'id': 15529, 'synset': 'crazy.n.01', 'name': 'crazy'}, {'id': 15530, 'synset': 'creature.n.02', 'name': 'creature'}, {'id': 15531, 'synset': 'creditor.n.01', 'name': 'creditor'}, {'id': 15532, 'synset': 'creep.n.01', 'name': 'creep'}, {'id': 15533, 'synset': 'criminologist.n.01', 'name': 'criminologist'}, {'id': 15534, 'synset': 'critic.n.02', 'name': 'critic'}, {'id': 15535, 'synset': 'croesus.n.02', 'name': 'Croesus'}, {'id': 15536, 'synset': 'cross-examiner.n.01', 'name': 'cross-examiner'}, {'id': 15537, 'synset': 'crossover_voter.n.01', 'name': 'crossover_voter'}, {'id': 15538, 'synset': 'croupier.n.01', 'name': 'croupier'}, {'id': 15539, 'synset': 'crown_prince.n.01', 'name': 'crown_prince'}, {'id': 15540, 'synset': 'crown_princess.n.01', 'name': 'crown_princess'}, {'id': 15541, 'synset': 'cryptanalyst.n.01', 'name': 'cryptanalyst'}, {'id': 15542, 'synset': 'cub_scout.n.01', 'name': 'Cub_Scout'}, {'id': 15543, 'synset': 'cuckold.n.01', 'name': 'cuckold'}, {'id': 15544, 'synset': 'cultist.n.02', 'name': 'cultist'}, {'id': 15545, 'synset': 'curandera.n.01', 'name': 'curandera'}, {'id': 15546, 'synset': 'curate.n.01', 'name': 'curate'}, {'id': 15547, 'synset': 'curator.n.01', 'name': 'curator'}, {'id': 15548, 'synset': 'customer_agent.n.01', 'name': 'customer_agent'}, {'id': 15549, 'synset': 'cutter.n.02', 'name': 'cutter'}, {'id': 15550, 'synset': 'cyberpunk.n.02', 'name': 'cyberpunk'}, {'id': 15551, 'synset': 'cyborg.n.01', 'name': 'cyborg'}, {'id': 15552, 'synset': 'cymbalist.n.01', 'name': 'cymbalist'}, {'id': 15553, 'synset': 'cynic.n.02', 'name': 'Cynic'}, {'id': 15554, 'synset': 'cytogeneticist.n.01', 'name': 'cytogeneticist'}, {'id': 15555, 'synset': 'cytologist.n.01', 'name': 'cytologist'}, {'id': 15556, 'synset': 'czar.n.02', 'name': 'czar'}, {'id': 15557, 'synset': 'czar.n.01', 'name': 'czar'}, {'id': 15558, 'synset': 'dad.n.01', 'name': 'dad'}, {'id': 15559, 'synset': 'dairyman.n.02', 'name': 'dairyman'}, {'id': 15560, 'synset': 'dalai_lama.n.01', 'name': 'Dalai_Lama'}, {'id': 15561, 'synset': 'dallier.n.01', 'name': 'dallier'}, {'id': 15562, 'synset': 'dancer.n.01', 'name': 'dancer'}, {'id': 15563, 'synset': 'dancer.n.02', 'name': 'dancer'}, {'id': 15564, 'synset': 'clog_dancer.n.01', 'name': 'clog_dancer'}, {'id': 15565, 'synset': 'dancing-master.n.01', 'name': 'dancing-master'}, {'id': 15566, 'synset': 'dark_horse.n.01', 'name': 'dark_horse'}, {'id': 15567, 'synset': 'darling.n.01', 'name': 'darling'}, {'id': 15568, 'synset': 'date.n.02', 'name': 'date'}, {'id': 15569, 'synset': 'daughter.n.01', 'name': 'daughter'}, {'id': 15570, 'synset': 'dawdler.n.01', 'name': 'dawdler'}, {'id': 15571, 'synset': 'day_boarder.n.01', 'name': 'day_boarder'}, {'id': 15572, 'synset': 'day_laborer.n.01', 'name': 'day_laborer'}, {'id': 15573, 'synset': 'deacon.n.01', 'name': 'deacon'}, {'id': 15574, 'synset': 'deaconess.n.01', 'name': 'deaconess'}, {'id': 15575, 'synset': 'deadeye.n.01', 'name': 'deadeye'}, {'id': 15576, 'synset': 'deipnosophist.n.01', 'name': 'deipnosophist'}, {'id': 15577, 'synset': 'dropout.n.02', 'name': 'dropout'}, {'id': 15578, 'synset': 'deadhead.n.01', 'name': 'deadhead'}, {'id': 15579, 'synset': 'deaf_person.n.01', 'name': 'deaf_person'}, {'id': 15580, 'synset': 'debtor.n.01', 'name': 'debtor'}, {'id': 15581, 'synset': 'deckhand.n.01', 'name': 'deckhand'}, {'id': 15582, 'synset': 'defamer.n.01', 'name': 'defamer'}, {'id': 15583, 'synset': 'defense_contractor.n.01', 'name': 'defense_contractor'}, {'id': 15584, 'synset': 'deist.n.01', 'name': 'deist'}, {'id': 15585, 'synset': 'delegate.n.01', 'name': 'delegate'}, {'id': 15586, 'synset': 'deliveryman.n.01', 'name': 'deliveryman'}, {'id': 15587, 'synset': 'demagogue.n.01', 'name': 'demagogue'}, {'id': 15588, 'synset': 'demigod.n.01', 'name': 'demigod'}, {'id': 15589, 'synset': 'demographer.n.01', 'name': 'demographer'}, {'id': 15590, 'synset': 'demonstrator.n.03', 'name': 'demonstrator'}, {'id': 15591, 'synset': 'den_mother.n.02', 'name': 'den_mother'}, {'id': 15592, 'synset': 'department_head.n.01', 'name': 'department_head'}, {'id': 15593, 'synset': 'depositor.n.01', 'name': 'depositor'}, {'id': 15594, 'synset': 'deputy.n.03', 'name': 'deputy'}, {'id': 15595, 'synset': 'dermatologist.n.01', 'name': 'dermatologist'}, {'id': 15596, 'synset': 'descender.n.01', 'name': 'descender'}, {'id': 15597, 'synset': 'designated_hitter.n.01', 'name': 'designated_hitter'}, {'id': 15598, 'synset': 'designer.n.04', 'name': 'designer'}, {'id': 15599, 'synset': 'desk_clerk.n.01', 'name': 'desk_clerk'}, {'id': 15600, 'synset': 'desk_officer.n.01', 'name': 'desk_officer'}, {'id': 15601, 'synset': 'desk_sergeant.n.01', 'name': 'desk_sergeant'}, {'id': 15602, 'synset': 'detainee.n.01', 'name': 'detainee'}, {'id': 15603, 'synset': 'detective.n.01', 'name': 'detective'}, {'id': 15604, 'synset': 'detective.n.02', 'name': 'detective'}, {'id': 15605, 'synset': 'detractor.n.01', 'name': 'detractor'}, {'id': 15606, 'synset': 'developer.n.01', 'name': 'developer'}, {'id': 15607, 'synset': 'deviationist.n.01', 'name': 'deviationist'}, {'id': 15608, 'synset': 'devisee.n.01', 'name': 'devisee'}, {'id': 15609, 'synset': 'devisor.n.01', 'name': 'devisor'}, {'id': 15610, 'synset': 'devourer.n.01', 'name': 'devourer'}, {'id': 15611, 'synset': 'dialectician.n.01', 'name': 'dialectician'}, {'id': 15612, 'synset': 'diarist.n.01', 'name': 'diarist'}, {'id': 15613, 'synset': 'dietician.n.01', 'name': 'dietician'}, {'id': 15614, 'synset': 'diocesan.n.01', 'name': 'diocesan'}, {'id': 15615, 'synset': 'director.n.03', 'name': 'director'}, {'id': 15616, 'synset': 'director.n.02', 'name': 'director'}, {'id': 15617, 'synset': 'dirty_old_man.n.01', 'name': 'dirty_old_man'}, {'id': 15618, 'synset': 'disbeliever.n.01', 'name': 'disbeliever'}, {'id': 15619, 'synset': 'disk_jockey.n.01', 'name': 'disk_jockey'}, {'id': 15620, 'synset': 'dispatcher.n.02', 'name': 'dispatcher'}, {'id': 15621, 'synset': 'distortionist.n.01', 'name': 'distortionist'}, {'id': 15622, 'synset': 'distributor.n.01', 'name': 'distributor'}, {'id': 15623, 'synset': 'district_attorney.n.01', 'name': 'district_attorney'}, {'id': 15624, 'synset': 'district_manager.n.01', 'name': 'district_manager'}, {'id': 15625, 'synset': 'diver.n.02', 'name': 'diver'}, {'id': 15626, 'synset': 'divorcee.n.01', 'name': 'divorcee'}, {'id': 15627, 'synset': 'ex-wife.n.01', 'name': 'ex-wife'}, {'id': 15628, 'synset': 'divorce_lawyer.n.01', 'name': 'divorce_lawyer'}, {'id': 15629, 'synset': 'docent.n.01', 'name': 'docent'}, {'id': 15630, 'synset': 'doctor.n.01', 'name': 'doctor'}, {'id': 15631, 'synset': 'dodo.n.01', 'name': 'dodo'}, {'id': 15632, 'synset': 'doge.n.01', 'name': 'doge'}, {'id': 15633, 'synset': 'dog_in_the_manger.n.01', 'name': 'dog_in_the_manger'}, {'id': 15634, 'synset': 'dogmatist.n.01', 'name': 'dogmatist'}, {'id': 15635, 'synset': 'dolichocephalic.n.01', 'name': 'dolichocephalic'}, {'id': 15636, 'synset': 'domestic_partner.n.01', 'name': 'domestic_partner'}, {'id': 15637, 'synset': 'dominican.n.02', 'name': 'Dominican'}, {'id': 15638, 'synset': 'dominus.n.01', 'name': 'dominus'}, {'id': 15639, 'synset': 'don.n.03', 'name': 'don'}, {'id': 15640, 'synset': 'donatist.n.01', 'name': 'Donatist'}, {'id': 15641, 'synset': 'donna.n.01', 'name': 'donna'}, {'id': 15642, 'synset': 'dosser.n.01', 'name': 'dosser'}, {'id': 15643, 'synset': 'double.n.03', 'name': 'double'}, {'id': 15644, 'synset': 'double-crosser.n.01', 'name': 'double-crosser'}, {'id': 15645, 'synset': 'down-and-out.n.01', 'name': 'down-and-out'}, {'id': 15646, 'synset': 'doyenne.n.01', 'name': 'doyenne'}, {'id': 15647, 'synset': 'draftsman.n.02', 'name': 'draftsman'}, {'id': 15648, 'synset': 'dramatist.n.01', 'name': 'dramatist'}, {'id': 15649, 'synset': 'dreamer.n.01', 'name': 'dreamer'}, {'id': 15650, 'synset': 'dressmaker.n.01', 'name': 'dressmaker'}, {'id': 15651, 'synset': "dressmaker's_model.n.01", 'name': "dressmaker's_model"}, {'id': 15652, 'synset': 'dribbler.n.02', 'name': 'dribbler'}, {'id': 15653, 'synset': 'dribbler.n.01', 'name': 'dribbler'}, {'id': 15654, 'synset': 'drinker.n.02', 'name': 'drinker'}, {'id': 15655, 'synset': 'drinker.n.01', 'name': 'drinker'}, {'id': 15656, 'synset': 'drug_addict.n.01', 'name': 'drug_addict'}, {'id': 15657, 'synset': 'drug_user.n.01', 'name': 'drug_user'}, {'id': 15658, 'synset': 'druid.n.01', 'name': 'Druid'}, {'id': 15659, 'synset': 'drum_majorette.n.02', 'name': 'drum_majorette'}, {'id': 15660, 'synset': 'drummer.n.01', 'name': 'drummer'}, {'id': 15661, 'synset': 'drunk.n.02', 'name': 'drunk'}, {'id': 15662, 'synset': 'drunkard.n.01', 'name': 'drunkard'}, {'id': 15663, 'synset': 'druze.n.01', 'name': 'Druze'}, {'id': 15664, 'synset': 'dry.n.01', 'name': 'dry'}, {'id': 15665, 'synset': 'dry_nurse.n.01', 'name': 'dry_nurse'}, {'id': 15666, 'synset': 'duchess.n.01', 'name': 'duchess'}, {'id': 15667, 'synset': 'duke.n.01', 'name': 'duke'}, {'id': 15668, 'synset': 'duffer.n.01', 'name': 'duffer'}, {'id': 15669, 'synset': 'dunker.n.02', 'name': 'dunker'}, {'id': 15670, 'synset': 'dutch_uncle.n.01', 'name': 'Dutch_uncle'}, {'id': 15671, 'synset': 'dyspeptic.n.01', 'name': 'dyspeptic'}, {'id': 15672, 'synset': 'eager_beaver.n.01', 'name': 'eager_beaver'}, {'id': 15673, 'synset': 'earl.n.01', 'name': 'earl'}, {'id': 15674, 'synset': 'earner.n.01', 'name': 'earner'}, {'id': 15675, 'synset': 'eavesdropper.n.01', 'name': 'eavesdropper'}, {'id': 15676, 'synset': 'eccentric.n.01', 'name': 'eccentric'}, {'id': 15677, 'synset': 'eclectic.n.01', 'name': 'eclectic'}, {'id': 15678, 'synset': 'econometrician.n.01', 'name': 'econometrician'}, {'id': 15679, 'synset': 'economist.n.01', 'name': 'economist'}, {'id': 15680, 'synset': 'ectomorph.n.01', 'name': 'ectomorph'}, {'id': 15681, 'synset': 'editor.n.01', 'name': 'editor'}, {'id': 15682, 'synset': 'egocentric.n.01', 'name': 'egocentric'}, {'id': 15683, 'synset': 'egotist.n.01', 'name': 'egotist'}, {'id': 15684, 'synset': 'ejaculator.n.01', 'name': 'ejaculator'}, {'id': 15685, 'synset': 'elder.n.03', 'name': 'elder'}, {'id': 15686, 'synset': 'elder_statesman.n.01', 'name': 'elder_statesman'}, {'id': 15687, 'synset': 'elected_official.n.01', 'name': 'elected_official'}, {'id': 15688, 'synset': 'electrician.n.01', 'name': 'electrician'}, {'id': 15689, 'synset': 'elegist.n.01', 'name': 'elegist'}, {'id': 15690, 'synset': 'elocutionist.n.01', 'name': 'elocutionist'}, {'id': 15691, 'synset': 'emancipator.n.01', 'name': 'emancipator'}, {'id': 15692, 'synset': 'embryologist.n.01', 'name': 'embryologist'}, {'id': 15693, 'synset': 'emeritus.n.01', 'name': 'emeritus'}, {'id': 15694, 'synset': 'emigrant.n.01', 'name': 'emigrant'}, {'id': 15695, 'synset': 'emissary.n.01', 'name': 'emissary'}, {'id': 15696, 'synset': 'empress.n.01', 'name': 'empress'}, {'id': 15697, 'synset': 'employee.n.01', 'name': 'employee'}, {'id': 15698, 'synset': 'employer.n.01', 'name': 'employer'}, {'id': 15699, 'synset': 'enchantress.n.02', 'name': 'enchantress'}, {'id': 15700, 'synset': 'enchantress.n.01', 'name': 'enchantress'}, {'id': 15701, 'synset': 'encyclopedist.n.01', 'name': 'encyclopedist'}, {'id': 15702, 'synset': 'endomorph.n.01', 'name': 'endomorph'}, {'id': 15703, 'synset': 'enemy.n.02', 'name': 'enemy'}, {'id': 15704, 'synset': 'energizer.n.01', 'name': 'energizer'}, {'id': 15705, 'synset': 'end_man.n.02', 'name': 'end_man'}, {'id': 15706, 'synset': 'end_man.n.01', 'name': 'end_man'}, {'id': 15707, 'synset': 'endorser.n.02', 'name': 'endorser'}, {'id': 15708, 'synset': 'enjoyer.n.01', 'name': 'enjoyer'}, {'id': 15709, 'synset': 'enlisted_woman.n.01', 'name': 'enlisted_woman'}, {'id': 15710, 'synset': 'enophile.n.01', 'name': 'enophile'}, {'id': 15711, 'synset': 'entrant.n.04', 'name': 'entrant'}, {'id': 15712, 'synset': 'entrant.n.03', 'name': 'entrant'}, {'id': 15713, 'synset': 'entrepreneur.n.01', 'name': 'entrepreneur'}, {'id': 15714, 'synset': 'envoy.n.01', 'name': 'envoy'}, {'id': 15715, 'synset': 'enzymologist.n.01', 'name': 'enzymologist'}, {'id': 15716, 'synset': 'eparch.n.01', 'name': 'eparch'}, {'id': 15717, 'synset': 'epidemiologist.n.01', 'name': 'epidemiologist'}, {'id': 15718, 'synset': 'epigone.n.01', 'name': 'epigone'}, {'id': 15719, 'synset': 'epileptic.n.01', 'name': 'epileptic'}, {'id': 15720, 'synset': 'episcopalian.n.01', 'name': 'Episcopalian'}, {'id': 15721, 'synset': 'equerry.n.02', 'name': 'equerry'}, {'id': 15722, 'synset': 'equerry.n.01', 'name': 'equerry'}, {'id': 15723, 'synset': 'erotic.n.01', 'name': 'erotic'}, {'id': 15724, 'synset': 'escapee.n.01', 'name': 'escapee'}, {'id': 15725, 'synset': 'escapist.n.01', 'name': 'escapist'}, {'id': 15726, 'synset': 'eskimo.n.01', 'name': 'Eskimo'}, {'id': 15727, 'synset': 'espionage_agent.n.01', 'name': 'espionage_agent'}, {'id': 15728, 'synset': 'esthetician.n.01', 'name': 'esthetician'}, {'id': 15729, 'synset': 'etcher.n.01', 'name': 'etcher'}, {'id': 15730, 'synset': 'ethnologist.n.01', 'name': 'ethnologist'}, {'id': 15731, 'synset': 'etonian.n.01', 'name': 'Etonian'}, {'id': 15732, 'synset': 'etymologist.n.01', 'name': 'etymologist'}, {'id': 15733, 'synset': 'evangelist.n.01', 'name': 'evangelist'}, {'id': 15734, 'synset': 'evangelist.n.02', 'name': 'Evangelist'}, {'id': 15735, 'synset': 'event_planner.n.01', 'name': 'event_planner'}, {'id': 15736, 'synset': 'examiner.n.02', 'name': 'examiner'}, {'id': 15737, 'synset': 'examiner.n.01', 'name': 'examiner'}, {'id': 15738, 'synset': 'exarch.n.03', 'name': 'exarch'}, {'id': 15739, 'synset': 'executant.n.01', 'name': 'executant'}, {'id': 15740, 'synset': 'executive_secretary.n.01', 'name': 'executive_secretary'}, {'id': 15741, 'synset': 'executive_vice_president.n.01', 'name': 'executive_vice_president'}, {'id': 15742, 'synset': 'executrix.n.01', 'name': 'executrix'}, {'id': 15743, 'synset': 'exegete.n.01', 'name': 'exegete'}, {'id': 15744, 'synset': 'exhibitor.n.01', 'name': 'exhibitor'}, {'id': 15745, 'synset': 'exhibitionist.n.02', 'name': 'exhibitionist'}, {'id': 15746, 'synset': 'exile.n.01', 'name': 'exile'}, {'id': 15747, 'synset': 'existentialist.n.01', 'name': 'existentialist'}, {'id': 15748, 'synset': 'exorcist.n.02', 'name': 'exorcist'}, {'id': 15749, 'synset': 'ex-spouse.n.01', 'name': 'ex-spouse'}, {'id': 15750, 'synset': 'extern.n.01', 'name': 'extern'}, {'id': 15751, 'synset': 'extremist.n.01', 'name': 'extremist'}, {'id': 15752, 'synset': 'extrovert.n.01', 'name': 'extrovert'}, {'id': 15753, 'synset': 'eyewitness.n.01', 'name': 'eyewitness'}, {'id': 15754, 'synset': 'facilitator.n.01', 'name': 'facilitator'}, {'id': 15755, 'synset': 'fairy_godmother.n.01', 'name': 'fairy_godmother'}, {'id': 15756, 'synset': 'falangist.n.01', 'name': 'falangist'}, {'id': 15757, 'synset': 'falconer.n.01', 'name': 'falconer'}, {'id': 15758, 'synset': 'falsifier.n.01', 'name': 'falsifier'}, {'id': 15759, 'synset': 'familiar.n.01', 'name': 'familiar'}, {'id': 15760, 'synset': 'fan.n.03', 'name': 'fan'}, {'id': 15761, 'synset': 'fanatic.n.01', 'name': 'fanatic'}, {'id': 15762, 'synset': 'fancier.n.01', 'name': 'fancier'}, {'id': 15763, 'synset': 'farm_boy.n.01', 'name': 'farm_boy'}, {'id': 15764, 'synset': 'farmer.n.01', 'name': 'farmer'}, {'id': 15765, 'synset': 'farmhand.n.01', 'name': 'farmhand'}, {'id': 15766, 'synset': 'fascist.n.01', 'name': 'fascist'}, {'id': 15767, 'synset': 'fascista.n.01', 'name': 'fascista'}, {'id': 15768, 'synset': 'fatalist.n.01', 'name': 'fatalist'}, {'id': 15769, 'synset': 'father.n.01', 'name': 'father'}, {'id': 15770, 'synset': 'father.n.03', 'name': 'Father'}, {'id': 15771, 'synset': 'father-figure.n.01', 'name': 'father-figure'}, {'id': 15772, 'synset': 'father-in-law.n.01', 'name': 'father-in-law'}, {'id': 15773, 'synset': 'fauntleroy.n.01', 'name': 'Fauntleroy'}, {'id': 15774, 'synset': 'fauve.n.01', 'name': 'Fauve'}, {'id': 15775, 'synset': 'favorite_son.n.01', 'name': 'favorite_son'}, {'id': 15776, 'synset': 'featherweight.n.03', 'name': 'featherweight'}, {'id': 15777, 'synset': 'federalist.n.02', 'name': 'federalist'}, {'id': 15778, 'synset': 'fellow_traveler.n.01', 'name': 'fellow_traveler'}, {'id': 15779, 'synset': 'female_aristocrat.n.01', 'name': 'female_aristocrat'}, {'id': 15780, 'synset': 'female_offspring.n.01', 'name': 'female_offspring'}, {'id': 15781, 'synset': 'female_child.n.01', 'name': 'female_child'}, {'id': 15782, 'synset': 'fence.n.02', 'name': 'fence'}, {'id': 15783, 'synset': 'fiance.n.01', 'name': 'fiance'}, {'id': 15784, 'synset': 'fielder.n.02', 'name': 'fielder'}, {'id': 15785, 'synset': 'field_judge.n.01', 'name': 'field_judge'}, {'id': 15786, 'synset': 'fighter_pilot.n.01', 'name': 'fighter_pilot'}, {'id': 15787, 'synset': 'filer.n.01', 'name': 'filer'}, {'id': 15788, 'synset': 'film_director.n.01', 'name': 'film_director'}, {'id': 15789, 'synset': 'finder.n.01', 'name': 'finder'}, {'id': 15790, 'synset': 'fire_chief.n.01', 'name': 'fire_chief'}, {'id': 15791, 'synset': 'fire-eater.n.03', 'name': 'fire-eater'}, {'id': 15792, 'synset': 'fire-eater.n.02', 'name': 'fire-eater'}, {'id': 15793, 'synset': 'fireman.n.04', 'name': 'fireman'}, {'id': 15794, 'synset': 'fire_marshall.n.01', 'name': 'fire_marshall'}, {'id': 15795, 'synset': 'fire_walker.n.01', 'name': 'fire_walker'}, {'id': 15796, 'synset': 'first_baseman.n.01', 'name': 'first_baseman'}, {'id': 15797, 'synset': 'firstborn.n.01', 'name': 'firstborn'}, {'id': 15798, 'synset': 'first_lady.n.02', 'name': 'first_lady'}, {'id': 15799, 'synset': 'first_lieutenant.n.01', 'name': 'first_lieutenant'}, {'id': 15800, 'synset': 'first_offender.n.01', 'name': 'first_offender'}, {'id': 15801, 'synset': 'first_sergeant.n.01', 'name': 'first_sergeant'}, {'id': 15802, 'synset': 'fishmonger.n.01', 'name': 'fishmonger'}, {'id': 15803, 'synset': 'flagellant.n.02', 'name': 'flagellant'}, {'id': 15804, 'synset': 'flag_officer.n.01', 'name': 'flag_officer'}, {'id': 15805, 'synset': 'flak_catcher.n.01', 'name': 'flak_catcher'}, {'id': 15806, 'synset': 'flanker_back.n.01', 'name': 'flanker_back'}, {'id': 15807, 'synset': 'flapper.n.01', 'name': 'flapper'}, {'id': 15808, 'synset': 'flatmate.n.01', 'name': 'flatmate'}, {'id': 15809, 'synset': 'flatterer.n.01', 'name': 'flatterer'}, {'id': 15810, 'synset': 'flibbertigibbet.n.01', 'name': 'flibbertigibbet'}, {'id': 15811, 'synset': 'flight_surgeon.n.01', 'name': 'flight_surgeon'}, {'id': 15812, 'synset': 'floorwalker.n.01', 'name': 'floorwalker'}, {'id': 15813, 'synset': 'flop.n.02', 'name': 'flop'}, {'id': 15814, 'synset': 'florentine.n.01', 'name': 'Florentine'}, {'id': 15815, 'synset': 'flower_girl.n.02', 'name': 'flower_girl'}, {'id': 15816, 'synset': 'flower_girl.n.01', 'name': 'flower_girl'}, {'id': 15817, 'synset': 'flutist.n.01', 'name': 'flutist'}, {'id': 15818, 'synset': 'fly-by-night.n.01', 'name': 'fly-by-night'}, {'id': 15819, 'synset': 'flyweight.n.02', 'name': 'flyweight'}, {'id': 15820, 'synset': 'flyweight.n.01', 'name': 'flyweight'}, {'id': 15821, 'synset': 'foe.n.02', 'name': 'foe'}, {'id': 15822, 'synset': 'folk_dancer.n.01', 'name': 'folk_dancer'}, {'id': 15823, 'synset': 'folk_poet.n.01', 'name': 'folk_poet'}, {'id': 15824, 'synset': 'follower.n.01', 'name': 'follower'}, {'id': 15825, 'synset': 'football_hero.n.01', 'name': 'football_hero'}, {'id': 15826, 'synset': 'football_player.n.01', 'name': 'football_player'}, {'id': 15827, 'synset': 'footman.n.01', 'name': 'footman'}, {'id': 15828, 'synset': 'forefather.n.01', 'name': 'forefather'}, {'id': 15829, 'synset': 'foremother.n.01', 'name': 'foremother'}, {'id': 15830, 'synset': 'foreign_agent.n.01', 'name': 'foreign_agent'}, {'id': 15831, 'synset': 'foreigner.n.02', 'name': 'foreigner'}, {'id': 15832, 'synset': 'boss.n.03', 'name': 'boss'}, {'id': 15833, 'synset': 'foreman.n.02', 'name': 'foreman'}, {'id': 15834, 'synset': 'forester.n.02', 'name': 'forester'}, {'id': 15835, 'synset': 'forewoman.n.02', 'name': 'forewoman'}, {'id': 15836, 'synset': 'forger.n.02', 'name': 'forger'}, {'id': 15837, 'synset': 'forward.n.01', 'name': 'forward'}, {'id': 15838, 'synset': 'foster-brother.n.01', 'name': 'foster-brother'}, {'id': 15839, 'synset': 'foster-father.n.01', 'name': 'foster-father'}, {'id': 15840, 'synset': 'foster-mother.n.01', 'name': 'foster-mother'}, {'id': 15841, 'synset': 'foster-sister.n.01', 'name': 'foster-sister'}, {'id': 15842, 'synset': 'foster-son.n.01', 'name': 'foster-son'}, {'id': 15843, 'synset': 'founder.n.02', 'name': 'founder'}, {'id': 15844, 'synset': 'foundress.n.01', 'name': 'foundress'}, {'id': 15845, 'synset': 'four-minute_man.n.01', 'name': 'four-minute_man'}, {'id': 15846, 'synset': 'framer.n.02', 'name': 'framer'}, {'id': 15847, 'synset': 'francophobe.n.01', 'name': 'Francophobe'}, {'id': 15848, 'synset': 'freak.n.01', 'name': 'freak'}, {'id': 15849, 'synset': 'free_agent.n.02', 'name': 'free_agent'}, {'id': 15850, 'synset': 'free_agent.n.01', 'name': 'free_agent'}, {'id': 15851, 'synset': 'freedom_rider.n.01', 'name': 'freedom_rider'}, {'id': 15852, 'synset': 'free-liver.n.01', 'name': 'free-liver'}, {'id': 15853, 'synset': 'freeloader.n.01', 'name': 'freeloader'}, {'id': 15854, 'synset': 'free_trader.n.01', 'name': 'free_trader'}, {'id': 15855, 'synset': 'freudian.n.01', 'name': 'Freudian'}, {'id': 15856, 'synset': 'friar.n.01', 'name': 'friar'}, {'id': 15857, 'synset': 'monk.n.01', 'name': 'monk'}, {'id': 15858, 'synset': 'frontierswoman.n.01', 'name': 'frontierswoman'}, {'id': 15859, 'synset': 'front_man.n.01', 'name': 'front_man'}, {'id': 15860, 'synset': 'frotteur.n.01', 'name': 'frotteur'}, {'id': 15861, 'synset': 'fucker.n.02', 'name': 'fucker'}, {'id': 15862, 'synset': 'fucker.n.01', 'name': 'fucker'}, {'id': 15863, 'synset': 'fuddy-duddy.n.01', 'name': 'fuddy-duddy'}, {'id': 15864, 'synset': 'fullback.n.01', 'name': 'fullback'}, {'id': 15865, 'synset': 'funambulist.n.01', 'name': 'funambulist'}, {'id': 15866, 'synset': 'fundamentalist.n.01', 'name': 'fundamentalist'}, {'id': 15867, 'synset': 'fundraiser.n.01', 'name': 'fundraiser'}, {'id': 15868, 'synset': 'futurist.n.01', 'name': 'futurist'}, {'id': 15869, 'synset': 'gadgeteer.n.01', 'name': 'gadgeteer'}, {'id': 15870, 'synset': 'gagman.n.02', 'name': 'gagman'}, {'id': 15871, 'synset': 'gagman.n.01', 'name': 'gagman'}, {'id': 15872, 'synset': 'gainer.n.01', 'name': 'gainer'}, {'id': 15873, 'synset': 'gal.n.03', 'name': 'gal'}, {'id': 15874, 'synset': 'galoot.n.01', 'name': 'galoot'}, {'id': 15875, 'synset': 'gambist.n.01', 'name': 'gambist'}, {'id': 15876, 'synset': 'gambler.n.01', 'name': 'gambler'}, {'id': 15877, 'synset': 'gamine.n.02', 'name': 'gamine'}, {'id': 15878, 'synset': 'garbage_man.n.01', 'name': 'garbage_man'}, {'id': 15879, 'synset': 'gardener.n.02', 'name': 'gardener'}, {'id': 15880, 'synset': 'garment_cutter.n.01', 'name': 'garment_cutter'}, {'id': 15881, 'synset': 'garroter.n.01', 'name': 'garroter'}, {'id': 15882, 'synset': 'gasman.n.01', 'name': 'gasman'}, {'id': 15883, 'synset': 'gastroenterologist.n.01', 'name': 'gastroenterologist'}, {'id': 15884, 'synset': 'gatherer.n.01', 'name': 'gatherer'}, {'id': 15885, 'synset': 'gawker.n.01', 'name': 'gawker'}, {'id': 15886, 'synset': 'gendarme.n.01', 'name': 'gendarme'}, {'id': 15887, 'synset': 'general.n.01', 'name': 'general'}, {'id': 15888, 'synset': 'generator.n.03', 'name': 'generator'}, {'id': 15889, 'synset': 'geneticist.n.01', 'name': 'geneticist'}, {'id': 15890, 'synset': 'genitor.n.01', 'name': 'genitor'}, {'id': 15891, 'synset': 'gent.n.01', 'name': 'gent'}, {'id': 15892, 'synset': 'geologist.n.01', 'name': 'geologist'}, {'id': 15893, 'synset': 'geophysicist.n.01', 'name': 'geophysicist'}, {'id': 15894, 'synset': 'ghostwriter.n.01', 'name': 'ghostwriter'}, {'id': 15895, 'synset': 'gibson_girl.n.01', 'name': 'Gibson_girl'}, {'id': 15896, 'synset': 'girl.n.01', 'name': 'girl'}, {'id': 15897, 'synset': 'girlfriend.n.02', 'name': 'girlfriend'}, {'id': 15898, 'synset': 'girlfriend.n.01', 'name': 'girlfriend'}, {'id': 15899, 'synset': 'girl_wonder.n.01', 'name': 'girl_wonder'}, {'id': 15900, 'synset': 'girondist.n.01', 'name': 'Girondist'}, {'id': 15901, 'synset': 'gitano.n.01', 'name': 'gitano'}, {'id': 15902, 'synset': 'gladiator.n.01', 'name': 'gladiator'}, {'id': 15903, 'synset': 'glassblower.n.01', 'name': 'glassblower'}, {'id': 15904, 'synset': 'gleaner.n.02', 'name': 'gleaner'}, {'id': 15905, 'synset': 'goat_herder.n.01', 'name': 'goat_herder'}, {'id': 15906, 'synset': 'godchild.n.01', 'name': 'godchild'}, {'id': 15907, 'synset': 'godfather.n.01', 'name': 'godfather'}, {'id': 15908, 'synset': 'godparent.n.01', 'name': 'godparent'}, {'id': 15909, 'synset': 'godson.n.01', 'name': 'godson'}, {'id': 15910, 'synset': 'gofer.n.01', 'name': 'gofer'}, {'id': 15911, 'synset': 'goffer.n.01', 'name': 'goffer'}, {'id': 15912, 'synset': 'goldsmith.n.01', 'name': 'goldsmith'}, {'id': 15913, 'synset': 'golfer.n.01', 'name': 'golfer'}, {'id': 15914, 'synset': 'gondolier.n.01', 'name': 'gondolier'}, {'id': 15915, 'synset': 'good_guy.n.01', 'name': 'good_guy'}, {'id': 15916, 'synset': 'good_old_boy.n.01', 'name': 'good_old_boy'}, {'id': 15917, 'synset': 'good_samaritan.n.01', 'name': 'good_Samaritan'}, {'id': 15918, 'synset': 'gossip_columnist.n.01', 'name': 'gossip_columnist'}, {'id': 15919, 'synset': 'gouger.n.01', 'name': 'gouger'}, {'id': 15920, 'synset': 'governor_general.n.01', 'name': 'governor_general'}, {'id': 15921, 'synset': 'grabber.n.01', 'name': 'grabber'}, {'id': 15922, 'synset': 'grader.n.01', 'name': 'grader'}, {'id': 15923, 'synset': 'graduate_nurse.n.01', 'name': 'graduate_nurse'}, {'id': 15924, 'synset': 'grammarian.n.01', 'name': 'grammarian'}, {'id': 15925, 'synset': 'granddaughter.n.01', 'name': 'granddaughter'}, {'id': 15926, 'synset': 'grande_dame.n.01', 'name': 'grande_dame'}, {'id': 15927, 'synset': 'grandfather.n.01', 'name': 'grandfather'}, {'id': 15928, 'synset': 'grand_inquisitor.n.01', 'name': 'Grand_Inquisitor'}, {'id': 15929, 'synset': 'grandma.n.01', 'name': 'grandma'}, {'id': 15930, 'synset': 'grandmaster.n.01', 'name': 'grandmaster'}, {'id': 15931, 'synset': 'grandparent.n.01', 'name': 'grandparent'}, {'id': 15932, 'synset': 'grantee.n.01', 'name': 'grantee'}, {'id': 15933, 'synset': 'granter.n.01', 'name': 'granter'}, {'id': 15934, 'synset': 'grass_widower.n.01', 'name': 'grass_widower'}, {'id': 15935, 'synset': 'great-aunt.n.01', 'name': 'great-aunt'}, {'id': 15936, 'synset': 'great_grandchild.n.01', 'name': 'great_grandchild'}, {'id': 15937, 'synset': 'great_granddaughter.n.01', 'name': 'great_granddaughter'}, {'id': 15938, 'synset': 'great_grandmother.n.01', 'name': 'great_grandmother'}, {'id': 15939, 'synset': 'great_grandparent.n.01', 'name': 'great_grandparent'}, {'id': 15940, 'synset': 'great_grandson.n.01', 'name': 'great_grandson'}, {'id': 15941, 'synset': 'great-nephew.n.01', 'name': 'great-nephew'}, {'id': 15942, 'synset': 'great-niece.n.01', 'name': 'great-niece'}, {'id': 15943, 'synset': 'green_beret.n.01', 'name': 'Green_Beret'}, {'id': 15944, 'synset': 'grenadier.n.01', 'name': 'grenadier'}, {'id': 15945, 'synset': 'greeter.n.01', 'name': 'greeter'}, {'id': 15946, 'synset': 'gringo.n.01', 'name': 'gringo'}, {'id': 15947, 'synset': 'grinner.n.01', 'name': 'grinner'}, {'id': 15948, 'synset': 'grocer.n.01', 'name': 'grocer'}, {'id': 15949, 'synset': 'groom.n.03', 'name': 'groom'}, {'id': 15950, 'synset': 'groom.n.01', 'name': 'groom'}, {'id': 15951, 'synset': 'grouch.n.01', 'name': 'grouch'}, {'id': 15952, 'synset': 'group_captain.n.01', 'name': 'group_captain'}, {'id': 15953, 'synset': 'grunter.n.01', 'name': 'grunter'}, {'id': 15954, 'synset': 'prison_guard.n.01', 'name': 'prison_guard'}, {'id': 15955, 'synset': 'guard.n.01', 'name': 'guard'}, {'id': 15956, 'synset': 'guesser.n.01', 'name': 'guesser'}, {'id': 15957, 'synset': 'guest.n.01', 'name': 'guest'}, {'id': 15958, 'synset': 'guest.n.03', 'name': 'guest'}, {'id': 15959, 'synset': 'guest_of_honor.n.01', 'name': 'guest_of_honor'}, {'id': 15960, 'synset': 'guest_worker.n.01', 'name': 'guest_worker'}, {'id': 15961, 'synset': 'guide.n.02', 'name': 'guide'}, {'id': 15962, 'synset': 'guitarist.n.01', 'name': 'guitarist'}, {'id': 15963, 'synset': 'gunnery_sergeant.n.01', 'name': 'gunnery_sergeant'}, {'id': 15964, 'synset': 'guru.n.01', 'name': 'guru'}, {'id': 15965, 'synset': 'guru.n.03', 'name': 'guru'}, {'id': 15966, 'synset': 'guvnor.n.01', 'name': 'guvnor'}, {'id': 15967, 'synset': 'guy.n.01', 'name': 'guy'}, {'id': 15968, 'synset': 'gymnast.n.01', 'name': 'gymnast'}, {'id': 15969, 'synset': 'gym_rat.n.01', 'name': 'gym_rat'}, {'id': 15970, 'synset': 'gynecologist.n.01', 'name': 'gynecologist'}, {'id': 15971, 'synset': 'gypsy.n.02', 'name': 'Gypsy'}, {'id': 15972, 'synset': 'hack.n.01', 'name': 'hack'}, {'id': 15973, 'synset': 'hacker.n.02', 'name': 'hacker'}, {'id': 15974, 'synset': 'haggler.n.01', 'name': 'haggler'}, {'id': 15975, 'synset': 'hairdresser.n.01', 'name': 'hairdresser'}, {'id': 15976, 'synset': 'hakim.n.02', 'name': 'hakim'}, {'id': 15977, 'synset': 'hakka.n.01', 'name': 'Hakka'}, {'id': 15978, 'synset': 'halberdier.n.01', 'name': 'halberdier'}, {'id': 15979, 'synset': 'halfback.n.01', 'name': 'halfback'}, {'id': 15980, 'synset': 'half_blood.n.01', 'name': 'half_blood'}, {'id': 15981, 'synset': 'hand.n.10', 'name': 'hand'}, {'id': 15982, 'synset': 'animal_trainer.n.01', 'name': 'animal_trainer'}, {'id': 15983, 'synset': 'handyman.n.01', 'name': 'handyman'}, {'id': 15984, 'synset': 'hang_glider.n.01', 'name': 'hang_glider'}, {'id': 15985, 'synset': 'hardliner.n.01', 'name': 'hardliner'}, {'id': 15986, 'synset': 'harlequin.n.01', 'name': 'harlequin'}, {'id': 15987, 'synset': 'harmonizer.n.02', 'name': 'harmonizer'}, {'id': 15988, 'synset': 'hash_head.n.01', 'name': 'hash_head'}, {'id': 15989, 'synset': 'hatchet_man.n.01', 'name': 'hatchet_man'}, {'id': 15990, 'synset': 'hater.n.01', 'name': 'hater'}, {'id': 15991, 'synset': 'hatmaker.n.01', 'name': 'hatmaker'}, {'id': 15992, 'synset': 'headman.n.02', 'name': 'headman'}, {'id': 15993, 'synset': 'headmaster.n.01', 'name': 'headmaster'}, {'id': 15994, 'synset': 'head_nurse.n.01', 'name': 'head_nurse'}, {'id': 15995, 'synset': 'hearer.n.01', 'name': 'hearer'}, {'id': 15996, 'synset': 'heartbreaker.n.01', 'name': 'heartbreaker'}, {'id': 15997, 'synset': 'heathen.n.01', 'name': 'heathen'}, {'id': 15998, 'synset': 'heavyweight.n.02', 'name': 'heavyweight'}, {'id': 15999, 'synset': 'heavy.n.01', 'name': 'heavy'}, {'id': 16000, 'synset': 'heckler.n.01', 'name': 'heckler'}, {'id': 16001, 'synset': 'hedger.n.02', 'name': 'hedger'}, {'id': 16002, 'synset': 'hedger.n.01', 'name': 'hedger'}, {'id': 16003, 'synset': 'hedonist.n.01', 'name': 'hedonist'}, {'id': 16004, 'synset': 'heir.n.01', 'name': 'heir'}, {'id': 16005, 'synset': 'heir_apparent.n.01', 'name': 'heir_apparent'}, {'id': 16006, 'synset': 'heiress.n.01', 'name': 'heiress'}, {'id': 16007, 'synset': 'heir_presumptive.n.01', 'name': 'heir_presumptive'}, {'id': 16008, 'synset': 'hellion.n.01', 'name': 'hellion'}, {'id': 16009, 'synset': 'helmsman.n.01', 'name': 'helmsman'}, {'id': 16010, 'synset': 'hire.n.01', 'name': 'hire'}, {'id': 16011, 'synset': 'hematologist.n.01', 'name': 'hematologist'}, {'id': 16012, 'synset': 'hemiplegic.n.01', 'name': 'hemiplegic'}, {'id': 16013, 'synset': 'herald.n.01', 'name': 'herald'}, {'id': 16014, 'synset': 'herbalist.n.01', 'name': 'herbalist'}, {'id': 16015, 'synset': 'herder.n.02', 'name': 'herder'}, {'id': 16016, 'synset': 'hermaphrodite.n.01', 'name': 'hermaphrodite'}, {'id': 16017, 'synset': 'heroine.n.02', 'name': 'heroine'}, {'id': 16018, 'synset': 'heroin_addict.n.01', 'name': 'heroin_addict'}, {'id': 16019, 'synset': 'hero_worshiper.n.01', 'name': 'hero_worshiper'}, {'id': 16020, 'synset': 'herr.n.01', 'name': 'Herr'}, {'id': 16021, 'synset': 'highbinder.n.01', 'name': 'highbinder'}, {'id': 16022, 'synset': 'highbrow.n.01', 'name': 'highbrow'}, {'id': 16023, 'synset': 'high_commissioner.n.01', 'name': 'high_commissioner'}, {'id': 16024, 'synset': 'highflier.n.01', 'name': 'highflier'}, {'id': 16025, 'synset': 'highlander.n.02', 'name': 'Highlander'}, {'id': 16026, 'synset': 'high-muck-a-muck.n.01', 'name': 'high-muck-a-muck'}, {'id': 16027, 'synset': 'high_priest.n.01', 'name': 'high_priest'}, {'id': 16028, 'synset': 'highjacker.n.01', 'name': 'highjacker'}, {'id': 16029, 'synset': 'hireling.n.01', 'name': 'hireling'}, {'id': 16030, 'synset': 'historian.n.01', 'name': 'historian'}, {'id': 16031, 'synset': 'hitchhiker.n.01', 'name': 'hitchhiker'}, {'id': 16032, 'synset': 'hitter.n.02', 'name': 'hitter'}, {'id': 16033, 'synset': 'hobbyist.n.01', 'name': 'hobbyist'}, {'id': 16034, 'synset': 'holdout.n.01', 'name': 'holdout'}, {'id': 16035, 'synset': 'holdover.n.01', 'name': 'holdover'}, {'id': 16036, 'synset': 'holdup_man.n.01', 'name': 'holdup_man'}, {'id': 16037, 'synset': 'homeboy.n.02', 'name': 'homeboy'}, {'id': 16038, 'synset': 'homeboy.n.01', 'name': 'homeboy'}, {'id': 16039, 'synset': 'home_buyer.n.01', 'name': 'home_buyer'}, {'id': 16040, 'synset': 'homegirl.n.01', 'name': 'homegirl'}, {'id': 16041, 'synset': 'homeless.n.01', 'name': 'homeless'}, {'id': 16042, 'synset': 'homeopath.n.01', 'name': 'homeopath'}, {'id': 16043, 'synset': 'honest_woman.n.01', 'name': 'honest_woman'}, {'id': 16044, 'synset': 'honor_guard.n.01', 'name': 'honor_guard'}, {'id': 16045, 'synset': 'hooker.n.05', 'name': 'hooker'}, {'id': 16046, 'synset': 'hoper.n.01', 'name': 'hoper'}, {'id': 16047, 'synset': 'hornist.n.01', 'name': 'hornist'}, {'id': 16048, 'synset': 'horseman.n.01', 'name': 'horseman'}, {'id': 16049, 'synset': 'horse_trader.n.01', 'name': 'horse_trader'}, {'id': 16050, 'synset': 'horsewoman.n.01', 'name': 'horsewoman'}, {'id': 16051, 'synset': 'horse_wrangler.n.01', 'name': 'horse_wrangler'}, {'id': 16052, 'synset': 'horticulturist.n.01', 'name': 'horticulturist'}, {'id': 16053, 'synset': 'hospital_chaplain.n.01', 'name': 'hospital_chaplain'}, {'id': 16054, 'synset': 'host.n.08', 'name': 'host'}, {'id': 16055, 'synset': 'host.n.01', 'name': 'host'}, {'id': 16056, 'synset': 'hostess.n.01', 'name': 'hostess'}, {'id': 16057, 'synset': 'hotelier.n.01', 'name': 'hotelier'}, {'id': 16058, 'synset': 'housekeeper.n.01', 'name': 'housekeeper'}, {'id': 16059, 'synset': 'housemaster.n.01', 'name': 'housemaster'}, {'id': 16060, 'synset': 'housemate.n.01', 'name': 'housemate'}, {'id': 16061, 'synset': 'house_physician.n.01', 'name': 'house_physician'}, {'id': 16062, 'synset': 'house_sitter.n.01', 'name': 'house_sitter'}, {'id': 16063, 'synset': 'housing_commissioner.n.01', 'name': 'housing_commissioner'}, {'id': 16064, 'synset': 'huckster.n.01', 'name': 'huckster'}, {'id': 16065, 'synset': 'hugger.n.01', 'name': 'hugger'}, {'id': 16066, 'synset': 'humanist.n.02', 'name': 'humanist'}, {'id': 16067, 'synset': 'humanitarian.n.01', 'name': 'humanitarian'}, {'id': 16068, 'synset': 'hunk.n.01', 'name': 'hunk'}, {'id': 16069, 'synset': 'huntress.n.01', 'name': 'huntress'}, {'id': 16070, 'synset': 'ex-husband.n.01', 'name': 'ex-husband'}, {'id': 16071, 'synset': 'hydrologist.n.01', 'name': 'hydrologist'}, {'id': 16072, 'synset': 'hyperope.n.01', 'name': 'hyperope'}, {'id': 16073, 'synset': 'hypertensive.n.01', 'name': 'hypertensive'}, {'id': 16074, 'synset': 'hypnotist.n.01', 'name': 'hypnotist'}, {'id': 16075, 'synset': 'hypocrite.n.01', 'name': 'hypocrite'}, {'id': 16076, 'synset': 'iceman.n.01', 'name': 'iceman'}, {'id': 16077, 'synset': 'iconoclast.n.02', 'name': 'iconoclast'}, {'id': 16078, 'synset': 'ideologist.n.01', 'name': 'ideologist'}, {'id': 16079, 'synset': 'idol.n.02', 'name': 'idol'}, {'id': 16080, 'synset': 'idolizer.n.01', 'name': 'idolizer'}, {'id': 16081, 'synset': 'imam.n.01', 'name': 'imam'}, {'id': 16082, 'synset': 'imperialist.n.01', 'name': 'imperialist'}, {'id': 16083, 'synset': 'important_person.n.01', 'name': 'important_person'}, {'id': 16084, 'synset': 'inamorato.n.01', 'name': 'inamorato'}, {'id': 16085, 'synset': 'incumbent.n.01', 'name': 'incumbent'}, {'id': 16086, 'synset': 'incurable.n.01', 'name': 'incurable'}, {'id': 16087, 'synset': 'inductee.n.01', 'name': 'inductee'}, {'id': 16088, 'synset': 'industrialist.n.01', 'name': 'industrialist'}, {'id': 16089, 'synset': 'infanticide.n.01', 'name': 'infanticide'}, {'id': 16090, 'synset': 'inferior.n.01', 'name': 'inferior'}, {'id': 16091, 'synset': 'infernal.n.01', 'name': 'infernal'}, {'id': 16092, 'synset': 'infielder.n.01', 'name': 'infielder'}, {'id': 16093, 'synset': 'infiltrator.n.02', 'name': 'infiltrator'}, {'id': 16094, 'synset': 'informer.n.01', 'name': 'informer'}, {'id': 16095, 'synset': 'ingenue.n.02', 'name': 'ingenue'}, {'id': 16096, 'synset': 'ingenue.n.01', 'name': 'ingenue'}, {'id': 16097, 'synset': 'polymath.n.01', 'name': 'polymath'}, {'id': 16098, 'synset': 'in-law.n.01', 'name': 'in-law'}, {'id': 16099, 'synset': 'inquiry_agent.n.01', 'name': 'inquiry_agent'}, {'id': 16100, 'synset': 'inspector.n.01', 'name': 'inspector'}, {'id': 16101, 'synset': 'inspector_general.n.01', 'name': 'inspector_general'}, {'id': 16102, 'synset': 'instigator.n.02', 'name': 'instigator'}, {'id': 16103, 'synset': 'insurance_broker.n.01', 'name': 'insurance_broker'}, {'id': 16104, 'synset': 'insurgent.n.01', 'name': 'insurgent'}, {'id': 16105, 'synset': 'intelligence_analyst.n.01', 'name': 'intelligence_analyst'}, {'id': 16106, 'synset': 'interior_designer.n.01', 'name': 'interior_designer'}, {'id': 16107, 'synset': 'interlocutor.n.02', 'name': 'interlocutor'}, {'id': 16108, 'synset': 'interlocutor.n.01', 'name': 'interlocutor'}, {'id': 16109, 'synset': 'international_grandmaster.n.01', 'name': 'International_Grandmaster'}, {'id': 16110, 'synset': 'internationalist.n.02', 'name': 'internationalist'}, {'id': 16111, 'synset': 'internist.n.01', 'name': 'internist'}, {'id': 16112, 'synset': 'interpreter.n.01', 'name': 'interpreter'}, {'id': 16113, 'synset': 'interpreter.n.02', 'name': 'interpreter'}, {'id': 16114, 'synset': 'intervenor.n.01', 'name': 'intervenor'}, {'id': 16115, 'synset': 'introvert.n.01', 'name': 'introvert'}, {'id': 16116, 'synset': 'invader.n.01', 'name': 'invader'}, {'id': 16117, 'synset': 'invalidator.n.01', 'name': 'invalidator'}, {'id': 16118, 'synset': 'investigator.n.02', 'name': 'investigator'}, {'id': 16119, 'synset': 'investor.n.01', 'name': 'investor'}, {'id': 16120, 'synset': 'invigilator.n.01', 'name': 'invigilator'}, {'id': 16121, 'synset': 'irreligionist.n.01', 'name': 'irreligionist'}, {'id': 16122, 'synset': 'ivy_leaguer.n.01', 'name': 'Ivy_Leaguer'}, {'id': 16123, 'synset': 'jack_of_all_trades.n.01', 'name': 'Jack_of_all_trades'}, {'id': 16124, 'synset': 'jacksonian.n.01', 'name': 'Jacksonian'}, {'id': 16125, 'synset': 'jane_doe.n.01', 'name': 'Jane_Doe'}, {'id': 16126, 'synset': 'janissary.n.01', 'name': 'janissary'}, {'id': 16127, 'synset': 'jat.n.01', 'name': 'Jat'}, {'id': 16128, 'synset': 'javanese.n.01', 'name': 'Javanese'}, {'id': 16129, 'synset': 'jekyll_and_hyde.n.01', 'name': 'Jekyll_and_Hyde'}, {'id': 16130, 'synset': 'jester.n.01', 'name': 'jester'}, {'id': 16131, 'synset': 'jesuit.n.01', 'name': 'Jesuit'}, {'id': 16132, 'synset': 'jezebel.n.02', 'name': 'jezebel'}, {'id': 16133, 'synset': 'jilt.n.01', 'name': 'jilt'}, {'id': 16134, 'synset': 'jobber.n.01', 'name': 'jobber'}, {'id': 16135, 'synset': 'job_candidate.n.01', 'name': 'job_candidate'}, {'id': 16136, 'synset': "job's_comforter.n.01", 'name': "Job's_comforter"}, {'id': 16137, 'synset': 'jockey.n.01', 'name': 'jockey'}, {'id': 16138, 'synset': 'john_doe.n.02', 'name': 'John_Doe'}, {'id': 16139, 'synset': 'journalist.n.01', 'name': 'journalist'}, {'id': 16140, 'synset': 'judge.n.01', 'name': 'judge'}, {'id': 16141, 'synset': 'judge_advocate.n.01', 'name': 'judge_advocate'}, {'id': 16142, 'synset': 'juggler.n.01', 'name': 'juggler'}, {'id': 16143, 'synset': 'jungian.n.01', 'name': 'Jungian'}, {'id': 16144, 'synset': 'junior.n.03', 'name': 'junior'}, {'id': 16145, 'synset': 'junior.n.02', 'name': 'junior'}, {'id': 16146, 'synset': 'junior.n.04', 'name': 'Junior'}, {'id': 16147, 'synset': 'junior_lightweight.n.01', 'name': 'junior_lightweight'}, {'id': 16148, 'synset': 'junior_middleweight.n.01', 'name': 'junior_middleweight'}, {'id': 16149, 'synset': 'jurist.n.01', 'name': 'jurist'}, {'id': 16150, 'synset': 'juror.n.01', 'name': 'juror'}, {'id': 16151, 'synset': 'justice_of_the_peace.n.01', 'name': 'justice_of_the_peace'}, {'id': 16152, 'synset': 'justiciar.n.01', 'name': 'justiciar'}, {'id': 16153, 'synset': 'kachina.n.01', 'name': 'kachina'}, {'id': 16154, 'synset': 'keyboardist.n.01', 'name': 'keyboardist'}, {'id': 16155, 'synset': 'khedive.n.01', 'name': 'Khedive'}, {'id': 16156, 'synset': 'kingmaker.n.02', 'name': 'kingmaker'}, {'id': 16157, 'synset': 'king.n.02', 'name': 'king'}, {'id': 16158, 'synset': "king's_counsel.n.01", 'name': "King's_Counsel"}, {'id': 16159, 'synset': 'counsel_to_the_crown.n.01', 'name': 'Counsel_to_the_Crown'}, {'id': 16160, 'synset': 'kin.n.01', 'name': 'kin'}, {'id': 16161, 'synset': 'enate.n.01', 'name': 'enate'}, {'id': 16162, 'synset': 'kink.n.03', 'name': 'kink'}, {'id': 16163, 'synset': 'kinswoman.n.01', 'name': 'kinswoman'}, {'id': 16164, 'synset': 'kisser.n.01', 'name': 'kisser'}, {'id': 16165, 'synset': 'kitchen_help.n.01', 'name': 'kitchen_help'}, {'id': 16166, 'synset': 'kitchen_police.n.01', 'name': 'kitchen_police'}, {'id': 16167, 'synset': 'klansman.n.01', 'name': 'Klansman'}, {'id': 16168, 'synset': 'kleptomaniac.n.01', 'name': 'kleptomaniac'}, {'id': 16169, 'synset': 'kneeler.n.01', 'name': 'kneeler'}, {'id': 16170, 'synset': 'knight.n.01', 'name': 'knight'}, {'id': 16171, 'synset': 'knocker.n.01', 'name': 'knocker'}, {'id': 16172, 'synset': 'knower.n.01', 'name': 'knower'}, {'id': 16173, 'synset': 'know-it-all.n.01', 'name': 'know-it-all'}, {'id': 16174, 'synset': 'kolkhoznik.n.01', 'name': 'kolkhoznik'}, {'id': 16175, 'synset': 'kshatriya.n.01', 'name': 'Kshatriya'}, {'id': 16176, 'synset': 'labor_coach.n.01', 'name': 'labor_coach'}, {'id': 16177, 'synset': 'laborer.n.01', 'name': 'laborer'}, {'id': 16178, 'synset': 'labourite.n.01', 'name': 'Labourite'}, {'id': 16179, 'synset': 'lady.n.01', 'name': 'lady'}, {'id': 16180, 'synset': 'lady-in-waiting.n.01', 'name': 'lady-in-waiting'}, {'id': 16181, 'synset': "lady's_maid.n.01", 'name': "lady's_maid"}, {'id': 16182, 'synset': 'lama.n.01', 'name': 'lama'}, {'id': 16183, 'synset': 'lamb.n.04', 'name': 'lamb'}, {'id': 16184, 'synset': 'lame_duck.n.01', 'name': 'lame_duck'}, {'id': 16185, 'synset': 'lamplighter.n.01', 'name': 'lamplighter'}, {'id': 16186, 'synset': 'land_agent.n.02', 'name': 'land_agent'}, {'id': 16187, 'synset': 'landgrave.n.01', 'name': 'landgrave'}, {'id': 16188, 'synset': 'landlubber.n.02', 'name': 'landlubber'}, {'id': 16189, 'synset': 'landlubber.n.01', 'name': 'landlubber'}, {'id': 16190, 'synset': 'landowner.n.01', 'name': 'landowner'}, {'id': 16191, 'synset': 'landscape_architect.n.01', 'name': 'landscape_architect'}, {'id': 16192, 'synset': 'langlaufer.n.01', 'name': 'langlaufer'}, {'id': 16193, 'synset': 'languisher.n.01', 'name': 'languisher'}, {'id': 16194, 'synset': 'lapidary.n.01', 'name': 'lapidary'}, {'id': 16195, 'synset': 'lass.n.01', 'name': 'lass'}, {'id': 16196, 'synset': 'latin.n.03', 'name': 'Latin'}, {'id': 16197, 'synset': 'latin.n.02', 'name': 'Latin'}, {'id': 16198, 'synset': 'latitudinarian.n.01', 'name': 'latitudinarian'}, {'id': 16199, 'synset': "jehovah's_witness.n.01", 'name': "Jehovah's_Witness"}, {'id': 16200, 'synset': 'law_agent.n.01', 'name': 'law_agent'}, {'id': 16201, 'synset': 'lawgiver.n.01', 'name': 'lawgiver'}, {'id': 16202, 'synset': 'lawman.n.01', 'name': 'lawman'}, {'id': 16203, 'synset': 'law_student.n.01', 'name': 'law_student'}, {'id': 16204, 'synset': 'lawyer.n.01', 'name': 'lawyer'}, {'id': 16205, 'synset': 'lay_reader.n.01', 'name': 'lay_reader'}, {'id': 16206, 'synset': 'lazybones.n.01', 'name': 'lazybones'}, {'id': 16207, 'synset': 'leaker.n.01', 'name': 'leaker'}, {'id': 16208, 'synset': 'leaseholder.n.01', 'name': 'leaseholder'}, {'id': 16209, 'synset': 'lector.n.02', 'name': 'lector'}, {'id': 16210, 'synset': 'lector.n.01', 'name': 'lector'}, {'id': 16211, 'synset': 'lecturer.n.02', 'name': 'lecturer'}, {'id': 16212, 'synset': 'left-hander.n.02', 'name': 'left-hander'}, {'id': 16213, 'synset': 'legal_representative.n.01', 'name': 'legal_representative'}, {'id': 16214, 'synset': 'legate.n.01', 'name': 'legate'}, {'id': 16215, 'synset': 'legatee.n.01', 'name': 'legatee'}, {'id': 16216, 'synset': 'legionnaire.n.02', 'name': 'legionnaire'}, {'id': 16217, 'synset': 'letterman.n.01', 'name': 'letterman'}, {'id': 16218, 'synset': 'liberator.n.01', 'name': 'liberator'}, {'id': 16219, 'synset': 'licenser.n.01', 'name': 'licenser'}, {'id': 16220, 'synset': 'licentiate.n.01', 'name': 'licentiate'}, {'id': 16221, 'synset': 'lieutenant.n.01', 'name': 'lieutenant'}, {'id': 16222, 'synset': 'lieutenant_colonel.n.01', 'name': 'lieutenant_colonel'}, {'id': 16223, 'synset': 'lieutenant_commander.n.01', 'name': 'lieutenant_commander'}, {'id': 16224, 'synset': 'lieutenant_junior_grade.n.01', 'name': 'lieutenant_junior_grade'}, {'id': 16225, 'synset': 'life.n.08', 'name': 'life'}, {'id': 16226, 'synset': 'lifeguard.n.01', 'name': 'lifeguard'}, {'id': 16227, 'synset': 'life_tenant.n.01', 'name': 'life_tenant'}, {'id': 16228, 'synset': 'light_flyweight.n.01', 'name': 'light_flyweight'}, {'id': 16229, 'synset': 'light_heavyweight.n.03', 'name': 'light_heavyweight'}, {'id': 16230, 'synset': 'light_heavyweight.n.01', 'name': 'light_heavyweight'}, {'id': 16231, 'synset': "light-o'-love.n.01", 'name': "light-o'-love"}, {'id': 16232, 'synset': 'lightweight.n.01', 'name': 'lightweight'}, {'id': 16233, 'synset': 'lightweight.n.04', 'name': 'lightweight'}, {'id': 16234, 'synset': 'lightweight.n.03', 'name': 'lightweight'}, {'id': 16235, 'synset': 'lilliputian.n.01', 'name': 'lilliputian'}, {'id': 16236, 'synset': 'limnologist.n.01', 'name': 'limnologist'}, {'id': 16237, 'synset': 'lineman.n.01', 'name': 'lineman'}, {'id': 16238, 'synset': 'line_officer.n.01', 'name': 'line_officer'}, {'id': 16239, 'synset': 'lion-hunter.n.01', 'name': 'lion-hunter'}, {'id': 16240, 'synset': 'lisper.n.01', 'name': 'lisper'}, {'id': 16241, 'synset': 'lister.n.02', 'name': 'lister'}, {'id': 16242, 'synset': 'literary_critic.n.01', 'name': 'literary_critic'}, {'id': 16243, 'synset': 'literate.n.01', 'name': 'literate'}, {'id': 16244, 'synset': 'litigant.n.01', 'name': 'litigant'}, {'id': 16245, 'synset': 'litterer.n.01', 'name': 'litterer'}, {'id': 16246, 'synset': 'little_brother.n.01', 'name': 'little_brother'}, {'id': 16247, 'synset': 'little_sister.n.01', 'name': 'little_sister'}, {'id': 16248, 'synset': 'lobbyist.n.01', 'name': 'lobbyist'}, {'id': 16249, 'synset': 'locksmith.n.01', 'name': 'locksmith'}, {'id': 16250, 'synset': 'locum_tenens.n.01', 'name': 'locum_tenens'}, {'id': 16251, 'synset': 'lord.n.03', 'name': 'Lord'}, {'id': 16252, 'synset': 'loser.n.03', 'name': 'loser'}, {'id': 16253, 'synset': 'loser.n.01', 'name': 'loser'}, {'id': 16254, 'synset': 'failure.n.04', 'name': 'failure'}, {'id': 16255, 'synset': 'lothario.n.01', 'name': 'Lothario'}, {'id': 16256, 'synset': 'loudmouth.n.01', 'name': 'loudmouth'}, {'id': 16257, 'synset': 'lowerclassman.n.01', 'name': 'lowerclassman'}, {'id': 16258, 'synset': 'lowlander.n.01', 'name': 'Lowlander'}, {'id': 16259, 'synset': 'loyalist.n.01', 'name': 'loyalist'}, {'id': 16260, 'synset': 'luddite.n.01', 'name': 'Luddite'}, {'id': 16261, 'synset': 'lumberman.n.01', 'name': 'lumberman'}, {'id': 16262, 'synset': 'lumper.n.02', 'name': 'lumper'}, {'id': 16263, 'synset': 'bedlamite.n.01', 'name': 'bedlamite'}, {'id': 16264, 'synset': 'pyromaniac.n.01', 'name': 'pyromaniac'}, {'id': 16265, 'synset': 'lutist.n.01', 'name': 'lutist'}, {'id': 16266, 'synset': 'lutheran.n.01', 'name': 'Lutheran'}, {'id': 16267, 'synset': 'lyricist.n.01', 'name': 'lyricist'}, {'id': 16268, 'synset': 'macebearer.n.01', 'name': 'macebearer'}, {'id': 16269, 'synset': 'machinist.n.01', 'name': 'machinist'}, {'id': 16270, 'synset': 'madame.n.01', 'name': 'madame'}, {'id': 16271, 'synset': 'maenad.n.01', 'name': 'maenad'}, {'id': 16272, 'synset': 'maestro.n.01', 'name': 'maestro'}, {'id': 16273, 'synset': 'magdalen.n.01', 'name': 'magdalen'}, {'id': 16274, 'synset': 'magician.n.01', 'name': 'magician'}, {'id': 16275, 'synset': 'magus.n.01', 'name': 'magus'}, {'id': 16276, 'synset': 'maharani.n.01', 'name': 'maharani'}, {'id': 16277, 'synset': 'mahatma.n.01', 'name': 'mahatma'}, {'id': 16278, 'synset': 'maid.n.02', 'name': 'maid'}, {'id': 16279, 'synset': 'maid.n.01', 'name': 'maid'}, {'id': 16280, 'synset': 'major.n.01', 'name': 'major'}, {'id': 16281, 'synset': 'major.n.03', 'name': 'major'}, {'id': 16282, 'synset': 'major-domo.n.01', 'name': 'major-domo'}, {'id': 16283, 'synset': 'maker.n.01', 'name': 'maker'}, {'id': 16284, 'synset': 'malahini.n.01', 'name': 'malahini'}, {'id': 16285, 'synset': 'malcontent.n.01', 'name': 'malcontent'}, {'id': 16286, 'synset': 'malik.n.01', 'name': 'malik'}, {'id': 16287, 'synset': 'malingerer.n.01', 'name': 'malingerer'}, {'id': 16288, 'synset': 'malthusian.n.01', 'name': 'Malthusian'}, {'id': 16289, 'synset': 'adonis.n.01', 'name': 'adonis'}, {'id': 16290, 'synset': 'man.n.03', 'name': 'man'}, {'id': 16291, 'synset': 'man.n.05', 'name': 'man'}, {'id': 16292, 'synset': 'manageress.n.01', 'name': 'manageress'}, {'id': 16293, 'synset': 'mandarin.n.03', 'name': 'mandarin'}, {'id': 16294, 'synset': 'maneuverer.n.01', 'name': 'maneuverer'}, {'id': 16295, 'synset': 'maniac.n.02', 'name': 'maniac'}, {'id': 16296, 'synset': 'manichaean.n.01', 'name': 'Manichaean'}, {'id': 16297, 'synset': 'manicurist.n.01', 'name': 'manicurist'}, {'id': 16298, 'synset': 'manipulator.n.02', 'name': 'manipulator'}, {'id': 16299, 'synset': 'man-at-arms.n.01', 'name': 'man-at-arms'}, {'id': 16300, 'synset': 'man_of_action.n.01', 'name': 'man_of_action'}, {'id': 16301, 'synset': 'man_of_letters.n.01', 'name': 'man_of_letters'}, {'id': 16302, 'synset': 'manufacturer.n.02', 'name': 'manufacturer'}, {'id': 16303, 'synset': 'marcher.n.02', 'name': 'marcher'}, {'id': 16304, 'synset': 'marchioness.n.02', 'name': 'marchioness'}, {'id': 16305, 'synset': 'margrave.n.02', 'name': 'margrave'}, {'id': 16306, 'synset': 'margrave.n.01', 'name': 'margrave'}, {'id': 16307, 'synset': 'marine.n.01', 'name': 'Marine'}, {'id': 16308, 'synset': 'marquess.n.02', 'name': 'marquess'}, {'id': 16309, 'synset': 'marquis.n.02', 'name': 'marquis'}, {'id': 16310, 'synset': 'marshal.n.02', 'name': 'marshal'}, {'id': 16311, 'synset': 'martinet.n.01', 'name': 'martinet'}, {'id': 16312, 'synset': 'masochist.n.01', 'name': 'masochist'}, {'id': 16313, 'synset': 'mason.n.04', 'name': 'mason'}, {'id': 16314, 'synset': 'masquerader.n.01', 'name': 'masquerader'}, {'id': 16315, 'synset': 'masseur.n.01', 'name': 'masseur'}, {'id': 16316, 'synset': 'masseuse.n.01', 'name': 'masseuse'}, {'id': 16317, 'synset': 'master.n.04', 'name': 'master'}, {'id': 16318, 'synset': 'master.n.07', 'name': 'master'}, {'id': 16319, 'synset': 'master-at-arms.n.01', 'name': 'master-at-arms'}, {'id': 16320, 'synset': 'master_of_ceremonies.n.01', 'name': 'master_of_ceremonies'}, {'id': 16321, 'synset': 'masturbator.n.01', 'name': 'masturbator'}, {'id': 16322, 'synset': 'matchmaker.n.01', 'name': 'matchmaker'}, {'id': 16323, 'synset': 'mate.n.01', 'name': 'mate'}, {'id': 16324, 'synset': 'mate.n.08', 'name': 'mate'}, {'id': 16325, 'synset': 'mate.n.03', 'name': 'mate'}, {'id': 16326, 'synset': 'mater.n.01', 'name': 'mater'}, {'id': 16327, 'synset': 'material.n.05', 'name': 'material'}, {'id': 16328, 'synset': 'materialist.n.02', 'name': 'materialist'}, {'id': 16329, 'synset': 'matriarch.n.01', 'name': 'matriarch'}, {'id': 16330, 'synset': 'matriarch.n.02', 'name': 'matriarch'}, {'id': 16331, 'synset': 'matriculate.n.01', 'name': 'matriculate'}, {'id': 16332, 'synset': 'matron.n.01', 'name': 'matron'}, {'id': 16333, 'synset': 'mayor.n.01', 'name': 'mayor'}, {'id': 16334, 'synset': 'mayoress.n.01', 'name': 'mayoress'}, {'id': 16335, 'synset': 'mechanical_engineer.n.01', 'name': 'mechanical_engineer'}, {'id': 16336, 'synset': 'medalist.n.02', 'name': 'medalist'}, {'id': 16337, 'synset': 'medical_officer.n.01', 'name': 'medical_officer'}, {'id': 16338, 'synset': 'medical_practitioner.n.01', 'name': 'medical_practitioner'}, {'id': 16339, 'synset': 'medical_scientist.n.01', 'name': 'medical_scientist'}, {'id': 16340, 'synset': 'medium.n.09', 'name': 'medium'}, {'id': 16341, 'synset': 'megalomaniac.n.01', 'name': 'megalomaniac'}, {'id': 16342, 'synset': 'melancholic.n.01', 'name': 'melancholic'}, {'id': 16343, 'synset': 'melkite.n.01', 'name': 'Melkite'}, {'id': 16344, 'synset': 'melter.n.01', 'name': 'melter'}, {'id': 16345, 'synset': 'nonmember.n.01', 'name': 'nonmember'}, {'id': 16346, 'synset': 'board_member.n.01', 'name': 'board_member'}, {'id': 16347, 'synset': 'clansman.n.01', 'name': 'clansman'}, {'id': 16348, 'synset': 'memorizer.n.01', 'name': 'memorizer'}, {'id': 16349, 'synset': 'mendelian.n.01', 'name': 'Mendelian'}, {'id': 16350, 'synset': 'mender.n.01', 'name': 'mender'}, {'id': 16351, 'synset': 'mesoamerican.n.01', 'name': 'Mesoamerican'}, {'id': 16352, 'synset': 'messmate.n.01', 'name': 'messmate'}, {'id': 16353, 'synset': 'mestiza.n.01', 'name': 'mestiza'}, {'id': 16354, 'synset': 'meteorologist.n.01', 'name': 'meteorologist'}, {'id': 16355, 'synset': 'meter_maid.n.01', 'name': 'meter_maid'}, {'id': 16356, 'synset': 'methodist.n.01', 'name': 'Methodist'}, {'id': 16357, 'synset': 'metis.n.01', 'name': 'Metis'}, {'id': 16358, 'synset': 'metropolitan.n.01', 'name': 'metropolitan'}, {'id': 16359, 'synset': 'mezzo-soprano.n.01', 'name': 'mezzo-soprano'}, {'id': 16360, 'synset': 'microeconomist.n.01', 'name': 'microeconomist'}, {'id': 16361, 'synset': 'middle-aged_man.n.01', 'name': 'middle-aged_man'}, {'id': 16362, 'synset': 'middlebrow.n.01', 'name': 'middlebrow'}, {'id': 16363, 'synset': 'middleweight.n.01', 'name': 'middleweight'}, {'id': 16364, 'synset': 'midwife.n.01', 'name': 'midwife'}, {'id': 16365, 'synset': 'mikado.n.01', 'name': 'mikado'}, {'id': 16366, 'synset': 'milanese.n.01', 'name': 'Milanese'}, {'id': 16367, 'synset': 'miler.n.02', 'name': 'miler'}, {'id': 16368, 'synset': 'miles_gloriosus.n.01', 'name': 'miles_gloriosus'}, {'id': 16369, 'synset': 'military_attache.n.01', 'name': 'military_attache'}, {'id': 16370, 'synset': 'military_chaplain.n.01', 'name': 'military_chaplain'}, {'id': 16371, 'synset': 'military_leader.n.01', 'name': 'military_leader'}, {'id': 16372, 'synset': 'military_officer.n.01', 'name': 'military_officer'}, {'id': 16373, 'synset': 'military_policeman.n.01', 'name': 'military_policeman'}, {'id': 16374, 'synset': 'mill_agent.n.01', 'name': 'mill_agent'}, {'id': 16375, 'synset': 'mill-hand.n.01', 'name': 'mill-hand'}, {'id': 16376, 'synset': 'millionairess.n.01', 'name': 'millionairess'}, {'id': 16377, 'synset': 'millwright.n.01', 'name': 'millwright'}, {'id': 16378, 'synset': 'minder.n.01', 'name': 'minder'}, {'id': 16379, 'synset': 'mining_engineer.n.01', 'name': 'mining_engineer'}, {'id': 16380, 'synset': 'minister.n.02', 'name': 'minister'}, {'id': 16381, 'synset': 'ministrant.n.01', 'name': 'ministrant'}, {'id': 16382, 'synset': 'minor_leaguer.n.01', 'name': 'minor_leaguer'}, {'id': 16383, 'synset': 'minuteman.n.01', 'name': 'Minuteman'}, {'id': 16384, 'synset': 'misanthrope.n.01', 'name': 'misanthrope'}, {'id': 16385, 'synset': 'misfit.n.01', 'name': 'misfit'}, {'id': 16386, 'synset': 'mistress.n.03', 'name': 'mistress'}, {'id': 16387, 'synset': 'mistress.n.01', 'name': 'mistress'}, {'id': 16388, 'synset': 'mixed-blood.n.01', 'name': 'mixed-blood'}, {'id': 16389, 'synset': 'model.n.03', 'name': 'model'}, {'id': 16390, 'synset': 'class_act.n.01', 'name': 'class_act'}, {'id': 16391, 'synset': 'modeler.n.01', 'name': 'modeler'}, {'id': 16392, 'synset': 'modifier.n.02', 'name': 'modifier'}, {'id': 16393, 'synset': 'molecular_biologist.n.01', 'name': 'molecular_biologist'}, {'id': 16394, 'synset': 'monegasque.n.01', 'name': 'Monegasque'}, {'id': 16395, 'synset': 'monetarist.n.01', 'name': 'monetarist'}, {'id': 16396, 'synset': 'moneygrubber.n.01', 'name': 'moneygrubber'}, {'id': 16397, 'synset': 'moneymaker.n.01', 'name': 'moneymaker'}, {'id': 16398, 'synset': 'mongoloid.n.01', 'name': 'Mongoloid'}, {'id': 16399, 'synset': 'monolingual.n.01', 'name': 'monolingual'}, {'id': 16400, 'synset': 'monologist.n.01', 'name': 'monologist'}, {'id': 16401, 'synset': 'moonlighter.n.01', 'name': 'moonlighter'}, {'id': 16402, 'synset': 'moralist.n.01', 'name': 'moralist'}, {'id': 16403, 'synset': 'morosoph.n.01', 'name': 'morosoph'}, {'id': 16404, 'synset': 'morris_dancer.n.01', 'name': 'morris_dancer'}, {'id': 16405, 'synset': 'mortal_enemy.n.01', 'name': 'mortal_enemy'}, {'id': 16406, 'synset': 'mortgagee.n.01', 'name': 'mortgagee'}, {'id': 16407, 'synset': 'mortician.n.01', 'name': 'mortician'}, {'id': 16408, 'synset': 'moss-trooper.n.01', 'name': 'moss-trooper'}, {'id': 16409, 'synset': 'mother.n.01', 'name': 'mother'}, {'id': 16410, 'synset': 'mother.n.04', 'name': 'mother'}, {'id': 16411, 'synset': 'mother.n.03', 'name': 'mother'}, {'id': 16412, 'synset': 'mother_figure.n.01', 'name': 'mother_figure'}, {'id': 16413, 'synset': 'mother_hen.n.01', 'name': 'mother_hen'}, {'id': 16414, 'synset': 'mother-in-law.n.01', 'name': 'mother-in-law'}, {'id': 16415, 'synset': "mother's_boy.n.01", 'name': "mother's_boy"}, {'id': 16416, 'synset': "mother's_daughter.n.01", 'name': "mother's_daughter"}, {'id': 16417, 'synset': 'motorcycle_cop.n.01', 'name': 'motorcycle_cop'}, {'id': 16418, 'synset': 'motorcyclist.n.01', 'name': 'motorcyclist'}, {'id': 16419, 'synset': 'mound_builder.n.01', 'name': 'Mound_Builder'}, {'id': 16420, 'synset': 'mountebank.n.01', 'name': 'mountebank'}, {'id': 16421, 'synset': 'mourner.n.01', 'name': 'mourner'}, {'id': 16422, 'synset': 'mouthpiece.n.03', 'name': 'mouthpiece'}, {'id': 16423, 'synset': 'mover.n.03', 'name': 'mover'}, {'id': 16424, 'synset': 'moviegoer.n.01', 'name': 'moviegoer'}, {'id': 16425, 'synset': 'muffin_man.n.01', 'name': 'muffin_man'}, {'id': 16426, 'synset': 'mugwump.n.02', 'name': 'mugwump'}, {'id': 16427, 'synset': 'mullah.n.01', 'name': 'Mullah'}, {'id': 16428, 'synset': 'muncher.n.01', 'name': 'muncher'}, {'id': 16429, 'synset': 'murderess.n.01', 'name': 'murderess'}, {'id': 16430, 'synset': 'murder_suspect.n.01', 'name': 'murder_suspect'}, {'id': 16431, 'synset': 'musher.n.01', 'name': 'musher'}, {'id': 16432, 'synset': 'musician.n.01', 'name': 'musician'}, {'id': 16433, 'synset': 'musicologist.n.01', 'name': 'musicologist'}, {'id': 16434, 'synset': 'music_teacher.n.01', 'name': 'music_teacher'}, {'id': 16435, 'synset': 'musketeer.n.01', 'name': 'musketeer'}, {'id': 16436, 'synset': 'muslimah.n.01', 'name': 'Muslimah'}, {'id': 16437, 'synset': 'mutilator.n.01', 'name': 'mutilator'}, {'id': 16438, 'synset': 'mutineer.n.01', 'name': 'mutineer'}, {'id': 16439, 'synset': 'mute.n.01', 'name': 'mute'}, {'id': 16440, 'synset': 'mutterer.n.01', 'name': 'mutterer'}, {'id': 16441, 'synset': 'muzzler.n.01', 'name': 'muzzler'}, {'id': 16442, 'synset': 'mycenaen.n.01', 'name': 'Mycenaen'}, {'id': 16443, 'synset': 'mycologist.n.01', 'name': 'mycologist'}, {'id': 16444, 'synset': 'myope.n.01', 'name': 'myope'}, {'id': 16445, 'synset': 'myrmidon.n.01', 'name': 'myrmidon'}, {'id': 16446, 'synset': 'mystic.n.01', 'name': 'mystic'}, {'id': 16447, 'synset': 'mythologist.n.01', 'name': 'mythologist'}, {'id': 16448, 'synset': 'naif.n.01', 'name': 'naif'}, {'id': 16449, 'synset': 'nailer.n.01', 'name': 'nailer'}, {'id': 16450, 'synset': 'namby-pamby.n.01', 'name': 'namby-pamby'}, {'id': 16451, 'synset': 'name_dropper.n.01', 'name': 'name_dropper'}, {'id': 16452, 'synset': 'namer.n.01', 'name': 'namer'}, {'id': 16453, 'synset': 'nan.n.01', 'name': 'nan'}, {'id': 16454, 'synset': 'nanny.n.01', 'name': 'nanny'}, {'id': 16455, 'synset': 'narc.n.01', 'name': 'narc'}, {'id': 16456, 'synset': 'narcissist.n.01', 'name': 'narcissist'}, {'id': 16457, 'synset': 'nark.n.01', 'name': 'nark'}, {'id': 16458, 'synset': 'nationalist.n.02', 'name': 'nationalist'}, {'id': 16459, 'synset': 'nautch_girl.n.01', 'name': 'nautch_girl'}, {'id': 16460, 'synset': 'naval_commander.n.01', 'name': 'naval_commander'}, {'id': 16461, 'synset': 'navy_seal.n.01', 'name': 'Navy_SEAL'}, {'id': 16462, 'synset': 'obstructionist.n.01', 'name': 'obstructionist'}, {'id': 16463, 'synset': 'nazarene.n.02', 'name': 'Nazarene'}, {'id': 16464, 'synset': 'nazarene.n.01', 'name': 'Nazarene'}, {'id': 16465, 'synset': 'nazi.n.01', 'name': 'Nazi'}, {'id': 16466, 'synset': 'nebbish.n.01', 'name': 'nebbish'}, {'id': 16467, 'synset': 'necker.n.01', 'name': 'necker'}, {'id': 16468, 'synset': 'neonate.n.01', 'name': 'neonate'}, {'id': 16469, 'synset': 'nephew.n.01', 'name': 'nephew'}, {'id': 16470, 'synset': 'neurobiologist.n.01', 'name': 'neurobiologist'}, {'id': 16471, 'synset': 'neurologist.n.01', 'name': 'neurologist'}, {'id': 16472, 'synset': 'neurosurgeon.n.01', 'name': 'neurosurgeon'}, {'id': 16473, 'synset': 'neutral.n.01', 'name': 'neutral'}, {'id': 16474, 'synset': 'neutralist.n.01', 'name': 'neutralist'}, {'id': 16475, 'synset': 'newcomer.n.01', 'name': 'newcomer'}, {'id': 16476, 'synset': 'newcomer.n.02', 'name': 'newcomer'}, {'id': 16477, 'synset': 'new_dealer.n.01', 'name': 'New_Dealer'}, {'id': 16478, 'synset': 'newspaper_editor.n.01', 'name': 'newspaper_editor'}, {'id': 16479, 'synset': 'newsreader.n.01', 'name': 'newsreader'}, {'id': 16480, 'synset': 'newtonian.n.01', 'name': 'Newtonian'}, {'id': 16481, 'synset': 'niece.n.01', 'name': 'niece'}, {'id': 16482, 'synset': 'niggard.n.01', 'name': 'niggard'}, {'id': 16483, 'synset': 'night_porter.n.01', 'name': 'night_porter'}, {'id': 16484, 'synset': 'night_rider.n.01', 'name': 'night_rider'}, {'id': 16485, 'synset': 'nimby.n.01', 'name': 'NIMBY'}, {'id': 16486, 'synset': 'niqaabi.n.01', 'name': 'niqaabi'}, {'id': 16487, 'synset': 'nitpicker.n.01', 'name': 'nitpicker'}, {'id': 16488, 'synset': 'nobelist.n.01', 'name': 'Nobelist'}, {'id': 16489, 'synset': 'noc.n.01', 'name': 'NOC'}, {'id': 16490, 'synset': 'noncandidate.n.01', 'name': 'noncandidate'}, {'id': 16491, 'synset': 'noncommissioned_officer.n.01', 'name': 'noncommissioned_officer'}, {'id': 16492, 'synset': 'nondescript.n.01', 'name': 'nondescript'}, {'id': 16493, 'synset': 'nondriver.n.01', 'name': 'nondriver'}, {'id': 16494, 'synset': 'nonparticipant.n.01', 'name': 'nonparticipant'}, {'id': 16495, 'synset': 'nonperson.n.01', 'name': 'nonperson'}, {'id': 16496, 'synset': 'nonresident.n.01', 'name': 'nonresident'}, {'id': 16497, 'synset': 'nonsmoker.n.01', 'name': 'nonsmoker'}, {'id': 16498, 'synset': 'northern_baptist.n.01', 'name': 'Northern_Baptist'}, {'id': 16499, 'synset': 'noticer.n.01', 'name': 'noticer'}, {'id': 16500, 'synset': 'novelist.n.01', 'name': 'novelist'}, {'id': 16501, 'synset': 'novitiate.n.02', 'name': 'novitiate'}, {'id': 16502, 'synset': 'nuclear_chemist.n.01', 'name': 'nuclear_chemist'}, {'id': 16503, 'synset': 'nudger.n.01', 'name': 'nudger'}, {'id': 16504, 'synset': 'nullipara.n.01', 'name': 'nullipara'}, {'id': 16505, 'synset': 'number_theorist.n.01', 'name': 'number_theorist'}, {'id': 16506, 'synset': 'nurse.n.01', 'name': 'nurse'}, {'id': 16507, 'synset': 'nursling.n.01', 'name': 'nursling'}, {'id': 16508, 'synset': 'nymph.n.03', 'name': 'nymph'}, {'id': 16509, 'synset': 'nymphet.n.01', 'name': 'nymphet'}, {'id': 16510, 'synset': 'nympholept.n.01', 'name': 'nympholept'}, {'id': 16511, 'synset': 'nymphomaniac.n.01', 'name': 'nymphomaniac'}, {'id': 16512, 'synset': 'oarswoman.n.01', 'name': 'oarswoman'}, {'id': 16513, 'synset': 'oboist.n.01', 'name': 'oboist'}, {'id': 16514, 'synset': 'obscurantist.n.01', 'name': 'obscurantist'}, {'id': 16515, 'synset': 'observer.n.02', 'name': 'observer'}, {'id': 16516, 'synset': 'obstetrician.n.01', 'name': 'obstetrician'}, {'id': 16517, 'synset': 'occupier.n.02', 'name': 'occupier'}, {'id': 16518, 'synset': 'occultist.n.01', 'name': 'occultist'}, {'id': 16519, 'synset': 'wine_lover.n.01', 'name': 'wine_lover'}, {'id': 16520, 'synset': 'offerer.n.01', 'name': 'offerer'}, {'id': 16521, 'synset': 'office-bearer.n.01', 'name': 'office-bearer'}, {'id': 16522, 'synset': 'office_boy.n.01', 'name': 'office_boy'}, {'id': 16523, 'synset': 'officeholder.n.01', 'name': 'officeholder'}, {'id': 16524, 'synset': 'officiant.n.01', 'name': 'officiant'}, {'id': 16525, 'synset': 'federal.n.02', 'name': 'Federal'}, {'id': 16526, 'synset': 'oilman.n.02', 'name': 'oilman'}, {'id': 16527, 'synset': 'oil_tycoon.n.01', 'name': 'oil_tycoon'}, {'id': 16528, 'synset': 'old-age_pensioner.n.01', 'name': 'old-age_pensioner'}, {'id': 16529, 'synset': 'old_boy.n.02', 'name': 'old_boy'}, {'id': 16530, 'synset': 'old_lady.n.01', 'name': 'old_lady'}, {'id': 16531, 'synset': 'old_man.n.03', 'name': 'old_man'}, {'id': 16532, 'synset': 'oldster.n.01', 'name': 'oldster'}, {'id': 16533, 'synset': 'old-timer.n.02', 'name': 'old-timer'}, {'id': 16534, 'synset': 'old_woman.n.01', 'name': 'old_woman'}, {'id': 16535, 'synset': 'oligarch.n.01', 'name': 'oligarch'}, {'id': 16536, 'synset': 'olympian.n.01', 'name': 'Olympian'}, {'id': 16537, 'synset': 'omnivore.n.01', 'name': 'omnivore'}, {'id': 16538, 'synset': 'oncologist.n.01', 'name': 'oncologist'}, {'id': 16539, 'synset': 'onlooker.n.01', 'name': 'onlooker'}, {'id': 16540, 'synset': 'onomancer.n.01', 'name': 'onomancer'}, {'id': 16541, 'synset': 'operator.n.03', 'name': 'operator'}, {'id': 16542, 'synset': 'opportunist.n.01', 'name': 'opportunist'}, {'id': 16543, 'synset': 'optimist.n.01', 'name': 'optimist'}, {'id': 16544, 'synset': 'orangeman.n.01', 'name': 'Orangeman'}, {'id': 16545, 'synset': 'orator.n.01', 'name': 'orator'}, {'id': 16546, 'synset': 'orderly.n.02', 'name': 'orderly'}, {'id': 16547, 'synset': 'orderly.n.01', 'name': 'orderly'}, {'id': 16548, 'synset': 'orderly_sergeant.n.01', 'name': 'orderly_sergeant'}, {'id': 16549, 'synset': 'ordinand.n.01', 'name': 'ordinand'}, {'id': 16550, 'synset': 'ordinary.n.03', 'name': 'ordinary'}, {'id': 16551, 'synset': 'organ-grinder.n.01', 'name': 'organ-grinder'}, {'id': 16552, 'synset': 'organist.n.01', 'name': 'organist'}, {'id': 16553, 'synset': 'organization_man.n.01', 'name': 'organization_man'}, {'id': 16554, 'synset': 'organizer.n.01', 'name': 'organizer'}, {'id': 16555, 'synset': 'organizer.n.02', 'name': 'organizer'}, {'id': 16556, 'synset': 'originator.n.01', 'name': 'originator'}, {'id': 16557, 'synset': 'ornithologist.n.01', 'name': 'ornithologist'}, {'id': 16558, 'synset': 'orphan.n.01', 'name': 'orphan'}, {'id': 16559, 'synset': 'orphan.n.02', 'name': 'orphan'}, {'id': 16560, 'synset': 'osteopath.n.01', 'name': 'osteopath'}, {'id': 16561, 'synset': 'out-and-outer.n.01', 'name': 'out-and-outer'}, {'id': 16562, 'synset': 'outdoorswoman.n.01', 'name': 'outdoorswoman'}, {'id': 16563, 'synset': 'outfielder.n.02', 'name': 'outfielder'}, {'id': 16564, 'synset': 'outfielder.n.01', 'name': 'outfielder'}, {'id': 16565, 'synset': 'right_fielder.n.01', 'name': 'right_fielder'}, {'id': 16566, 'synset': 'right-handed_pitcher.n.01', 'name': 'right-handed_pitcher'}, {'id': 16567, 'synset': 'outlier.n.01', 'name': 'outlier'}, {'id': 16568, 'synset': 'owner-occupier.n.01', 'name': 'owner-occupier'}, {'id': 16569, 'synset': 'oyabun.n.01', 'name': 'oyabun'}, {'id': 16570, 'synset': 'packrat.n.01', 'name': 'packrat'}, {'id': 16571, 'synset': 'padrone.n.02', 'name': 'padrone'}, {'id': 16572, 'synset': 'padrone.n.01', 'name': 'padrone'}, {'id': 16573, 'synset': 'page.n.04', 'name': 'page'}, {'id': 16574, 'synset': 'painter.n.02', 'name': 'painter'}, {'id': 16575, 'synset': 'paleo-american.n.01', 'name': 'Paleo-American'}, {'id': 16576, 'synset': 'paleontologist.n.01', 'name': 'paleontologist'}, {'id': 16577, 'synset': 'pallbearer.n.01', 'name': 'pallbearer'}, {'id': 16578, 'synset': 'palmist.n.01', 'name': 'palmist'}, {'id': 16579, 'synset': 'pamperer.n.01', 'name': 'pamperer'}, {'id': 16580, 'synset': 'panchen_lama.n.01', 'name': 'Panchen_Lama'}, {'id': 16581, 'synset': 'panelist.n.01', 'name': 'panelist'}, {'id': 16582, 'synset': 'panhandler.n.01', 'name': 'panhandler'}, {'id': 16583, 'synset': 'paparazzo.n.01', 'name': 'paparazzo'}, {'id': 16584, 'synset': 'paperboy.n.01', 'name': 'paperboy'}, {'id': 16585, 'synset': 'paperhanger.n.02', 'name': 'paperhanger'}, {'id': 16586, 'synset': 'paperhanger.n.01', 'name': 'paperhanger'}, {'id': 16587, 'synset': 'papoose.n.01', 'name': 'papoose'}, {'id': 16588, 'synset': 'pardoner.n.02', 'name': 'pardoner'}, {'id': 16589, 'synset': 'paretic.n.01', 'name': 'paretic'}, {'id': 16590, 'synset': 'parishioner.n.01', 'name': 'parishioner'}, {'id': 16591, 'synset': 'park_commissioner.n.01', 'name': 'park_commissioner'}, {'id': 16592, 'synset': 'parliamentarian.n.01', 'name': 'Parliamentarian'}, {'id': 16593, 'synset': 'parliamentary_agent.n.01', 'name': 'parliamentary_agent'}, {'id': 16594, 'synset': 'parodist.n.01', 'name': 'parodist'}, {'id': 16595, 'synset': 'parricide.n.01', 'name': 'parricide'}, {'id': 16596, 'synset': 'parrot.n.02', 'name': 'parrot'}, {'id': 16597, 'synset': 'partaker.n.01', 'name': 'partaker'}, {'id': 16598, 'synset': 'part-timer.n.01', 'name': 'part-timer'}, {'id': 16599, 'synset': 'party.n.05', 'name': 'party'}, {'id': 16600, 'synset': 'party_man.n.01', 'name': 'party_man'}, {'id': 16601, 'synset': 'passenger.n.01', 'name': 'passenger'}, {'id': 16602, 'synset': 'passer.n.03', 'name': 'passer'}, {'id': 16603, 'synset': 'paster.n.01', 'name': 'paster'}, {'id': 16604, 'synset': 'pater.n.01', 'name': 'pater'}, {'id': 16605, 'synset': 'patient.n.01', 'name': 'patient'}, {'id': 16606, 'synset': 'patriarch.n.04', 'name': 'patriarch'}, {'id': 16607, 'synset': 'patriarch.n.03', 'name': 'patriarch'}, {'id': 16608, 'synset': 'patriarch.n.02', 'name': 'patriarch'}, {'id': 16609, 'synset': 'patriot.n.01', 'name': 'patriot'}, {'id': 16610, 'synset': 'patron.n.03', 'name': 'patron'}, {'id': 16611, 'synset': 'patternmaker.n.01', 'name': 'patternmaker'}, {'id': 16612, 'synset': 'pawnbroker.n.01', 'name': 'pawnbroker'}, {'id': 16613, 'synset': 'payer.n.01', 'name': 'payer'}, {'id': 16614, 'synset': 'peacekeeper.n.01', 'name': 'peacekeeper'}, {'id': 16615, 'synset': 'peasant.n.02', 'name': 'peasant'}, {'id': 16616, 'synset': 'pedant.n.01', 'name': 'pedant'}, {'id': 16617, 'synset': 'peddler.n.01', 'name': 'peddler'}, {'id': 16618, 'synset': 'pederast.n.01', 'name': 'pederast'}, {'id': 16619, 'synset': 'penologist.n.01', 'name': 'penologist'}, {'id': 16620, 'synset': 'pentathlete.n.01', 'name': 'pentathlete'}, {'id': 16621, 'synset': 'pentecostal.n.01', 'name': 'Pentecostal'}, {'id': 16622, 'synset': 'percussionist.n.01', 'name': 'percussionist'}, {'id': 16623, 'synset': 'periodontist.n.01', 'name': 'periodontist'}, {'id': 16624, 'synset': 'peshmerga.n.01', 'name': 'peshmerga'}, {'id': 16625, 'synset': 'personality.n.02', 'name': 'personality'}, {'id': 16626, 'synset': 'personal_representative.n.01', 'name': 'personal_representative'}, {'id': 16627, 'synset': 'personage.n.01', 'name': 'personage'}, {'id': 16628, 'synset': 'persona_grata.n.01', 'name': 'persona_grata'}, {'id': 16629, 'synset': 'persona_non_grata.n.01', 'name': 'persona_non_grata'}, {'id': 16630, 'synset': 'personification.n.01', 'name': 'personification'}, {'id': 16631, 'synset': 'perspirer.n.01', 'name': 'perspirer'}, {'id': 16632, 'synset': 'pervert.n.01', 'name': 'pervert'}, {'id': 16633, 'synset': 'pessimist.n.01', 'name': 'pessimist'}, {'id': 16634, 'synset': 'pest.n.03', 'name': 'pest'}, {'id': 16635, 'synset': 'peter_pan.n.01', 'name': 'Peter_Pan'}, {'id': 16636, 'synset': 'petitioner.n.01', 'name': 'petitioner'}, {'id': 16637, 'synset': 'petit_juror.n.01', 'name': 'petit_juror'}, {'id': 16638, 'synset': 'pet_sitter.n.01', 'name': 'pet_sitter'}, {'id': 16639, 'synset': 'petter.n.01', 'name': 'petter'}, {'id': 16640, 'synset': 'pharaoh.n.01', 'name': 'Pharaoh'}, {'id': 16641, 'synset': 'pharmacist.n.01', 'name': 'pharmacist'}, {'id': 16642, 'synset': 'philanthropist.n.01', 'name': 'philanthropist'}, {'id': 16643, 'synset': 'philatelist.n.01', 'name': 'philatelist'}, {'id': 16644, 'synset': 'philosopher.n.02', 'name': 'philosopher'}, {'id': 16645, 'synset': 'phonetician.n.01', 'name': 'phonetician'}, {'id': 16646, 'synset': 'phonologist.n.01', 'name': 'phonologist'}, {'id': 16647, 'synset': 'photojournalist.n.01', 'name': 'photojournalist'}, {'id': 16648, 'synset': 'photometrist.n.01', 'name': 'photometrist'}, {'id': 16649, 'synset': 'physical_therapist.n.01', 'name': 'physical_therapist'}, {'id': 16650, 'synset': 'physicist.n.01', 'name': 'physicist'}, {'id': 16651, 'synset': 'piano_maker.n.01', 'name': 'piano_maker'}, {'id': 16652, 'synset': 'picker.n.01', 'name': 'picker'}, {'id': 16653, 'synset': 'picnicker.n.01', 'name': 'picnicker'}, {'id': 16654, 'synset': 'pilgrim.n.01', 'name': 'pilgrim'}, {'id': 16655, 'synset': 'pill.n.03', 'name': 'pill'}, {'id': 16656, 'synset': 'pillar.n.03', 'name': 'pillar'}, {'id': 16657, 'synset': 'pill_head.n.01', 'name': 'pill_head'}, {'id': 16658, 'synset': 'pilot.n.02', 'name': 'pilot'}, {'id': 16659, 'synset': 'piltdown_man.n.01', 'name': 'Piltdown_man'}, {'id': 16660, 'synset': 'pimp.n.01', 'name': 'pimp'}, {'id': 16661, 'synset': 'pipe_smoker.n.01', 'name': 'pipe_smoker'}, {'id': 16662, 'synset': 'pip-squeak.n.01', 'name': 'pip-squeak'}, {'id': 16663, 'synset': 'pisser.n.01', 'name': 'pisser'}, {'id': 16664, 'synset': 'pitcher.n.01', 'name': 'pitcher'}, {'id': 16665, 'synset': 'pitchman.n.01', 'name': 'pitchman'}, {'id': 16666, 'synset': 'placeman.n.01', 'name': 'placeman'}, {'id': 16667, 'synset': 'placer_miner.n.01', 'name': 'placer_miner'}, {'id': 16668, 'synset': 'plagiarist.n.01', 'name': 'plagiarist'}, {'id': 16669, 'synset': 'plainsman.n.01', 'name': 'plainsman'}, {'id': 16670, 'synset': 'planner.n.01', 'name': 'planner'}, {'id': 16671, 'synset': 'planter.n.01', 'name': 'planter'}, {'id': 16672, 'synset': 'plasterer.n.01', 'name': 'plasterer'}, {'id': 16673, 'synset': 'platinum_blond.n.01', 'name': 'platinum_blond'}, {'id': 16674, 'synset': 'platitudinarian.n.01', 'name': 'platitudinarian'}, {'id': 16675, 'synset': 'playboy.n.01', 'name': 'playboy'}, {'id': 16676, 'synset': 'player.n.01', 'name': 'player'}, {'id': 16677, 'synset': 'playmate.n.01', 'name': 'playmate'}, {'id': 16678, 'synset': 'pleaser.n.01', 'name': 'pleaser'}, {'id': 16679, 'synset': 'pledger.n.01', 'name': 'pledger'}, {'id': 16680, 'synset': 'plenipotentiary.n.01', 'name': 'plenipotentiary'}, {'id': 16681, 'synset': 'plier.n.01', 'name': 'plier'}, {'id': 16682, 'synset': 'plodder.n.03', 'name': 'plodder'}, {'id': 16683, 'synset': 'plodder.n.02', 'name': 'plodder'}, {'id': 16684, 'synset': 'plotter.n.02', 'name': 'plotter'}, {'id': 16685, 'synset': 'plumber.n.01', 'name': 'plumber'}, {'id': 16686, 'synset': 'pluralist.n.02', 'name': 'pluralist'}, {'id': 16687, 'synset': 'pluralist.n.01', 'name': 'pluralist'}, {'id': 16688, 'synset': 'poet.n.01', 'name': 'poet'}, {'id': 16689, 'synset': 'pointsman.n.01', 'name': 'pointsman'}, {'id': 16690, 'synset': 'point_woman.n.01', 'name': 'point_woman'}, {'id': 16691, 'synset': 'policyholder.n.01', 'name': 'policyholder'}, {'id': 16692, 'synset': 'political_prisoner.n.01', 'name': 'political_prisoner'}, {'id': 16693, 'synset': 'political_scientist.n.01', 'name': 'political_scientist'}, {'id': 16694, 'synset': 'politician.n.02', 'name': 'politician'}, {'id': 16695, 'synset': 'politician.n.03', 'name': 'politician'}, {'id': 16696, 'synset': 'pollster.n.01', 'name': 'pollster'}, {'id': 16697, 'synset': 'polluter.n.01', 'name': 'polluter'}, {'id': 16698, 'synset': 'pool_player.n.01', 'name': 'pool_player'}, {'id': 16699, 'synset': 'portraitist.n.01', 'name': 'portraitist'}, {'id': 16700, 'synset': 'poseuse.n.01', 'name': 'poseuse'}, {'id': 16701, 'synset': 'positivist.n.01', 'name': 'positivist'}, {'id': 16702, 'synset': 'postdoc.n.02', 'name': 'postdoc'}, {'id': 16703, 'synset': 'poster_girl.n.01', 'name': 'poster_girl'}, {'id': 16704, 'synset': 'postulator.n.02', 'name': 'postulator'}, {'id': 16705, 'synset': 'private_citizen.n.01', 'name': 'private_citizen'}, {'id': 16706, 'synset': 'problem_solver.n.01', 'name': 'problem_solver'}, {'id': 16707, 'synset': 'pro-lifer.n.01', 'name': 'pro-lifer'}, {'id': 16708, 'synset': 'prosthetist.n.01', 'name': 'prosthetist'}, {'id': 16709, 'synset': 'postulant.n.01', 'name': 'postulant'}, {'id': 16710, 'synset': 'potboy.n.01', 'name': 'potboy'}, {'id': 16711, 'synset': 'poultryman.n.01', 'name': 'poultryman'}, {'id': 16712, 'synset': 'power_user.n.01', 'name': 'power_user'}, {'id': 16713, 'synset': 'power_worker.n.01', 'name': 'power_worker'}, {'id': 16714, 'synset': 'practitioner.n.01', 'name': 'practitioner'}, {'id': 16715, 'synset': 'prayer.n.05', 'name': 'prayer'}, {'id': 16716, 'synset': 'preceptor.n.01', 'name': 'preceptor'}, {'id': 16717, 'synset': 'predecessor.n.01', 'name': 'predecessor'}, {'id': 16718, 'synset': 'preemptor.n.02', 'name': 'preemptor'}, {'id': 16719, 'synset': 'preemptor.n.01', 'name': 'preemptor'}, {'id': 16720, 'synset': 'premature_baby.n.01', 'name': 'premature_baby'}, {'id': 16721, 'synset': 'presbyter.n.01', 'name': 'presbyter'}, {'id': 16722, 'synset': 'presenter.n.02', 'name': 'presenter'}, {'id': 16723, 'synset': 'presentist.n.01', 'name': 'presentist'}, {'id': 16724, 'synset': 'preserver.n.03', 'name': 'preserver'}, {'id': 16725, 'synset': 'president.n.03', 'name': 'president'}, {'id': 16726, 'synset': 'president_of_the_united_states.n.01', 'name': 'President_of_the_United_States'}, {'id': 16727, 'synset': 'president.n.05', 'name': 'president'}, {'id': 16728, 'synset': 'press_agent.n.01', 'name': 'press_agent'}, {'id': 16729, 'synset': 'press_photographer.n.01', 'name': 'press_photographer'}, {'id': 16730, 'synset': 'priest.n.01', 'name': 'priest'}, {'id': 16731, 'synset': 'prima_ballerina.n.01', 'name': 'prima_ballerina'}, {'id': 16732, 'synset': 'prima_donna.n.02', 'name': 'prima_donna'}, {'id': 16733, 'synset': 'prima_donna.n.01', 'name': 'prima_donna'}, {'id': 16734, 'synset': 'primigravida.n.01', 'name': 'primigravida'}, {'id': 16735, 'synset': 'primordial_dwarf.n.01', 'name': 'primordial_dwarf'}, {'id': 16736, 'synset': 'prince_charming.n.01', 'name': 'prince_charming'}, {'id': 16737, 'synset': 'prince_consort.n.01', 'name': 'prince_consort'}, {'id': 16738, 'synset': 'princeling.n.01', 'name': 'princeling'}, {'id': 16739, 'synset': 'prince_of_wales.n.01', 'name': 'Prince_of_Wales'}, {'id': 16740, 'synset': 'princess.n.01', 'name': 'princess'}, {'id': 16741, 'synset': 'princess_royal.n.01', 'name': 'princess_royal'}, {'id': 16742, 'synset': 'principal.n.06', 'name': 'principal'}, {'id': 16743, 'synset': 'principal.n.02', 'name': 'principal'}, {'id': 16744, 'synset': 'print_seller.n.01', 'name': 'print_seller'}, {'id': 16745, 'synset': 'prior.n.01', 'name': 'prior'}, {'id': 16746, 'synset': 'private.n.01', 'name': 'private'}, {'id': 16747, 'synset': 'probationer.n.01', 'name': 'probationer'}, {'id': 16748, 'synset': 'processor.n.02', 'name': 'processor'}, {'id': 16749, 'synset': 'process-server.n.01', 'name': 'process-server'}, {'id': 16750, 'synset': 'proconsul.n.02', 'name': 'proconsul'}, {'id': 16751, 'synset': 'proconsul.n.01', 'name': 'proconsul'}, {'id': 16752, 'synset': 'proctologist.n.01', 'name': 'proctologist'}, {'id': 16753, 'synset': 'proctor.n.01', 'name': 'proctor'}, {'id': 16754, 'synset': 'procurator.n.02', 'name': 'procurator'}, {'id': 16755, 'synset': 'procurer.n.02', 'name': 'procurer'}, {'id': 16756, 'synset': 'profit_taker.n.01', 'name': 'profit_taker'}, {'id': 16757, 'synset': 'programmer.n.01', 'name': 'programmer'}, {'id': 16758, 'synset': 'promiser.n.01', 'name': 'promiser'}, {'id': 16759, 'synset': 'promoter.n.01', 'name': 'promoter'}, {'id': 16760, 'synset': 'promulgator.n.01', 'name': 'promulgator'}, {'id': 16761, 'synset': 'propagandist.n.01', 'name': 'propagandist'}, {'id': 16762, 'synset': 'propagator.n.02', 'name': 'propagator'}, {'id': 16763, 'synset': 'property_man.n.01', 'name': 'property_man'}, {'id': 16764, 'synset': 'prophetess.n.01', 'name': 'prophetess'}, {'id': 16765, 'synset': 'prophet.n.02', 'name': 'prophet'}, {'id': 16766, 'synset': 'prosecutor.n.01', 'name': 'prosecutor'}, {'id': 16767, 'synset': 'prospector.n.01', 'name': 'prospector'}, {'id': 16768, 'synset': 'protectionist.n.01', 'name': 'protectionist'}, {'id': 16769, 'synset': 'protegee.n.01', 'name': 'protegee'}, {'id': 16770, 'synset': 'protozoologist.n.01', 'name': 'protozoologist'}, {'id': 16771, 'synset': 'provost_marshal.n.01', 'name': 'provost_marshal'}, {'id': 16772, 'synset': 'pruner.n.01', 'name': 'pruner'}, {'id': 16773, 'synset': 'psalmist.n.01', 'name': 'psalmist'}, {'id': 16774, 'synset': 'psephologist.n.01', 'name': 'psephologist'}, {'id': 16775, 'synset': 'psychiatrist.n.01', 'name': 'psychiatrist'}, {'id': 16776, 'synset': 'psychic.n.01', 'name': 'psychic'}, {'id': 16777, 'synset': 'psycholinguist.n.01', 'name': 'psycholinguist'}, {'id': 16778, 'synset': 'psychophysicist.n.01', 'name': 'psychophysicist'}, {'id': 16779, 'synset': 'publican.n.01', 'name': 'publican'}, {'id': 16780, 'synset': 'pudge.n.01', 'name': 'pudge'}, {'id': 16781, 'synset': 'puerpera.n.01', 'name': 'puerpera'}, {'id': 16782, 'synset': 'punching_bag.n.01', 'name': 'punching_bag'}, {'id': 16783, 'synset': 'punter.n.02', 'name': 'punter'}, {'id': 16784, 'synset': 'punter.n.01', 'name': 'punter'}, {'id': 16785, 'synset': 'puppeteer.n.01', 'name': 'puppeteer'}, {'id': 16786, 'synset': 'puppy.n.02', 'name': 'puppy'}, {'id': 16787, 'synset': 'purchasing_agent.n.01', 'name': 'purchasing_agent'}, {'id': 16788, 'synset': 'puritan.n.02', 'name': 'puritan'}, {'id': 16789, 'synset': 'puritan.n.01', 'name': 'Puritan'}, {'id': 16790, 'synset': 'pursuer.n.02', 'name': 'pursuer'}, {'id': 16791, 'synset': 'pusher.n.03', 'name': 'pusher'}, {'id': 16792, 'synset': 'pusher.n.02', 'name': 'pusher'}, {'id': 16793, 'synset': 'pusher.n.01', 'name': 'pusher'}, {'id': 16794, 'synset': 'putz.n.01', 'name': 'putz'}, {'id': 16795, 'synset': 'pygmy.n.02', 'name': 'Pygmy'}, {'id': 16796, 'synset': 'qadi.n.01', 'name': 'qadi'}, {'id': 16797, 'synset': 'quadriplegic.n.01', 'name': 'quadriplegic'}, {'id': 16798, 'synset': 'quadruplet.n.02', 'name': 'quadruplet'}, {'id': 16799, 'synset': 'quaker.n.02', 'name': 'quaker'}, {'id': 16800, 'synset': 'quarter.n.11', 'name': 'quarter'}, {'id': 16801, 'synset': 'quarterback.n.01', 'name': 'quarterback'}, {'id': 16802, 'synset': 'quartermaster.n.01', 'name': 'quartermaster'}, {'id': 16803, 'synset': 'quartermaster_general.n.01', 'name': 'quartermaster_general'}, {'id': 16804, 'synset': 'quebecois.n.01', 'name': 'Quebecois'}, {'id': 16805, 'synset': 'queen.n.02', 'name': 'queen'}, {'id': 16806, 'synset': 'queen_of_england.n.01', 'name': 'Queen_of_England'}, {'id': 16807, 'synset': 'queen.n.03', 'name': 'queen'}, {'id': 16808, 'synset': 'queen.n.04', 'name': 'queen'}, {'id': 16809, 'synset': 'queen_consort.n.01', 'name': 'queen_consort'}, {'id': 16810, 'synset': 'queen_mother.n.01', 'name': 'queen_mother'}, {'id': 16811, 'synset': "queen's_counsel.n.01", 'name': "Queen's_Counsel"}, {'id': 16812, 'synset': 'question_master.n.01', 'name': 'question_master'}, {'id': 16813, 'synset': 'quick_study.n.01', 'name': 'quick_study'}, {'id': 16814, 'synset': 'quietist.n.01', 'name': 'quietist'}, {'id': 16815, 'synset': 'quitter.n.01', 'name': 'quitter'}, {'id': 16816, 'synset': 'rabbi.n.01', 'name': 'rabbi'}, {'id': 16817, 'synset': 'racist.n.01', 'name': 'racist'}, {'id': 16818, 'synset': 'radiobiologist.n.01', 'name': 'radiobiologist'}, {'id': 16819, 'synset': 'radiologic_technologist.n.01', 'name': 'radiologic_technologist'}, {'id': 16820, 'synset': 'radiologist.n.01', 'name': 'radiologist'}, {'id': 16821, 'synset': 'rainmaker.n.02', 'name': 'rainmaker'}, {'id': 16822, 'synset': 'raiser.n.01', 'name': 'raiser'}, {'id': 16823, 'synset': 'raja.n.01', 'name': 'raja'}, {'id': 16824, 'synset': 'rake.n.01', 'name': 'rake'}, {'id': 16825, 'synset': 'ramrod.n.02', 'name': 'ramrod'}, {'id': 16826, 'synset': 'ranch_hand.n.01', 'name': 'ranch_hand'}, {'id': 16827, 'synset': 'ranker.n.01', 'name': 'ranker'}, {'id': 16828, 'synset': 'ranter.n.01', 'name': 'ranter'}, {'id': 16829, 'synset': 'rape_suspect.n.01', 'name': 'rape_suspect'}, {'id': 16830, 'synset': 'rapper.n.01', 'name': 'rapper'}, {'id': 16831, 'synset': 'rapporteur.n.01', 'name': 'rapporteur'}, {'id': 16832, 'synset': 'rare_bird.n.01', 'name': 'rare_bird'}, {'id': 16833, 'synset': 'ratepayer.n.01', 'name': 'ratepayer'}, {'id': 16834, 'synset': 'raw_recruit.n.01', 'name': 'raw_recruit'}, {'id': 16835, 'synset': 'reader.n.01', 'name': 'reader'}, {'id': 16836, 'synset': 'reading_teacher.n.01', 'name': 'reading_teacher'}, {'id': 16837, 'synset': 'realist.n.01', 'name': 'realist'}, {'id': 16838, 'synset': 'real_estate_broker.n.01', 'name': 'real_estate_broker'}, {'id': 16839, 'synset': 'rear_admiral.n.01', 'name': 'rear_admiral'}, {'id': 16840, 'synset': 'receiver.n.05', 'name': 'receiver'}, {'id': 16841, 'synset': 'reciter.n.01', 'name': 'reciter'}, {'id': 16842, 'synset': 'recruit.n.02', 'name': 'recruit'}, {'id': 16843, 'synset': 'recruit.n.01', 'name': 'recruit'}, {'id': 16844, 'synset': 'recruiter.n.01', 'name': 'recruiter'}, {'id': 16845, 'synset': 'recruiting-sergeant.n.01', 'name': 'recruiting-sergeant'}, {'id': 16846, 'synset': 'redcap.n.01', 'name': 'redcap'}, {'id': 16847, 'synset': 'redhead.n.01', 'name': 'redhead'}, {'id': 16848, 'synset': 'redneck.n.01', 'name': 'redneck'}, {'id': 16849, 'synset': 'reeler.n.02', 'name': 'reeler'}, {'id': 16850, 'synset': 'reenactor.n.01', 'name': 'reenactor'}, {'id': 16851, 'synset': 'referral.n.01', 'name': 'referral'}, {'id': 16852, 'synset': 'referee.n.01', 'name': 'referee'}, {'id': 16853, 'synset': 'refiner.n.01', 'name': 'refiner'}, {'id': 16854, 'synset': 'reform_jew.n.01', 'name': 'Reform_Jew'}, {'id': 16855, 'synset': 'registered_nurse.n.01', 'name': 'registered_nurse'}, {'id': 16856, 'synset': 'registrar.n.01', 'name': 'registrar'}, {'id': 16857, 'synset': 'regius_professor.n.01', 'name': 'Regius_professor'}, {'id': 16858, 'synset': 'reliever.n.02', 'name': 'reliever'}, {'id': 16859, 'synset': 'anchorite.n.01', 'name': 'anchorite'}, {'id': 16860, 'synset': 'religious_leader.n.01', 'name': 'religious_leader'}, {'id': 16861, 'synset': 'remover.n.02', 'name': 'remover'}, {'id': 16862, 'synset': 'renaissance_man.n.01', 'name': 'Renaissance_man'}, {'id': 16863, 'synset': 'renegade.n.01', 'name': 'renegade'}, {'id': 16864, 'synset': 'rentier.n.01', 'name': 'rentier'}, {'id': 16865, 'synset': 'repairman.n.01', 'name': 'repairman'}, {'id': 16866, 'synset': 'reporter.n.01', 'name': 'reporter'}, {'id': 16867, 'synset': 'newswoman.n.01', 'name': 'newswoman'}, {'id': 16868, 'synset': 'representative.n.01', 'name': 'representative'}, {'id': 16869, 'synset': 'reprobate.n.01', 'name': 'reprobate'}, {'id': 16870, 'synset': 'rescuer.n.02', 'name': 'rescuer'}, {'id': 16871, 'synset': 'reservist.n.01', 'name': 'reservist'}, {'id': 16872, 'synset': 'resident_commissioner.n.01', 'name': 'resident_commissioner'}, {'id': 16873, 'synset': 'respecter.n.01', 'name': 'respecter'}, {'id': 16874, 'synset': 'restaurateur.n.01', 'name': 'restaurateur'}, {'id': 16875, 'synset': 'restrainer.n.02', 'name': 'restrainer'}, {'id': 16876, 'synset': 'retailer.n.01', 'name': 'retailer'}, {'id': 16877, 'synset': 'retiree.n.01', 'name': 'retiree'}, {'id': 16878, 'synset': 'returning_officer.n.01', 'name': 'returning_officer'}, {'id': 16879, 'synset': 'revenant.n.01', 'name': 'revenant'}, {'id': 16880, 'synset': 'revisionist.n.01', 'name': 'revisionist'}, {'id': 16881, 'synset': 'revolutionist.n.01', 'name': 'revolutionist'}, {'id': 16882, 'synset': 'rheumatologist.n.01', 'name': 'rheumatologist'}, {'id': 16883, 'synset': 'rhodesian_man.n.01', 'name': 'Rhodesian_man'}, {'id': 16884, 'synset': 'rhymer.n.01', 'name': 'rhymer'}, {'id': 16885, 'synset': 'rich_person.n.01', 'name': 'rich_person'}, {'id': 16886, 'synset': 'rider.n.03', 'name': 'rider'}, {'id': 16887, 'synset': 'riding_master.n.01', 'name': 'riding_master'}, {'id': 16888, 'synset': 'rifleman.n.02', 'name': 'rifleman'}, {'id': 16889, 'synset': 'right-hander.n.02', 'name': 'right-hander'}, {'id': 16890, 'synset': 'right-hand_man.n.01', 'name': 'right-hand_man'}, {'id': 16891, 'synset': 'ringer.n.03', 'name': 'ringer'}, {'id': 16892, 'synset': 'ringleader.n.01', 'name': 'ringleader'}, {'id': 16893, 'synset': 'roadman.n.02', 'name': 'roadman'}, {'id': 16894, 'synset': 'roarer.n.01', 'name': 'roarer'}, {'id': 16895, 'synset': 'rocket_engineer.n.01', 'name': 'rocket_engineer'}, {'id': 16896, 'synset': 'rocket_scientist.n.01', 'name': 'rocket_scientist'}, {'id': 16897, 'synset': 'rock_star.n.01', 'name': 'rock_star'}, {'id': 16898, 'synset': 'romanov.n.01', 'name': 'Romanov'}, {'id': 16899, 'synset': 'romanticist.n.02', 'name': 'romanticist'}, {'id': 16900, 'synset': 'ropemaker.n.01', 'name': 'ropemaker'}, {'id': 16901, 'synset': 'roper.n.02', 'name': 'roper'}, {'id': 16902, 'synset': 'roper.n.01', 'name': 'roper'}, {'id': 16903, 'synset': 'ropewalker.n.01', 'name': 'ropewalker'}, {'id': 16904, 'synset': 'rosebud.n.02', 'name': 'rosebud'}, {'id': 16905, 'synset': 'rosicrucian.n.02', 'name': 'Rosicrucian'}, {'id': 16906, 'synset': 'mountie.n.01', 'name': 'Mountie'}, {'id': 16907, 'synset': 'rough_rider.n.01', 'name': 'Rough_Rider'}, {'id': 16908, 'synset': 'roundhead.n.01', 'name': 'roundhead'}, {'id': 16909, 'synset': 'civil_authority.n.01', 'name': 'civil_authority'}, {'id': 16910, 'synset': 'runner.n.03', 'name': 'runner'}, {'id': 16911, 'synset': 'runner.n.02', 'name': 'runner'}, {'id': 16912, 'synset': 'runner.n.06', 'name': 'runner'}, {'id': 16913, 'synset': 'running_back.n.01', 'name': 'running_back'}, {'id': 16914, 'synset': 'rusher.n.02', 'name': 'rusher'}, {'id': 16915, 'synset': 'rustic.n.01', 'name': 'rustic'}, {'id': 16916, 'synset': 'saboteur.n.01', 'name': 'saboteur'}, {'id': 16917, 'synset': 'sadist.n.01', 'name': 'sadist'}, {'id': 16918, 'synset': 'sailing_master.n.01', 'name': 'sailing_master'}, {'id': 16919, 'synset': 'sailor.n.01', 'name': 'sailor'}, {'id': 16920, 'synset': 'salesgirl.n.01', 'name': 'salesgirl'}, {'id': 16921, 'synset': 'salesman.n.01', 'name': 'salesman'}, {'id': 16922, 'synset': 'salesperson.n.01', 'name': 'salesperson'}, {'id': 16923, 'synset': 'salvager.n.01', 'name': 'salvager'}, {'id': 16924, 'synset': 'sandwichman.n.01', 'name': 'sandwichman'}, {'id': 16925, 'synset': 'sangoma.n.01', 'name': 'sangoma'}, {'id': 16926, 'synset': 'sannup.n.01', 'name': 'sannup'}, {'id': 16927, 'synset': 'sapper.n.02', 'name': 'sapper'}, {'id': 16928, 'synset': 'sassenach.n.01', 'name': 'Sassenach'}, {'id': 16929, 'synset': 'satrap.n.01', 'name': 'satrap'}, {'id': 16930, 'synset': 'saunterer.n.01', 'name': 'saunterer'}, {'id': 16931, 'synset': 'savoyard.n.01', 'name': 'Savoyard'}, {'id': 16932, 'synset': 'sawyer.n.01', 'name': 'sawyer'}, {'id': 16933, 'synset': 'scalper.n.01', 'name': 'scalper'}, {'id': 16934, 'synset': 'scandalmonger.n.01', 'name': 'scandalmonger'}, {'id': 16935, 'synset': 'scapegrace.n.01', 'name': 'scapegrace'}, {'id': 16936, 'synset': 'scene_painter.n.02', 'name': 'scene_painter'}, {'id': 16937, 'synset': 'schemer.n.01', 'name': 'schemer'}, {'id': 16938, 'synset': 'schizophrenic.n.01', 'name': 'schizophrenic'}, {'id': 16939, 'synset': 'schlemiel.n.01', 'name': 'schlemiel'}, {'id': 16940, 'synset': 'schlockmeister.n.01', 'name': 'schlockmeister'}, {'id': 16941, 'synset': 'scholar.n.01', 'name': 'scholar'}, {'id': 16942, 'synset': 'scholiast.n.01', 'name': 'scholiast'}, {'id': 16943, 'synset': 'schoolchild.n.01', 'name': 'schoolchild'}, {'id': 16944, 'synset': 'schoolfriend.n.01', 'name': 'schoolfriend'}, {'id': 16945, 'synset': 'schoolman.n.01', 'name': 'Schoolman'}, {'id': 16946, 'synset': 'schoolmaster.n.02', 'name': 'schoolmaster'}, {'id': 16947, 'synset': 'schoolmate.n.01', 'name': 'schoolmate'}, {'id': 16948, 'synset': 'scientist.n.01', 'name': 'scientist'}, {'id': 16949, 'synset': 'scion.n.01', 'name': 'scion'}, {'id': 16950, 'synset': 'scoffer.n.02', 'name': 'scoffer'}, {'id': 16951, 'synset': 'scofflaw.n.01', 'name': 'scofflaw'}, {'id': 16952, 'synset': 'scorekeeper.n.01', 'name': 'scorekeeper'}, {'id': 16953, 'synset': 'scorer.n.02', 'name': 'scorer'}, {'id': 16954, 'synset': 'scourer.n.02', 'name': 'scourer'}, {'id': 16955, 'synset': 'scout.n.03', 'name': 'scout'}, {'id': 16956, 'synset': 'scoutmaster.n.01', 'name': 'scoutmaster'}, {'id': 16957, 'synset': 'scrambler.n.01', 'name': 'scrambler'}, {'id': 16958, 'synset': 'scratcher.n.02', 'name': 'scratcher'}, {'id': 16959, 'synset': 'screen_actor.n.01', 'name': 'screen_actor'}, {'id': 16960, 'synset': 'scrutineer.n.01', 'name': 'scrutineer'}, {'id': 16961, 'synset': 'scuba_diver.n.01', 'name': 'scuba_diver'}, {'id': 16962, 'synset': 'sculptor.n.01', 'name': 'sculptor'}, {'id': 16963, 'synset': 'sea_scout.n.01', 'name': 'Sea_Scout'}, {'id': 16964, 'synset': 'seasonal_worker.n.01', 'name': 'seasonal_worker'}, {'id': 16965, 'synset': 'seasoner.n.01', 'name': 'seasoner'}, {'id': 16966, 'synset': 'second_baseman.n.01', 'name': 'second_baseman'}, {'id': 16967, 'synset': 'second_cousin.n.01', 'name': 'second_cousin'}, {'id': 16968, 'synset': 'seconder.n.01', 'name': 'seconder'}, {'id': 16969, 'synset': 'second_fiddle.n.01', 'name': 'second_fiddle'}, {'id': 16970, 'synset': 'second-in-command.n.01', 'name': 'second-in-command'}, {'id': 16971, 'synset': 'second_lieutenant.n.01', 'name': 'second_lieutenant'}, {'id': 16972, 'synset': 'second-rater.n.01', 'name': 'second-rater'}, {'id': 16973, 'synset': 'secretary.n.01', 'name': 'secretary'}, {'id': 16974, 'synset': 'secretary_of_agriculture.n.01', 'name': 'Secretary_of_Agriculture'}, {'id': 16975, 'synset': 'secretary_of_health_and_human_services.n.01', 'name': 'Secretary_of_Health_and_Human_Services'}, {'id': 16976, 'synset': 'secretary_of_state.n.01', 'name': 'Secretary_of_State'}, {'id': 16977, 'synset': 'secretary_of_the_interior.n.02', 'name': 'Secretary_of_the_Interior'}, {'id': 16978, 'synset': 'sectarian.n.01', 'name': 'sectarian'}, {'id': 16979, 'synset': 'section_hand.n.01', 'name': 'section_hand'}, {'id': 16980, 'synset': 'secularist.n.01', 'name': 'secularist'}, {'id': 16981, 'synset': 'security_consultant.n.01', 'name': 'security_consultant'}, {'id': 16982, 'synset': 'seeded_player.n.01', 'name': 'seeded_player'}, {'id': 16983, 'synset': 'seeder.n.01', 'name': 'seeder'}, {'id': 16984, 'synset': 'seeker.n.01', 'name': 'seeker'}, {'id': 16985, 'synset': 'segregate.n.01', 'name': 'segregate'}, {'id': 16986, 'synset': 'segregator.n.01', 'name': 'segregator'}, {'id': 16987, 'synset': 'selectman.n.01', 'name': 'selectman'}, {'id': 16988, 'synset': 'selectwoman.n.01', 'name': 'selectwoman'}, {'id': 16989, 'synset': 'selfish_person.n.01', 'name': 'selfish_person'}, {'id': 16990, 'synset': 'self-starter.n.01', 'name': 'self-starter'}, {'id': 16991, 'synset': 'seller.n.01', 'name': 'seller'}, {'id': 16992, 'synset': 'selling_agent.n.01', 'name': 'selling_agent'}, {'id': 16993, 'synset': 'semanticist.n.01', 'name': 'semanticist'}, {'id': 16994, 'synset': 'semifinalist.n.01', 'name': 'semifinalist'}, {'id': 16995, 'synset': 'seminarian.n.01', 'name': 'seminarian'}, {'id': 16996, 'synset': 'senator.n.01', 'name': 'senator'}, {'id': 16997, 'synset': 'sendee.n.01', 'name': 'sendee'}, {'id': 16998, 'synset': 'senior.n.01', 'name': 'senior'}, {'id': 16999, 'synset': 'senior_vice_president.n.01', 'name': 'senior_vice_president'}, {'id': 17000, 'synset': 'separatist.n.01', 'name': 'separatist'}, {'id': 17001, 'synset': 'septuagenarian.n.01', 'name': 'septuagenarian'}, {'id': 17002, 'synset': 'serf.n.01', 'name': 'serf'}, {'id': 17003, 'synset': 'spree_killer.n.01', 'name': 'spree_killer'}, {'id': 17004, 'synset': 'serjeant-at-law.n.01', 'name': 'serjeant-at-law'}, {'id': 17005, 'synset': 'server.n.02', 'name': 'server'}, {'id': 17006, 'synset': 'serviceman.n.01', 'name': 'serviceman'}, {'id': 17007, 'synset': 'settler.n.01', 'name': 'settler'}, {'id': 17008, 'synset': 'settler.n.03', 'name': 'settler'}, {'id': 17009, 'synset': 'sex_symbol.n.01', 'name': 'sex_symbol'}, {'id': 17010, 'synset': 'sexton.n.02', 'name': 'sexton'}, {'id': 17011, 'synset': 'shaheed.n.01', 'name': 'shaheed'}, {'id': 17012, 'synset': 'shakespearian.n.01', 'name': 'Shakespearian'}, {'id': 17013, 'synset': 'shanghaier.n.01', 'name': 'shanghaier'}, {'id': 17014, 'synset': 'sharecropper.n.01', 'name': 'sharecropper'}, {'id': 17015, 'synset': 'shaver.n.01', 'name': 'shaver'}, {'id': 17016, 'synset': 'shavian.n.01', 'name': 'Shavian'}, {'id': 17017, 'synset': 'sheep.n.02', 'name': 'sheep'}, {'id': 17018, 'synset': 'sheik.n.01', 'name': 'sheik'}, {'id': 17019, 'synset': 'shelver.n.01', 'name': 'shelver'}, {'id': 17020, 'synset': 'shepherd.n.01', 'name': 'shepherd'}, {'id': 17021, 'synset': 'ship-breaker.n.01', 'name': 'ship-breaker'}, {'id': 17022, 'synset': 'shipmate.n.01', 'name': 'shipmate'}, {'id': 17023, 'synset': 'shipowner.n.01', 'name': 'shipowner'}, {'id': 17024, 'synset': 'shipping_agent.n.01', 'name': 'shipping_agent'}, {'id': 17025, 'synset': 'shirtmaker.n.01', 'name': 'shirtmaker'}, {'id': 17026, 'synset': 'shogun.n.01', 'name': 'shogun'}, {'id': 17027, 'synset': 'shopaholic.n.01', 'name': 'shopaholic'}, {'id': 17028, 'synset': 'shop_girl.n.01', 'name': 'shop_girl'}, {'id': 17029, 'synset': 'shop_steward.n.01', 'name': 'shop_steward'}, {'id': 17030, 'synset': 'shot_putter.n.01', 'name': 'shot_putter'}, {'id': 17031, 'synset': 'shrew.n.01', 'name': 'shrew'}, {'id': 17032, 'synset': 'shuffler.n.01', 'name': 'shuffler'}, {'id': 17033, 'synset': 'shyster.n.01', 'name': 'shyster'}, {'id': 17034, 'synset': 'sibling.n.01', 'name': 'sibling'}, {'id': 17035, 'synset': 'sick_person.n.01', 'name': 'sick_person'}, {'id': 17036, 'synset': 'sightreader.n.01', 'name': 'sightreader'}, {'id': 17037, 'synset': 'signaler.n.01', 'name': 'signaler'}, {'id': 17038, 'synset': 'signer.n.01', 'name': 'signer'}, {'id': 17039, 'synset': 'signor.n.01', 'name': 'signor'}, {'id': 17040, 'synset': 'signora.n.01', 'name': 'signora'}, {'id': 17041, 'synset': 'signore.n.01', 'name': 'signore'}, {'id': 17042, 'synset': 'signorina.n.01', 'name': 'signorina'}, {'id': 17043, 'synset': 'silent_partner.n.01', 'name': 'silent_partner'}, {'id': 17044, 'synset': 'addle-head.n.01', 'name': 'addle-head'}, {'id': 17045, 'synset': 'simperer.n.01', 'name': 'simperer'}, {'id': 17046, 'synset': 'singer.n.01', 'name': 'singer'}, {'id': 17047, 'synset': 'sinologist.n.01', 'name': 'Sinologist'}, {'id': 17048, 'synset': 'sipper.n.01', 'name': 'sipper'}, {'id': 17049, 'synset': 'sirrah.n.01', 'name': 'sirrah'}, {'id': 17050, 'synset': 'sister.n.02', 'name': 'Sister'}, {'id': 17051, 'synset': 'sister.n.01', 'name': 'sister'}, {'id': 17052, 'synset': 'waverer.n.01', 'name': 'waverer'}, {'id': 17053, 'synset': 'sitar_player.n.01', 'name': 'sitar_player'}, {'id': 17054, 'synset': 'sixth-former.n.01', 'name': 'sixth-former'}, {'id': 17055, 'synset': 'skateboarder.n.01', 'name': 'skateboarder'}, {'id': 17056, 'synset': 'skeptic.n.01', 'name': 'skeptic'}, {'id': 17057, 'synset': 'sketcher.n.01', 'name': 'sketcher'}, {'id': 17058, 'synset': 'skidder.n.02', 'name': 'skidder'}, {'id': 17059, 'synset': 'skier.n.01', 'name': 'skier'}, {'id': 17060, 'synset': 'skinny-dipper.n.01', 'name': 'skinny-dipper'}, {'id': 17061, 'synset': 'skin-diver.n.01', 'name': 'skin-diver'}, {'id': 17062, 'synset': 'skinhead.n.01', 'name': 'skinhead'}, {'id': 17063, 'synset': 'slasher.n.01', 'name': 'slasher'}, {'id': 17064, 'synset': 'slattern.n.02', 'name': 'slattern'}, {'id': 17065, 'synset': 'sleeper.n.01', 'name': 'sleeper'}, {'id': 17066, 'synset': 'sleeper.n.02', 'name': 'sleeper'}, {'id': 17067, 'synset': 'sleeping_beauty.n.02', 'name': 'sleeping_beauty'}, {'id': 17068, 'synset': 'sleuth.n.01', 'name': 'sleuth'}, {'id': 17069, 'synset': 'slob.n.01', 'name': 'slob'}, {'id': 17070, 'synset': 'sloganeer.n.01', 'name': 'sloganeer'}, {'id': 17071, 'synset': 'slopseller.n.01', 'name': 'slopseller'}, {'id': 17072, 'synset': 'smasher.n.02', 'name': 'smasher'}, {'id': 17073, 'synset': 'smirker.n.01', 'name': 'smirker'}, {'id': 17074, 'synset': 'smith.n.10', 'name': 'smith'}, {'id': 17075, 'synset': 'smoothie.n.01', 'name': 'smoothie'}, {'id': 17076, 'synset': 'smuggler.n.01', 'name': 'smuggler'}, {'id': 17077, 'synset': 'sneezer.n.01', 'name': 'sneezer'}, {'id': 17078, 'synset': 'snob.n.01', 'name': 'snob'}, {'id': 17079, 'synset': 'snoop.n.01', 'name': 'snoop'}, {'id': 17080, 'synset': 'snorer.n.01', 'name': 'snorer'}, {'id': 17081, 'synset': 'sob_sister.n.01', 'name': 'sob_sister'}, {'id': 17082, 'synset': 'soccer_player.n.01', 'name': 'soccer_player'}, {'id': 17083, 'synset': 'social_anthropologist.n.01', 'name': 'social_anthropologist'}, {'id': 17084, 'synset': 'social_climber.n.01', 'name': 'social_climber'}, {'id': 17085, 'synset': 'socialist.n.01', 'name': 'socialist'}, {'id': 17086, 'synset': 'socializer.n.01', 'name': 'socializer'}, {'id': 17087, 'synset': 'social_scientist.n.01', 'name': 'social_scientist'}, {'id': 17088, 'synset': 'social_secretary.n.01', 'name': 'social_secretary'}, {'id': 17089, 'synset': 'socinian.n.01', 'name': 'Socinian'}, {'id': 17090, 'synset': 'sociolinguist.n.01', 'name': 'sociolinguist'}, {'id': 17091, 'synset': 'sociologist.n.01', 'name': 'sociologist'}, {'id': 17092, 'synset': 'soda_jerk.n.01', 'name': 'soda_jerk'}, {'id': 17093, 'synset': 'sodalist.n.01', 'name': 'sodalist'}, {'id': 17094, 'synset': 'sodomite.n.01', 'name': 'sodomite'}, {'id': 17095, 'synset': 'soldier.n.01', 'name': 'soldier'}, {'id': 17096, 'synset': 'son.n.01', 'name': 'son'}, {'id': 17097, 'synset': 'songster.n.02', 'name': 'songster'}, {'id': 17098, 'synset': 'songstress.n.01', 'name': 'songstress'}, {'id': 17099, 'synset': 'songwriter.n.01', 'name': 'songwriter'}, {'id': 17100, 'synset': 'sorcerer.n.01', 'name': 'sorcerer'}, {'id': 17101, 'synset': 'sorehead.n.01', 'name': 'sorehead'}, {'id': 17102, 'synset': 'soul_mate.n.01', 'name': 'soul_mate'}, {'id': 17103, 'synset': 'southern_baptist.n.01', 'name': 'Southern_Baptist'}, {'id': 17104, 'synset': 'sovereign.n.01', 'name': 'sovereign'}, {'id': 17105, 'synset': 'spacewalker.n.01', 'name': 'spacewalker'}, {'id': 17106, 'synset': 'spanish_american.n.01', 'name': 'Spanish_American'}, {'id': 17107, 'synset': 'sparring_partner.n.01', 'name': 'sparring_partner'}, {'id': 17108, 'synset': 'spastic.n.01', 'name': 'spastic'}, {'id': 17109, 'synset': 'speaker.n.01', 'name': 'speaker'}, {'id': 17110, 'synset': 'native_speaker.n.01', 'name': 'native_speaker'}, {'id': 17111, 'synset': 'speaker.n.03', 'name': 'Speaker'}, {'id': 17112, 'synset': 'speechwriter.n.01', 'name': 'speechwriter'}, {'id': 17113, 'synset': 'specialist.n.02', 'name': 'specialist'}, {'id': 17114, 'synset': 'specifier.n.01', 'name': 'specifier'}, {'id': 17115, 'synset': 'spectator.n.01', 'name': 'spectator'}, {'id': 17116, 'synset': 'speech_therapist.n.01', 'name': 'speech_therapist'}, {'id': 17117, 'synset': 'speedskater.n.01', 'name': 'speedskater'}, {'id': 17118, 'synset': 'spellbinder.n.01', 'name': 'spellbinder'}, {'id': 17119, 'synset': 'sphinx.n.01', 'name': 'sphinx'}, {'id': 17120, 'synset': 'spinster.n.01', 'name': 'spinster'}, {'id': 17121, 'synset': 'split_end.n.01', 'name': 'split_end'}, {'id': 17122, 'synset': 'sport.n.05', 'name': 'sport'}, {'id': 17123, 'synset': 'sport.n.03', 'name': 'sport'}, {'id': 17124, 'synset': 'sporting_man.n.02', 'name': 'sporting_man'}, {'id': 17125, 'synset': 'sports_announcer.n.01', 'name': 'sports_announcer'}, {'id': 17126, 'synset': 'sports_editor.n.01', 'name': 'sports_editor'}, {'id': 17127, 'synset': 'sprog.n.02', 'name': 'sprog'}, {'id': 17128, 'synset': 'square_dancer.n.01', 'name': 'square_dancer'}, {'id': 17129, 'synset': 'square_shooter.n.01', 'name': 'square_shooter'}, {'id': 17130, 'synset': 'squatter.n.02', 'name': 'squatter'}, {'id': 17131, 'synset': 'squire.n.02', 'name': 'squire'}, {'id': 17132, 'synset': 'squire.n.01', 'name': 'squire'}, {'id': 17133, 'synset': 'staff_member.n.01', 'name': 'staff_member'}, {'id': 17134, 'synset': 'staff_sergeant.n.01', 'name': 'staff_sergeant'}, {'id': 17135, 'synset': 'stage_director.n.01', 'name': 'stage_director'}, {'id': 17136, 'synset': 'stainer.n.01', 'name': 'stainer'}, {'id': 17137, 'synset': 'stakeholder.n.01', 'name': 'stakeholder'}, {'id': 17138, 'synset': 'stalker.n.02', 'name': 'stalker'}, {'id': 17139, 'synset': 'stalking-horse.n.01', 'name': 'stalking-horse'}, {'id': 17140, 'synset': 'stammerer.n.01', 'name': 'stammerer'}, {'id': 17141, 'synset': 'stamper.n.02', 'name': 'stamper'}, {'id': 17142, 'synset': 'standee.n.01', 'name': 'standee'}, {'id': 17143, 'synset': 'stand-in.n.01', 'name': 'stand-in'}, {'id': 17144, 'synset': 'star.n.04', 'name': 'star'}, {'id': 17145, 'synset': 'starlet.n.01', 'name': 'starlet'}, {'id': 17146, 'synset': 'starter.n.03', 'name': 'starter'}, {'id': 17147, 'synset': 'statesman.n.01', 'name': 'statesman'}, {'id': 17148, 'synset': 'state_treasurer.n.01', 'name': 'state_treasurer'}, {'id': 17149, 'synset': 'stationer.n.01', 'name': 'stationer'}, {'id': 17150, 'synset': 'stenographer.n.01', 'name': 'stenographer'}, {'id': 17151, 'synset': 'stentor.n.01', 'name': 'stentor'}, {'id': 17152, 'synset': 'stepbrother.n.01', 'name': 'stepbrother'}, {'id': 17153, 'synset': 'stepmother.n.01', 'name': 'stepmother'}, {'id': 17154, 'synset': 'stepparent.n.01', 'name': 'stepparent'}, {'id': 17155, 'synset': 'stevedore.n.01', 'name': 'stevedore'}, {'id': 17156, 'synset': 'steward.n.01', 'name': 'steward'}, {'id': 17157, 'synset': 'steward.n.03', 'name': 'steward'}, {'id': 17158, 'synset': 'steward.n.02', 'name': 'steward'}, {'id': 17159, 'synset': 'stickler.n.01', 'name': 'stickler'}, {'id': 17160, 'synset': 'stiff.n.01', 'name': 'stiff'}, {'id': 17161, 'synset': 'stifler.n.01', 'name': 'stifler'}, {'id': 17162, 'synset': 'stipendiary.n.01', 'name': 'stipendiary'}, {'id': 17163, 'synset': 'stitcher.n.01', 'name': 'stitcher'}, {'id': 17164, 'synset': 'stockjobber.n.01', 'name': 'stockjobber'}, {'id': 17165, 'synset': 'stock_trader.n.01', 'name': 'stock_trader'}, {'id': 17166, 'synset': 'stockist.n.01', 'name': 'stockist'}, {'id': 17167, 'synset': 'stoker.n.02', 'name': 'stoker'}, {'id': 17168, 'synset': 'stooper.n.02', 'name': 'stooper'}, {'id': 17169, 'synset': 'store_detective.n.01', 'name': 'store_detective'}, {'id': 17170, 'synset': 'strafer.n.01', 'name': 'strafer'}, {'id': 17171, 'synset': 'straight_man.n.01', 'name': 'straight_man'}, {'id': 17172, 'synset': 'stranger.n.01', 'name': 'stranger'}, {'id': 17173, 'synset': 'stranger.n.02', 'name': 'stranger'}, {'id': 17174, 'synset': 'strategist.n.01', 'name': 'strategist'}, {'id': 17175, 'synset': 'straw_boss.n.01', 'name': 'straw_boss'}, {'id': 17176, 'synset': 'streetwalker.n.01', 'name': 'streetwalker'}, {'id': 17177, 'synset': 'stretcher-bearer.n.01', 'name': 'stretcher-bearer'}, {'id': 17178, 'synset': 'struggler.n.01', 'name': 'struggler'}, {'id': 17179, 'synset': 'stud.n.01', 'name': 'stud'}, {'id': 17180, 'synset': 'student.n.01', 'name': 'student'}, {'id': 17181, 'synset': 'stumblebum.n.01', 'name': 'stumblebum'}, {'id': 17182, 'synset': 'stylist.n.01', 'name': 'stylist'}, {'id': 17183, 'synset': 'subaltern.n.01', 'name': 'subaltern'}, {'id': 17184, 'synset': 'subcontractor.n.01', 'name': 'subcontractor'}, {'id': 17185, 'synset': 'subduer.n.01', 'name': 'subduer'}, {'id': 17186, 'synset': 'subject.n.06', 'name': 'subject'}, {'id': 17187, 'synset': 'subordinate.n.01', 'name': 'subordinate'}, {'id': 17188, 'synset': 'substitute.n.02', 'name': 'substitute'}, {'id': 17189, 'synset': 'successor.n.03', 'name': 'successor'}, {'id': 17190, 'synset': 'successor.n.01', 'name': 'successor'}, {'id': 17191, 'synset': 'succorer.n.01', 'name': 'succorer'}, {'id': 17192, 'synset': 'sufi.n.01', 'name': 'Sufi'}, {'id': 17193, 'synset': 'suffragan.n.01', 'name': 'suffragan'}, {'id': 17194, 'synset': 'suffragette.n.01', 'name': 'suffragette'}, {'id': 17195, 'synset': 'sugar_daddy.n.01', 'name': 'sugar_daddy'}, {'id': 17196, 'synset': 'suicide_bomber.n.01', 'name': 'suicide_bomber'}, {'id': 17197, 'synset': 'suitor.n.01', 'name': 'suitor'}, {'id': 17198, 'synset': 'sumo_wrestler.n.01', 'name': 'sumo_wrestler'}, {'id': 17199, 'synset': 'sunbather.n.01', 'name': 'sunbather'}, {'id': 17200, 'synset': 'sundowner.n.01', 'name': 'sundowner'}, {'id': 17201, 'synset': 'super_heavyweight.n.01', 'name': 'super_heavyweight'}, {'id': 17202, 'synset': 'superior.n.01', 'name': 'superior'}, {'id': 17203, 'synset': 'supermom.n.01', 'name': 'supermom'}, {'id': 17204, 'synset': 'supernumerary.n.02', 'name': 'supernumerary'}, {'id': 17205, 'synset': 'supremo.n.01', 'name': 'supremo'}, {'id': 17206, 'synset': 'surgeon.n.01', 'name': 'surgeon'}, {'id': 17207, 'synset': 'surgeon_general.n.02', 'name': 'Surgeon_General'}, {'id': 17208, 'synset': 'surgeon_general.n.01', 'name': 'Surgeon_General'}, {'id': 17209, 'synset': 'surpriser.n.01', 'name': 'surpriser'}, {'id': 17210, 'synset': 'surveyor.n.01', 'name': 'surveyor'}, {'id': 17211, 'synset': 'surveyor.n.02', 'name': 'surveyor'}, {'id': 17212, 'synset': 'survivor.n.01', 'name': 'survivor'}, {'id': 17213, 'synset': 'sutler.n.01', 'name': 'sutler'}, {'id': 17214, 'synset': 'sweeper.n.01', 'name': 'sweeper'}, {'id': 17215, 'synset': 'sweetheart.n.01', 'name': 'sweetheart'}, {'id': 17216, 'synset': 'swinger.n.02', 'name': 'swinger'}, {'id': 17217, 'synset': 'switcher.n.01', 'name': 'switcher'}, {'id': 17218, 'synset': 'swot.n.01', 'name': 'swot'}, {'id': 17219, 'synset': 'sycophant.n.01', 'name': 'sycophant'}, {'id': 17220, 'synset': 'sylph.n.01', 'name': 'sylph'}, {'id': 17221, 'synset': 'sympathizer.n.02', 'name': 'sympathizer'}, {'id': 17222, 'synset': 'symphonist.n.01', 'name': 'symphonist'}, {'id': 17223, 'synset': 'syncopator.n.01', 'name': 'syncopator'}, {'id': 17224, 'synset': 'syndic.n.01', 'name': 'syndic'}, {'id': 17225, 'synset': 'tactician.n.01', 'name': 'tactician'}, {'id': 17226, 'synset': 'tagger.n.02', 'name': 'tagger'}, {'id': 17227, 'synset': 'tailback.n.01', 'name': 'tailback'}, {'id': 17228, 'synset': 'tallyman.n.02', 'name': 'tallyman'}, {'id': 17229, 'synset': 'tallyman.n.01', 'name': 'tallyman'}, {'id': 17230, 'synset': 'tanker.n.02', 'name': 'tanker'}, {'id': 17231, 'synset': 'tapper.n.04', 'name': 'tapper'}, {'id': 17232, 'synset': 'tartuffe.n.01', 'name': 'Tartuffe'}, {'id': 17233, 'synset': 'tarzan.n.01', 'name': 'Tarzan'}, {'id': 17234, 'synset': 'taster.n.01', 'name': 'taster'}, {'id': 17235, 'synset': 'tax_assessor.n.01', 'name': 'tax_assessor'}, {'id': 17236, 'synset': 'taxer.n.01', 'name': 'taxer'}, {'id': 17237, 'synset': 'taxi_dancer.n.01', 'name': 'taxi_dancer'}, {'id': 17238, 'synset': 'taxonomist.n.01', 'name': 'taxonomist'}, {'id': 17239, 'synset': 'teacher.n.01', 'name': 'teacher'}, {'id': 17240, 'synset': 'teaching_fellow.n.01', 'name': 'teaching_fellow'}, {'id': 17241, 'synset': 'tearaway.n.01', 'name': 'tearaway'}, {'id': 17242, 'synset': 'technical_sergeant.n.01', 'name': 'technical_sergeant'}, {'id': 17243, 'synset': 'technician.n.02', 'name': 'technician'}, {'id': 17244, 'synset': 'ted.n.01', 'name': 'Ted'}, {'id': 17245, 'synset': 'teetotaler.n.01', 'name': 'teetotaler'}, {'id': 17246, 'synset': 'television_reporter.n.01', 'name': 'television_reporter'}, {'id': 17247, 'synset': 'temporizer.n.01', 'name': 'temporizer'}, {'id': 17248, 'synset': 'tempter.n.01', 'name': 'tempter'}, {'id': 17249, 'synset': 'term_infant.n.01', 'name': 'term_infant'}, {'id': 17250, 'synset': 'toiler.n.01', 'name': 'toiler'}, {'id': 17251, 'synset': 'tenant.n.01', 'name': 'tenant'}, {'id': 17252, 'synset': 'tenant.n.02', 'name': 'tenant'}, {'id': 17253, 'synset': 'tenderfoot.n.01', 'name': 'tenderfoot'}, {'id': 17254, 'synset': 'tennis_player.n.01', 'name': 'tennis_player'}, {'id': 17255, 'synset': 'tennis_pro.n.01', 'name': 'tennis_pro'}, {'id': 17256, 'synset': 'tenor_saxophonist.n.01', 'name': 'tenor_saxophonist'}, {'id': 17257, 'synset': 'termer.n.01', 'name': 'termer'}, {'id': 17258, 'synset': 'terror.n.02', 'name': 'terror'}, {'id': 17259, 'synset': 'tertigravida.n.01', 'name': 'tertigravida'}, {'id': 17260, 'synset': 'testator.n.01', 'name': 'testator'}, {'id': 17261, 'synset': 'testatrix.n.01', 'name': 'testatrix'}, {'id': 17262, 'synset': 'testee.n.01', 'name': 'testee'}, {'id': 17263, 'synset': 'test-tube_baby.n.01', 'name': 'test-tube_baby'}, {'id': 17264, 'synset': 'texas_ranger.n.01', 'name': 'Texas_Ranger'}, {'id': 17265, 'synset': 'thane.n.02', 'name': 'thane'}, {'id': 17266, 'synset': 'theatrical_producer.n.01', 'name': 'theatrical_producer'}, {'id': 17267, 'synset': 'theologian.n.01', 'name': 'theologian'}, {'id': 17268, 'synset': 'theorist.n.01', 'name': 'theorist'}, {'id': 17269, 'synset': 'theosophist.n.01', 'name': 'theosophist'}, {'id': 17270, 'synset': 'therapist.n.01', 'name': 'therapist'}, {'id': 17271, 'synset': 'thessalonian.n.01', 'name': 'Thessalonian'}, {'id': 17272, 'synset': 'thinker.n.01', 'name': 'thinker'}, {'id': 17273, 'synset': 'thinker.n.02', 'name': 'thinker'}, {'id': 17274, 'synset': 'thrower.n.02', 'name': 'thrower'}, {'id': 17275, 'synset': 'thurifer.n.01', 'name': 'thurifer'}, {'id': 17276, 'synset': 'ticket_collector.n.01', 'name': 'ticket_collector'}, {'id': 17277, 'synset': 'tight_end.n.01', 'name': 'tight_end'}, {'id': 17278, 'synset': 'tiler.n.01', 'name': 'tiler'}, {'id': 17279, 'synset': 'timekeeper.n.01', 'name': 'timekeeper'}, {'id': 17280, 'synset': 'timorese.n.01', 'name': 'Timorese'}, {'id': 17281, 'synset': 'tinkerer.n.01', 'name': 'tinkerer'}, {'id': 17282, 'synset': 'tinsmith.n.01', 'name': 'tinsmith'}, {'id': 17283, 'synset': 'tinter.n.01', 'name': 'tinter'}, {'id': 17284, 'synset': 'tippler.n.01', 'name': 'tippler'}, {'id': 17285, 'synset': 'tipster.n.01', 'name': 'tipster'}, {'id': 17286, 'synset': 't-man.n.01', 'name': 'T-man'}, {'id': 17287, 'synset': 'toastmaster.n.01', 'name': 'toastmaster'}, {'id': 17288, 'synset': 'toast_mistress.n.01', 'name': 'toast_mistress'}, {'id': 17289, 'synset': 'tobogganist.n.01', 'name': 'tobogganist'}, {'id': 17290, 'synset': 'tomboy.n.01', 'name': 'tomboy'}, {'id': 17291, 'synset': 'toolmaker.n.01', 'name': 'toolmaker'}, {'id': 17292, 'synset': 'torchbearer.n.01', 'name': 'torchbearer'}, {'id': 17293, 'synset': 'tory.n.01', 'name': 'Tory'}, {'id': 17294, 'synset': 'tory.n.02', 'name': 'Tory'}, {'id': 17295, 'synset': 'tosser.n.02', 'name': 'tosser'}, {'id': 17296, 'synset': 'tosser.n.01', 'name': 'tosser'}, {'id': 17297, 'synset': 'totalitarian.n.01', 'name': 'totalitarian'}, {'id': 17298, 'synset': 'tourist.n.01', 'name': 'tourist'}, {'id': 17299, 'synset': 'tout.n.02', 'name': 'tout'}, {'id': 17300, 'synset': 'tout.n.01', 'name': 'tout'}, {'id': 17301, 'synset': 'tovarich.n.01', 'name': 'tovarich'}, {'id': 17302, 'synset': 'towhead.n.01', 'name': 'towhead'}, {'id': 17303, 'synset': 'town_clerk.n.01', 'name': 'town_clerk'}, {'id': 17304, 'synset': 'town_crier.n.01', 'name': 'town_crier'}, {'id': 17305, 'synset': 'townsman.n.02', 'name': 'townsman'}, {'id': 17306, 'synset': 'toxicologist.n.01', 'name': 'toxicologist'}, {'id': 17307, 'synset': 'track_star.n.01', 'name': 'track_star'}, {'id': 17308, 'synset': 'trader.n.01', 'name': 'trader'}, {'id': 17309, 'synset': 'trade_unionist.n.01', 'name': 'trade_unionist'}, {'id': 17310, 'synset': 'traditionalist.n.01', 'name': 'traditionalist'}, {'id': 17311, 'synset': 'traffic_cop.n.01', 'name': 'traffic_cop'}, {'id': 17312, 'synset': 'tragedian.n.02', 'name': 'tragedian'}, {'id': 17313, 'synset': 'tragedian.n.01', 'name': 'tragedian'}, {'id': 17314, 'synset': 'tragedienne.n.01', 'name': 'tragedienne'}, {'id': 17315, 'synset': 'trail_boss.n.01', 'name': 'trail_boss'}, {'id': 17316, 'synset': 'trainer.n.01', 'name': 'trainer'}, {'id': 17317, 'synset': 'traitor.n.01', 'name': 'traitor'}, {'id': 17318, 'synset': 'traitress.n.01', 'name': 'traitress'}, {'id': 17319, 'synset': 'transactor.n.01', 'name': 'transactor'}, {'id': 17320, 'synset': 'transcriber.n.03', 'name': 'transcriber'}, {'id': 17321, 'synset': 'transfer.n.02', 'name': 'transfer'}, {'id': 17322, 'synset': 'transferee.n.01', 'name': 'transferee'}, {'id': 17323, 'synset': 'translator.n.01', 'name': 'translator'}, {'id': 17324, 'synset': 'transvestite.n.01', 'name': 'transvestite'}, {'id': 17325, 'synset': 'traveling_salesman.n.01', 'name': 'traveling_salesman'}, {'id': 17326, 'synset': 'traverser.n.01', 'name': 'traverser'}, {'id': 17327, 'synset': 'trawler.n.01', 'name': 'trawler'}, {'id': 17328, 'synset': 'treasury.n.04', 'name': 'Treasury'}, {'id': 17329, 'synset': 'trencher.n.01', 'name': 'trencher'}, {'id': 17330, 'synset': 'trend-setter.n.01', 'name': 'trend-setter'}, {'id': 17331, 'synset': 'tribesman.n.01', 'name': 'tribesman'}, {'id': 17332, 'synset': 'trier.n.02', 'name': 'trier'}, {'id': 17333, 'synset': 'trifler.n.01', 'name': 'trifler'}, {'id': 17334, 'synset': 'trooper.n.02', 'name': 'trooper'}, {'id': 17335, 'synset': 'trooper.n.03', 'name': 'trooper'}, {'id': 17336, 'synset': 'trotskyite.n.01', 'name': 'Trotskyite'}, {'id': 17337, 'synset': 'truant.n.01', 'name': 'truant'}, {'id': 17338, 'synset': 'trumpeter.n.01', 'name': 'trumpeter'}, {'id': 17339, 'synset': 'trusty.n.01', 'name': 'trusty'}, {'id': 17340, 'synset': 'tudor.n.03', 'name': 'Tudor'}, {'id': 17341, 'synset': 'tumbler.n.01', 'name': 'tumbler'}, {'id': 17342, 'synset': 'tutee.n.01', 'name': 'tutee'}, {'id': 17343, 'synset': 'twin.n.01', 'name': 'twin'}, {'id': 17344, 'synset': 'two-timer.n.01', 'name': 'two-timer'}, {'id': 17345, 'synset': 'tyke.n.01', 'name': 'Tyke'}, {'id': 17346, 'synset': 'tympanist.n.01', 'name': 'tympanist'}, {'id': 17347, 'synset': 'typist.n.01', 'name': 'typist'}, {'id': 17348, 'synset': 'tyrant.n.01', 'name': 'tyrant'}, {'id': 17349, 'synset': 'umpire.n.01', 'name': 'umpire'}, {'id': 17350, 'synset': 'understudy.n.01', 'name': 'understudy'}, {'id': 17351, 'synset': 'undesirable.n.01', 'name': 'undesirable'}, {'id': 17352, 'synset': 'unicyclist.n.01', 'name': 'unicyclist'}, {'id': 17353, 'synset': 'unilateralist.n.01', 'name': 'unilateralist'}, {'id': 17354, 'synset': 'unitarian.n.01', 'name': 'Unitarian'}, {'id': 17355, 'synset': 'arminian.n.01', 'name': 'Arminian'}, {'id': 17356, 'synset': 'universal_donor.n.01', 'name': 'universal_donor'}, {'id': 17357, 'synset': 'unix_guru.n.01', 'name': 'UNIX_guru'}, {'id': 17358, 'synset': 'unknown_soldier.n.01', 'name': 'Unknown_Soldier'}, {'id': 17359, 'synset': 'upsetter.n.01', 'name': 'upsetter'}, {'id': 17360, 'synset': 'upstager.n.01', 'name': 'upstager'}, {'id': 17361, 'synset': 'upstart.n.02', 'name': 'upstart'}, {'id': 17362, 'synset': 'upstart.n.01', 'name': 'upstart'}, {'id': 17363, 'synset': 'urchin.n.01', 'name': 'urchin'}, {'id': 17364, 'synset': 'urologist.n.01', 'name': 'urologist'}, {'id': 17365, 'synset': 'usherette.n.01', 'name': 'usherette'}, {'id': 17366, 'synset': 'usher.n.02', 'name': 'usher'}, {'id': 17367, 'synset': 'usurper.n.01', 'name': 'usurper'}, {'id': 17368, 'synset': 'utility_man.n.01', 'name': 'utility_man'}, {'id': 17369, 'synset': 'utilizer.n.01', 'name': 'utilizer'}, {'id': 17370, 'synset': 'utopian.n.01', 'name': 'Utopian'}, {'id': 17371, 'synset': 'uxoricide.n.01', 'name': 'uxoricide'}, {'id': 17372, 'synset': 'vacationer.n.01', 'name': 'vacationer'}, {'id': 17373, 'synset': 'valedictorian.n.01', 'name': 'valedictorian'}, {'id': 17374, 'synset': 'valley_girl.n.01', 'name': 'valley_girl'}, {'id': 17375, 'synset': 'vaulter.n.01', 'name': 'vaulter'}, {'id': 17376, 'synset': 'vegetarian.n.01', 'name': 'vegetarian'}, {'id': 17377, 'synset': 'vegan.n.01', 'name': 'vegan'}, {'id': 17378, 'synset': 'venerator.n.01', 'name': 'venerator'}, {'id': 17379, 'synset': 'venture_capitalist.n.01', 'name': 'venture_capitalist'}, {'id': 17380, 'synset': 'venturer.n.01', 'name': 'venturer'}, {'id': 17381, 'synset': 'vermin.n.01', 'name': 'vermin'}, {'id': 17382, 'synset': 'very_important_person.n.01', 'name': 'very_important_person'}, {'id': 17383, 'synset': 'vibist.n.01', 'name': 'vibist'}, {'id': 17384, 'synset': 'vicar.n.01', 'name': 'vicar'}, {'id': 17385, 'synset': 'vicar.n.03', 'name': 'vicar'}, {'id': 17386, 'synset': 'vicar-general.n.01', 'name': 'vicar-general'}, {'id': 17387, 'synset': 'vice_chancellor.n.01', 'name': 'vice_chancellor'}, {'id': 17388, 'synset': 'vicegerent.n.01', 'name': 'vicegerent'}, {'id': 17389, 'synset': 'vice_president.n.01', 'name': 'vice_president'}, {'id': 17390, 'synset': 'vice-regent.n.01', 'name': 'vice-regent'}, {'id': 17391, 'synset': 'victim.n.02', 'name': 'victim'}, {'id': 17392, 'synset': 'victorian.n.01', 'name': 'Victorian'}, {'id': 17393, 'synset': 'victualer.n.01', 'name': 'victualer'}, {'id': 17394, 'synset': 'vigilante.n.01', 'name': 'vigilante'}, {'id': 17395, 'synset': 'villager.n.01', 'name': 'villager'}, {'id': 17396, 'synset': 'vintager.n.01', 'name': 'vintager'}, {'id': 17397, 'synset': 'vintner.n.01', 'name': 'vintner'}, {'id': 17398, 'synset': 'violator.n.02', 'name': 'violator'}, {'id': 17399, 'synset': 'violator.n.01', 'name': 'violator'}, {'id': 17400, 'synset': 'violist.n.01', 'name': 'violist'}, {'id': 17401, 'synset': 'virago.n.01', 'name': 'virago'}, {'id': 17402, 'synset': 'virologist.n.01', 'name': 'virologist'}, {'id': 17403, 'synset': 'visayan.n.01', 'name': 'Visayan'}, {'id': 17404, 'synset': 'viscountess.n.01', 'name': 'viscountess'}, {'id': 17405, 'synset': 'viscount.n.01', 'name': 'viscount'}, {'id': 17406, 'synset': 'visigoth.n.01', 'name': 'Visigoth'}, {'id': 17407, 'synset': 'visionary.n.01', 'name': 'visionary'}, {'id': 17408, 'synset': 'visiting_fireman.n.01', 'name': 'visiting_fireman'}, {'id': 17409, 'synset': 'visiting_professor.n.01', 'name': 'visiting_professor'}, {'id': 17410, 'synset': 'visualizer.n.01', 'name': 'visualizer'}, {'id': 17411, 'synset': 'vixen.n.01', 'name': 'vixen'}, {'id': 17412, 'synset': 'vizier.n.01', 'name': 'vizier'}, {'id': 17413, 'synset': 'voicer.n.01', 'name': 'voicer'}, {'id': 17414, 'synset': 'volunteer.n.02', 'name': 'volunteer'}, {'id': 17415, 'synset': 'volunteer.n.01', 'name': 'volunteer'}, {'id': 17416, 'synset': 'votary.n.02', 'name': 'votary'}, {'id': 17417, 'synset': 'votary.n.01', 'name': 'votary'}, {'id': 17418, 'synset': 'vouchee.n.01', 'name': 'vouchee'}, {'id': 17419, 'synset': 'vower.n.01', 'name': 'vower'}, {'id': 17420, 'synset': 'voyager.n.01', 'name': 'voyager'}, {'id': 17421, 'synset': 'voyeur.n.01', 'name': 'voyeur'}, {'id': 17422, 'synset': 'vulcanizer.n.01', 'name': 'vulcanizer'}, {'id': 17423, 'synset': 'waffler.n.01', 'name': 'waffler'}, {'id': 17424, 'synset': 'wagnerian.n.01', 'name': 'Wagnerian'}, {'id': 17425, 'synset': 'waif.n.01', 'name': 'waif'}, {'id': 17426, 'synset': 'wailer.n.01', 'name': 'wailer'}, {'id': 17427, 'synset': 'waiter.n.01', 'name': 'waiter'}, {'id': 17428, 'synset': 'waitress.n.01', 'name': 'waitress'}, {'id': 17429, 'synset': 'walking_delegate.n.01', 'name': 'walking_delegate'}, {'id': 17430, 'synset': 'walk-on.n.01', 'name': 'walk-on'}, {'id': 17431, 'synset': 'wallah.n.01', 'name': 'wallah'}, {'id': 17432, 'synset': 'wally.n.01', 'name': 'wally'}, {'id': 17433, 'synset': 'waltzer.n.01', 'name': 'waltzer'}, {'id': 17434, 'synset': 'wanderer.n.01', 'name': 'wanderer'}, {'id': 17435, 'synset': 'wandering_jew.n.01', 'name': 'Wandering_Jew'}, {'id': 17436, 'synset': 'wanton.n.01', 'name': 'wanton'}, {'id': 17437, 'synset': 'warrantee.n.02', 'name': 'warrantee'}, {'id': 17438, 'synset': 'warrantee.n.01', 'name': 'warrantee'}, {'id': 17439, 'synset': 'washer.n.01', 'name': 'washer'}, {'id': 17440, 'synset': 'washerman.n.01', 'name': 'washerman'}, {'id': 17441, 'synset': 'washwoman.n.01', 'name': 'washwoman'}, {'id': 17442, 'synset': 'wassailer.n.01', 'name': 'wassailer'}, {'id': 17443, 'synset': 'wastrel.n.01', 'name': 'wastrel'}, {'id': 17444, 'synset': 'wave.n.09', 'name': 'Wave'}, {'id': 17445, 'synset': 'weatherman.n.01', 'name': 'weatherman'}, {'id': 17446, 'synset': 'weekend_warrior.n.02', 'name': 'weekend_warrior'}, {'id': 17447, 'synset': 'weeder.n.01', 'name': 'weeder'}, {'id': 17448, 'synset': 'welder.n.01', 'name': 'welder'}, {'id': 17449, 'synset': 'welfare_case.n.01', 'name': 'welfare_case'}, {'id': 17450, 'synset': 'westerner.n.01', 'name': 'westerner'}, {'id': 17451, 'synset': 'west-sider.n.01', 'name': 'West-sider'}, {'id': 17452, 'synset': 'wetter.n.02', 'name': 'wetter'}, {'id': 17453, 'synset': 'whaler.n.01', 'name': 'whaler'}, {'id': 17454, 'synset': 'whig.n.02', 'name': 'Whig'}, {'id': 17455, 'synset': 'whiner.n.01', 'name': 'whiner'}, {'id': 17456, 'synset': 'whipper-in.n.01', 'name': 'whipper-in'}, {'id': 17457, 'synset': 'whisperer.n.01', 'name': 'whisperer'}, {'id': 17458, 'synset': 'whiteface.n.02', 'name': 'whiteface'}, {'id': 17459, 'synset': 'carmelite.n.01', 'name': 'Carmelite'}, {'id': 17460, 'synset': 'augustinian.n.01', 'name': 'Augustinian'}, {'id': 17461, 'synset': 'white_hope.n.01', 'name': 'white_hope'}, {'id': 17462, 'synset': 'white_supremacist.n.01', 'name': 'white_supremacist'}, {'id': 17463, 'synset': 'whoremaster.n.02', 'name': 'whoremaster'}, {'id': 17464, 'synset': 'whoremaster.n.01', 'name': 'whoremaster'}, {'id': 17465, 'synset': 'widow.n.01', 'name': 'widow'}, {'id': 17466, 'synset': 'wife.n.01', 'name': 'wife'}, {'id': 17467, 'synset': 'wiggler.n.01', 'name': 'wiggler'}, {'id': 17468, 'synset': 'wimp.n.01', 'name': 'wimp'}, {'id': 17469, 'synset': 'wing_commander.n.01', 'name': 'wing_commander'}, {'id': 17470, 'synset': 'winger.n.01', 'name': 'winger'}, {'id': 17471, 'synset': 'winner.n.02', 'name': 'winner'}, {'id': 17472, 'synset': 'winner.n.01', 'name': 'winner'}, {'id': 17473, 'synset': 'window_dresser.n.01', 'name': 'window_dresser'}, {'id': 17474, 'synset': 'winker.n.01', 'name': 'winker'}, {'id': 17475, 'synset': 'wiper.n.01', 'name': 'wiper'}, {'id': 17476, 'synset': 'wireman.n.01', 'name': 'wireman'}, {'id': 17477, 'synset': 'wise_guy.n.01', 'name': 'wise_guy'}, {'id': 17478, 'synset': 'witch_doctor.n.01', 'name': 'witch_doctor'}, {'id': 17479, 'synset': 'withdrawer.n.05', 'name': 'withdrawer'}, {'id': 17480, 'synset': 'withdrawer.n.01', 'name': 'withdrawer'}, {'id': 17481, 'synset': 'woman.n.01', 'name': 'woman'}, {'id': 17482, 'synset': 'woman.n.02', 'name': 'woman'}, {'id': 17483, 'synset': 'wonder_boy.n.01', 'name': 'wonder_boy'}, {'id': 17484, 'synset': 'wonderer.n.01', 'name': 'wonderer'}, {'id': 17485, 'synset': 'working_girl.n.01', 'name': 'working_girl'}, {'id': 17486, 'synset': 'workman.n.01', 'name': 'workman'}, {'id': 17487, 'synset': 'workmate.n.01', 'name': 'workmate'}, {'id': 17488, 'synset': 'worldling.n.01', 'name': 'worldling'}, {'id': 17489, 'synset': 'worshiper.n.01', 'name': 'worshiper'}, {'id': 17490, 'synset': 'worthy.n.01', 'name': 'worthy'}, {'id': 17491, 'synset': 'wrecker.n.01', 'name': 'wrecker'}, {'id': 17492, 'synset': 'wright.n.07', 'name': 'wright'}, {'id': 17493, 'synset': 'write-in_candidate.n.01', 'name': 'write-in_candidate'}, {'id': 17494, 'synset': 'writer.n.01', 'name': 'writer'}, {'id': 17495, 'synset': 'wykehamist.n.01', 'name': 'Wykehamist'}, {'id': 17496, 'synset': 'yakuza.n.01', 'name': 'yakuza'}, {'id': 17497, 'synset': 'yard_bird.n.01', 'name': 'yard_bird'}, {'id': 17498, 'synset': 'yardie.n.01', 'name': 'yardie'}, {'id': 17499, 'synset': 'yardman.n.01', 'name': 'yardman'}, {'id': 17500, 'synset': 'yardmaster.n.01', 'name': 'yardmaster'}, {'id': 17501, 'synset': 'yenta.n.02', 'name': 'yenta'}, {'id': 17502, 'synset': 'yogi.n.02', 'name': 'yogi'}, {'id': 17503, 'synset': 'young_buck.n.01', 'name': 'young_buck'}, {'id': 17504, 'synset': 'young_turk.n.02', 'name': 'young_Turk'}, {'id': 17505, 'synset': 'young_turk.n.01', 'name': 'Young_Turk'}, {'id': 17506, 'synset': 'zionist.n.01', 'name': 'Zionist'}, {'id': 17507, 'synset': 'zoo_keeper.n.01', 'name': 'zoo_keeper'}, {'id': 17508, 'synset': 'genet.n.01', 'name': 'Genet'}, {'id': 17509, 'synset': 'kennan.n.01', 'name': 'Kennan'}, {'id': 17510, 'synset': 'munro.n.01', 'name': 'Munro'}, {'id': 17511, 'synset': 'popper.n.01', 'name': 'Popper'}, {'id': 17512, 'synset': 'stoker.n.01', 'name': 'Stoker'}, {'id': 17513, 'synset': 'townes.n.01', 'name': 'Townes'}, {'id': 17514, 'synset': 'dust_storm.n.01', 'name': 'dust_storm'}, {'id': 17515, 'synset': 'parhelion.n.01', 'name': 'parhelion'}, {'id': 17516, 'synset': 'snow.n.01', 'name': 'snow'}, {'id': 17517, 'synset': 'facula.n.01', 'name': 'facula'}, {'id': 17518, 'synset': 'wave.n.08', 'name': 'wave'}, {'id': 17519, 'synset': 'microflora.n.01', 'name': 'microflora'}, {'id': 17520, 'synset': 'wilding.n.01', 'name': 'wilding'}, {'id': 17521, 'synset': 'semi-climber.n.01', 'name': 'semi-climber'}, {'id': 17522, 'synset': 'volva.n.01', 'name': 'volva'}, {'id': 17523, 'synset': 'basidiocarp.n.01', 'name': 'basidiocarp'}, {'id': 17524, 'synset': 'domatium.n.01', 'name': 'domatium'}, {'id': 17525, 'synset': 'apomict.n.01', 'name': 'apomict'}, {'id': 17526, 'synset': 'aquatic.n.01', 'name': 'aquatic'}, {'id': 17527, 'synset': 'bryophyte.n.01', 'name': 'bryophyte'}, {'id': 17528, 'synset': 'acrocarp.n.01', 'name': 'acrocarp'}, {'id': 17529, 'synset': 'sphagnum.n.01', 'name': 'sphagnum'}, {'id': 17530, 'synset': 'liverwort.n.01', 'name': 'liverwort'}, {'id': 17531, 'synset': 'hepatica.n.02', 'name': 'hepatica'}, {'id': 17532, 'synset': 'pecopteris.n.01', 'name': 'pecopteris'}, {'id': 17533, 'synset': 'pteridophyte.n.01', 'name': 'pteridophyte'}, {'id': 17534, 'synset': 'fern.n.01', 'name': 'fern'}, {'id': 17535, 'synset': 'fern_ally.n.01', 'name': 'fern_ally'}, {'id': 17536, 'synset': 'spore.n.01', 'name': 'spore'}, {'id': 17537, 'synset': 'carpospore.n.01', 'name': 'carpospore'}, {'id': 17538, 'synset': 'chlamydospore.n.01', 'name': 'chlamydospore'}, {'id': 17539, 'synset': 'conidium.n.01', 'name': 'conidium'}, {'id': 17540, 'synset': 'oospore.n.01', 'name': 'oospore'}, {'id': 17541, 'synset': 'tetraspore.n.01', 'name': 'tetraspore'}, {'id': 17542, 'synset': 'zoospore.n.01', 'name': 'zoospore'}, {'id': 17543, 'synset': 'cryptogam.n.01', 'name': 'cryptogam'}, {'id': 17544, 'synset': 'spermatophyte.n.01', 'name': 'spermatophyte'}, {'id': 17545, 'synset': 'seedling.n.01', 'name': 'seedling'}, {'id': 17546, 'synset': 'annual.n.01', 'name': 'annual'}, {'id': 17547, 'synset': 'biennial.n.01', 'name': 'biennial'}, {'id': 17548, 'synset': 'perennial.n.01', 'name': 'perennial'}, {'id': 17549, 'synset': 'hygrophyte.n.01', 'name': 'hygrophyte'}, {'id': 17550, 'synset': 'gymnosperm.n.01', 'name': 'gymnosperm'}, {'id': 17551, 'synset': 'gnetum.n.01', 'name': 'gnetum'}, {'id': 17552, 'synset': 'catha_edulis.n.01', 'name': 'Catha_edulis'}, {'id': 17553, 'synset': 'ephedra.n.01', 'name': 'ephedra'}, {'id': 17554, 'synset': 'mahuang.n.01', 'name': 'mahuang'}, {'id': 17555, 'synset': 'welwitschia.n.01', 'name': 'welwitschia'}, {'id': 17556, 'synset': 'cycad.n.01', 'name': 'cycad'}, {'id': 17557, 'synset': 'sago_palm.n.02', 'name': 'sago_palm'}, {'id': 17558, 'synset': 'false_sago.n.01', 'name': 'false_sago'}, {'id': 17559, 'synset': 'zamia.n.01', 'name': 'zamia'}, {'id': 17560, 'synset': 'coontie.n.01', 'name': 'coontie'}, {'id': 17561, 'synset': 'ceratozamia.n.01', 'name': 'ceratozamia'}, {'id': 17562, 'synset': 'dioon.n.01', 'name': 'dioon'}, {'id': 17563, 'synset': 'encephalartos.n.01', 'name': 'encephalartos'}, {'id': 17564, 'synset': 'kaffir_bread.n.01', 'name': 'kaffir_bread'}, {'id': 17565, 'synset': 'macrozamia.n.01', 'name': 'macrozamia'}, {'id': 17566, 'synset': 'burrawong.n.01', 'name': 'burrawong'}, {'id': 17567, 'synset': 'pine.n.01', 'name': 'pine'}, {'id': 17568, 'synset': 'pinon.n.01', 'name': 'pinon'}, {'id': 17569, 'synset': 'nut_pine.n.01', 'name': 'nut_pine'}, {'id': 17570, 'synset': 'pinon_pine.n.01', 'name': 'pinon_pine'}, {'id': 17571, 'synset': 'rocky_mountain_pinon.n.01', 'name': 'Rocky_mountain_pinon'}, {'id': 17572, 'synset': 'single-leaf.n.01', 'name': 'single-leaf'}, {'id': 17573, 'synset': 'bishop_pine.n.01', 'name': 'bishop_pine'}, {'id': 17574, 'synset': 'california_single-leaf_pinyon.n.01', 'name': 'California_single-leaf_pinyon'}, {'id': 17575, 'synset': "parry's_pinyon.n.01", 'name': "Parry's_pinyon"}, {'id': 17576, 'synset': 'spruce_pine.n.04', 'name': 'spruce_pine'}, {'id': 17577, 'synset': 'black_pine.n.05', 'name': 'black_pine'}, {'id': 17578, 'synset': 'pitch_pine.n.02', 'name': 'pitch_pine'}, {'id': 17579, 'synset': 'pond_pine.n.01', 'name': 'pond_pine'}, {'id': 17580, 'synset': 'stone_pine.n.01', 'name': 'stone_pine'}, {'id': 17581, 'synset': 'swiss_pine.n.01', 'name': 'Swiss_pine'}, {'id': 17582, 'synset': 'cembra_nut.n.01', 'name': 'cembra_nut'}, {'id': 17583, 'synset': 'swiss_mountain_pine.n.01', 'name': 'Swiss_mountain_pine'}, {'id': 17584, 'synset': 'ancient_pine.n.01', 'name': 'ancient_pine'}, {'id': 17585, 'synset': 'white_pine.n.01', 'name': 'white_pine'}, {'id': 17586, 'synset': 'american_white_pine.n.01', 'name': 'American_white_pine'}, {'id': 17587, 'synset': 'western_white_pine.n.01', 'name': 'western_white_pine'}, {'id': 17588, 'synset': 'southwestern_white_pine.n.01', 'name': 'southwestern_white_pine'}, {'id': 17589, 'synset': 'limber_pine.n.01', 'name': 'limber_pine'}, {'id': 17590, 'synset': 'whitebark_pine.n.01', 'name': 'whitebark_pine'}, {'id': 17591, 'synset': 'yellow_pine.n.01', 'name': 'yellow_pine'}, {'id': 17592, 'synset': 'ponderosa.n.01', 'name': 'ponderosa'}, {'id': 17593, 'synset': 'jeffrey_pine.n.01', 'name': 'Jeffrey_pine'}, {'id': 17594, 'synset': 'shore_pine.n.01', 'name': 'shore_pine'}, {'id': 17595, 'synset': 'sierra_lodgepole_pine.n.01', 'name': 'Sierra_lodgepole_pine'}, {'id': 17596, 'synset': 'loblolly_pine.n.01', 'name': 'loblolly_pine'}, {'id': 17597, 'synset': 'jack_pine.n.01', 'name': 'jack_pine'}, {'id': 17598, 'synset': 'swamp_pine.n.01', 'name': 'swamp_pine'}, {'id': 17599, 'synset': 'longleaf_pine.n.01', 'name': 'longleaf_pine'}, {'id': 17600, 'synset': 'shortleaf_pine.n.01', 'name': 'shortleaf_pine'}, {'id': 17601, 'synset': 'red_pine.n.02', 'name': 'red_pine'}, {'id': 17602, 'synset': 'scotch_pine.n.01', 'name': 'Scotch_pine'}, {'id': 17603, 'synset': 'scrub_pine.n.01', 'name': 'scrub_pine'}, {'id': 17604, 'synset': 'monterey_pine.n.01', 'name': 'Monterey_pine'}, {'id': 17605, 'synset': 'bristlecone_pine.n.01', 'name': 'bristlecone_pine'}, {'id': 17606, 'synset': 'table-mountain_pine.n.01', 'name': 'table-mountain_pine'}, {'id': 17607, 'synset': 'knobcone_pine.n.01', 'name': 'knobcone_pine'}, {'id': 17608, 'synset': 'japanese_red_pine.n.01', 'name': 'Japanese_red_pine'}, {'id': 17609, 'synset': 'japanese_black_pine.n.01', 'name': 'Japanese_black_pine'}, {'id': 17610, 'synset': 'torrey_pine.n.01', 'name': 'Torrey_pine'}, {'id': 17611, 'synset': 'larch.n.02', 'name': 'larch'}, {'id': 17612, 'synset': 'american_larch.n.01', 'name': 'American_larch'}, {'id': 17613, 'synset': 'western_larch.n.01', 'name': 'western_larch'}, {'id': 17614, 'synset': 'subalpine_larch.n.01', 'name': 'subalpine_larch'}, {'id': 17615, 'synset': 'european_larch.n.01', 'name': 'European_larch'}, {'id': 17616, 'synset': 'siberian_larch.n.01', 'name': 'Siberian_larch'}, {'id': 17617, 'synset': 'golden_larch.n.01', 'name': 'golden_larch'}, {'id': 17618, 'synset': 'fir.n.02', 'name': 'fir'}, {'id': 17619, 'synset': 'silver_fir.n.01', 'name': 'silver_fir'}, {'id': 17620, 'synset': 'amabilis_fir.n.01', 'name': 'amabilis_fir'}, {'id': 17621, 'synset': 'european_silver_fir.n.01', 'name': 'European_silver_fir'}, {'id': 17622, 'synset': 'white_fir.n.01', 'name': 'white_fir'}, {'id': 17623, 'synset': 'balsam_fir.n.01', 'name': 'balsam_fir'}, {'id': 17624, 'synset': 'fraser_fir.n.01', 'name': 'Fraser_fir'}, {'id': 17625, 'synset': 'lowland_fir.n.01', 'name': 'lowland_fir'}, {'id': 17626, 'synset': 'alpine_fir.n.01', 'name': 'Alpine_fir'}, {'id': 17627, 'synset': 'santa_lucia_fir.n.01', 'name': 'Santa_Lucia_fir'}, {'id': 17628, 'synset': 'cedar.n.03', 'name': 'cedar'}, {'id': 17629, 'synset': 'cedar_of_lebanon.n.01', 'name': 'cedar_of_Lebanon'}, {'id': 17630, 'synset': 'deodar.n.01', 'name': 'deodar'}, {'id': 17631, 'synset': 'atlas_cedar.n.01', 'name': 'Atlas_cedar'}, {'id': 17632, 'synset': 'spruce.n.02', 'name': 'spruce'}, {'id': 17633, 'synset': 'norway_spruce.n.01', 'name': 'Norway_spruce'}, {'id': 17634, 'synset': 'weeping_spruce.n.01', 'name': 'weeping_spruce'}, {'id': 17635, 'synset': 'engelmann_spruce.n.01', 'name': 'Engelmann_spruce'}, {'id': 17636, 'synset': 'white_spruce.n.01', 'name': 'white_spruce'}, {'id': 17637, 'synset': 'black_spruce.n.01', 'name': 'black_spruce'}, {'id': 17638, 'synset': 'siberian_spruce.n.01', 'name': 'Siberian_spruce'}, {'id': 17639, 'synset': 'sitka_spruce.n.01', 'name': 'Sitka_spruce'}, {'id': 17640, 'synset': 'oriental_spruce.n.01', 'name': 'oriental_spruce'}, {'id': 17641, 'synset': 'colorado_spruce.n.01', 'name': 'Colorado_spruce'}, {'id': 17642, 'synset': 'red_spruce.n.01', 'name': 'red_spruce'}, {'id': 17643, 'synset': 'hemlock.n.04', 'name': 'hemlock'}, {'id': 17644, 'synset': 'eastern_hemlock.n.01', 'name': 'eastern_hemlock'}, {'id': 17645, 'synset': 'carolina_hemlock.n.01', 'name': 'Carolina_hemlock'}, {'id': 17646, 'synset': 'mountain_hemlock.n.01', 'name': 'mountain_hemlock'}, {'id': 17647, 'synset': 'western_hemlock.n.01', 'name': 'western_hemlock'}, {'id': 17648, 'synset': 'douglas_fir.n.02', 'name': 'douglas_fir'}, {'id': 17649, 'synset': 'green_douglas_fir.n.01', 'name': 'green_douglas_fir'}, {'id': 17650, 'synset': 'big-cone_spruce.n.01', 'name': 'big-cone_spruce'}, {'id': 17651, 'synset': 'cathaya.n.01', 'name': 'Cathaya'}, {'id': 17652, 'synset': 'cedar.n.01', 'name': 'cedar'}, {'id': 17653, 'synset': 'cypress.n.02', 'name': 'cypress'}, {'id': 17654, 'synset': 'gowen_cypress.n.01', 'name': 'gowen_cypress'}, {'id': 17655, 'synset': 'pygmy_cypress.n.01', 'name': 'pygmy_cypress'}, {'id': 17656, 'synset': 'santa_cruz_cypress.n.01', 'name': 'Santa_Cruz_cypress'}, {'id': 17657, 'synset': 'arizona_cypress.n.01', 'name': 'Arizona_cypress'}, {'id': 17658, 'synset': 'guadalupe_cypress.n.01', 'name': 'Guadalupe_cypress'}, {'id': 17659, 'synset': 'monterey_cypress.n.01', 'name': 'Monterey_cypress'}, {'id': 17660, 'synset': 'mexican_cypress.n.01', 'name': 'Mexican_cypress'}, {'id': 17661, 'synset': 'italian_cypress.n.01', 'name': 'Italian_cypress'}, {'id': 17662, 'synset': 'king_william_pine.n.01', 'name': 'King_William_pine'}, {'id': 17663, 'synset': 'chilean_cedar.n.01', 'name': 'Chilean_cedar'}, {'id': 17664, 'synset': 'incense_cedar.n.02', 'name': 'incense_cedar'}, {'id': 17665, 'synset': 'southern_white_cedar.n.01', 'name': 'southern_white_cedar'}, {'id': 17666, 'synset': 'oregon_cedar.n.01', 'name': 'Oregon_cedar'}, {'id': 17667, 'synset': 'yellow_cypress.n.01', 'name': 'yellow_cypress'}, {'id': 17668, 'synset': 'japanese_cedar.n.01', 'name': 'Japanese_cedar'}, {'id': 17669, 'synset': 'juniper_berry.n.01', 'name': 'juniper_berry'}, {'id': 17670, 'synset': 'incense_cedar.n.01', 'name': 'incense_cedar'}, {'id': 17671, 'synset': 'kawaka.n.01', 'name': 'kawaka'}, {'id': 17672, 'synset': 'pahautea.n.01', 'name': 'pahautea'}, {'id': 17673, 'synset': 'metasequoia.n.01', 'name': 'metasequoia'}, {'id': 17674, 'synset': 'arborvitae.n.01', 'name': 'arborvitae'}, {'id': 17675, 'synset': 'western_red_cedar.n.01', 'name': 'western_red_cedar'}, {'id': 17676, 'synset': 'american_arborvitae.n.01', 'name': 'American_arborvitae'}, {'id': 17677, 'synset': 'oriental_arborvitae.n.01', 'name': 'Oriental_arborvitae'}, {'id': 17678, 'synset': 'hiba_arborvitae.n.01', 'name': 'hiba_arborvitae'}, {'id': 17679, 'synset': 'keteleeria.n.01', 'name': 'keteleeria'}, {'id': 17680, 'synset': 'wollemi_pine.n.01', 'name': 'Wollemi_pine'}, {'id': 17681, 'synset': 'araucaria.n.01', 'name': 'araucaria'}, {'id': 17682, 'synset': 'monkey_puzzle.n.01', 'name': 'monkey_puzzle'}, {'id': 17683, 'synset': 'norfolk_island_pine.n.01', 'name': 'norfolk_island_pine'}, {'id': 17684, 'synset': 'new_caledonian_pine.n.01', 'name': 'new_caledonian_pine'}, {'id': 17685, 'synset': 'bunya_bunya.n.01', 'name': 'bunya_bunya'}, {'id': 17686, 'synset': 'hoop_pine.n.01', 'name': 'hoop_pine'}, {'id': 17687, 'synset': 'kauri_pine.n.01', 'name': 'kauri_pine'}, {'id': 17688, 'synset': 'kauri.n.02', 'name': 'kauri'}, {'id': 17689, 'synset': 'amboina_pine.n.01', 'name': 'amboina_pine'}, {'id': 17690, 'synset': 'dundathu_pine.n.01', 'name': 'dundathu_pine'}, {'id': 17691, 'synset': 'red_kauri.n.01', 'name': 'red_kauri'}, {'id': 17692, 'synset': 'plum-yew.n.01', 'name': 'plum-yew'}, {'id': 17693, 'synset': 'california_nutmeg.n.01', 'name': 'California_nutmeg'}, {'id': 17694, 'synset': 'stinking_cedar.n.01', 'name': 'stinking_cedar'}, {'id': 17695, 'synset': 'celery_pine.n.01', 'name': 'celery_pine'}, {'id': 17696, 'synset': 'celery_top_pine.n.01', 'name': 'celery_top_pine'}, {'id': 17697, 'synset': 'tanekaha.n.01', 'name': 'tanekaha'}, {'id': 17698, 'synset': 'alpine_celery_pine.n.01', 'name': 'Alpine_celery_pine'}, {'id': 17699, 'synset': 'yellowwood.n.02', 'name': 'yellowwood'}, {'id': 17700, 'synset': 'gymnospermous_yellowwood.n.01', 'name': 'gymnospermous_yellowwood'}, {'id': 17701, 'synset': 'podocarp.n.01', 'name': 'podocarp'}, {'id': 17702, 'synset': 'yacca.n.01', 'name': 'yacca'}, {'id': 17703, 'synset': 'brown_pine.n.01', 'name': 'brown_pine'}, {'id': 17704, 'synset': 'cape_yellowwood.n.01', 'name': 'cape_yellowwood'}, {'id': 17705, 'synset': 'south-african_yellowwood.n.01', 'name': 'South-African_yellowwood'}, {'id': 17706, 'synset': 'alpine_totara.n.01', 'name': 'alpine_totara'}, {'id': 17707, 'synset': 'totara.n.01', 'name': 'totara'}, {'id': 17708, 'synset': 'common_yellowwood.n.01', 'name': 'common_yellowwood'}, {'id': 17709, 'synset': 'kahikatea.n.01', 'name': 'kahikatea'}, {'id': 17710, 'synset': 'rimu.n.01', 'name': 'rimu'}, {'id': 17711, 'synset': 'tarwood.n.02', 'name': 'tarwood'}, {'id': 17712, 'synset': 'common_sickle_pine.n.01', 'name': 'common_sickle_pine'}, {'id': 17713, 'synset': 'yellow-leaf_sickle_pine.n.01', 'name': 'yellow-leaf_sickle_pine'}, {'id': 17714, 'synset': 'tarwood.n.01', 'name': 'tarwood'}, {'id': 17715, 'synset': 'westland_pine.n.01', 'name': 'westland_pine'}, {'id': 17716, 'synset': 'huon_pine.n.01', 'name': 'huon_pine'}, {'id': 17717, 'synset': 'chilean_rimu.n.01', 'name': 'Chilean_rimu'}, {'id': 17718, 'synset': 'mountain_rimu.n.01', 'name': 'mountain_rimu'}, {'id': 17719, 'synset': 'nagi.n.01', 'name': 'nagi'}, {'id': 17720, 'synset': 'miro.n.01', 'name': 'miro'}, {'id': 17721, 'synset': 'matai.n.01', 'name': 'matai'}, {'id': 17722, 'synset': 'plum-fruited_yew.n.01', 'name': 'plum-fruited_yew'}, {'id': 17723, 'synset': 'prince_albert_yew.n.01', 'name': 'Prince_Albert_yew'}, {'id': 17724, 'synset': 'sundacarpus_amara.n.01', 'name': 'Sundacarpus_amara'}, {'id': 17725, 'synset': 'japanese_umbrella_pine.n.01', 'name': 'Japanese_umbrella_pine'}, {'id': 17726, 'synset': 'yew.n.02', 'name': 'yew'}, {'id': 17727, 'synset': 'old_world_yew.n.01', 'name': 'Old_World_yew'}, {'id': 17728, 'synset': 'pacific_yew.n.01', 'name': 'Pacific_yew'}, {'id': 17729, 'synset': 'japanese_yew.n.01', 'name': 'Japanese_yew'}, {'id': 17730, 'synset': 'florida_yew.n.01', 'name': 'Florida_yew'}, {'id': 17731, 'synset': 'new_caledonian_yew.n.01', 'name': 'New_Caledonian_yew'}, {'id': 17732, 'synset': 'white-berry_yew.n.01', 'name': 'white-berry_yew'}, {'id': 17733, 'synset': 'ginkgo.n.01', 'name': 'ginkgo'}, {'id': 17734, 'synset': 'angiosperm.n.01', 'name': 'angiosperm'}, {'id': 17735, 'synset': 'dicot.n.01', 'name': 'dicot'}, {'id': 17736, 'synset': 'monocot.n.01', 'name': 'monocot'}, {'id': 17737, 'synset': 'floret.n.01', 'name': 'floret'}, {'id': 17738, 'synset': 'flower.n.01', 'name': 'flower'}, {'id': 17739, 'synset': 'bloomer.n.01', 'name': 'bloomer'}, {'id': 17740, 'synset': 'wildflower.n.01', 'name': 'wildflower'}, {'id': 17741, 'synset': 'apetalous_flower.n.01', 'name': 'apetalous_flower'}, {'id': 17742, 'synset': 'inflorescence.n.02', 'name': 'inflorescence'}, {'id': 17743, 'synset': 'rosebud.n.01', 'name': 'rosebud'}, {'id': 17744, 'synset': 'gynostegium.n.01', 'name': 'gynostegium'}, {'id': 17745, 'synset': 'pollinium.n.01', 'name': 'pollinium'}, {'id': 17746, 'synset': 'pistil.n.01', 'name': 'pistil'}, {'id': 17747, 'synset': 'gynobase.n.01', 'name': 'gynobase'}, {'id': 17748, 'synset': 'gynophore.n.01', 'name': 'gynophore'}, {'id': 17749, 'synset': 'stylopodium.n.01', 'name': 'stylopodium'}, {'id': 17750, 'synset': 'carpophore.n.01', 'name': 'carpophore'}, {'id': 17751, 'synset': 'cornstalk.n.01', 'name': 'cornstalk'}, {'id': 17752, 'synset': 'petiolule.n.01', 'name': 'petiolule'}, {'id': 17753, 'synset': 'mericarp.n.01', 'name': 'mericarp'}, {'id': 17754, 'synset': 'micropyle.n.01', 'name': 'micropyle'}, {'id': 17755, 'synset': 'germ_tube.n.01', 'name': 'germ_tube'}, {'id': 17756, 'synset': 'pollen_tube.n.01', 'name': 'pollen_tube'}, {'id': 17757, 'synset': 'gemma.n.01', 'name': 'gemma'}, {'id': 17758, 'synset': 'galbulus.n.01', 'name': 'galbulus'}, {'id': 17759, 'synset': 'nectary.n.01', 'name': 'nectary'}, {'id': 17760, 'synset': 'pericarp.n.01', 'name': 'pericarp'}, {'id': 17761, 'synset': 'epicarp.n.01', 'name': 'epicarp'}, {'id': 17762, 'synset': 'mesocarp.n.01', 'name': 'mesocarp'}, {'id': 17763, 'synset': 'pip.n.03', 'name': 'pip'}, {'id': 17764, 'synset': 'silique.n.01', 'name': 'silique'}, {'id': 17765, 'synset': 'cataphyll.n.01', 'name': 'cataphyll'}, {'id': 17766, 'synset': 'perisperm.n.01', 'name': 'perisperm'}, {'id': 17767, 'synset': 'monocarp.n.01', 'name': 'monocarp'}, {'id': 17768, 'synset': 'sporophyte.n.01', 'name': 'sporophyte'}, {'id': 17769, 'synset': 'gametophyte.n.01', 'name': 'gametophyte'}, {'id': 17770, 'synset': 'megasporangium.n.01', 'name': 'megasporangium'}, {'id': 17771, 'synset': 'microspore.n.01', 'name': 'microspore'}, {'id': 17772, 'synset': 'microsporangium.n.01', 'name': 'microsporangium'}, {'id': 17773, 'synset': 'microsporophyll.n.01', 'name': 'microsporophyll'}, {'id': 17774, 'synset': 'archespore.n.01', 'name': 'archespore'}, {'id': 17775, 'synset': 'bonduc_nut.n.01', 'name': 'bonduc_nut'}, {'id': 17776, 'synset': "job's_tears.n.01", 'name': "Job's_tears"}, {'id': 17777, 'synset': 'oilseed.n.01', 'name': 'oilseed'}, {'id': 17778, 'synset': 'castor_bean.n.01', 'name': 'castor_bean'}, {'id': 17779, 'synset': 'cottonseed.n.01', 'name': 'cottonseed'}, {'id': 17780, 'synset': 'candlenut.n.02', 'name': 'candlenut'}, {'id': 17781, 'synset': 'peach_pit.n.01', 'name': 'peach_pit'}, {'id': 17782, 'synset': 'hypanthium.n.01', 'name': 'hypanthium'}, {'id': 17783, 'synset': 'petal.n.01', 'name': 'petal'}, {'id': 17784, 'synset': 'corolla.n.01', 'name': 'corolla'}, {'id': 17785, 'synset': 'lip.n.02', 'name': 'lip'}, {'id': 17786, 'synset': 'perianth.n.01', 'name': 'perianth'}, {'id': 17787, 'synset': 'thistledown.n.01', 'name': 'thistledown'}, {'id': 17788, 'synset': 'custard_apple.n.01', 'name': 'custard_apple'}, {'id': 17789, 'synset': 'cherimoya.n.01', 'name': 'cherimoya'}, {'id': 17790, 'synset': 'ilama.n.01', 'name': 'ilama'}, {'id': 17791, 'synset': 'soursop.n.01', 'name': 'soursop'}, {'id': 17792, 'synset': "bullock's_heart.n.01", 'name': "bullock's_heart"}, {'id': 17793, 'synset': 'sweetsop.n.01', 'name': 'sweetsop'}, {'id': 17794, 'synset': 'pond_apple.n.01', 'name': 'pond_apple'}, {'id': 17795, 'synset': 'pawpaw.n.02', 'name': 'pawpaw'}, {'id': 17796, 'synset': 'ilang-ilang.n.02', 'name': 'ilang-ilang'}, {'id': 17797, 'synset': 'lancewood.n.02', 'name': 'lancewood'}, {'id': 17798, 'synset': 'guinea_pepper.n.02', 'name': 'Guinea_pepper'}, {'id': 17799, 'synset': 'barberry.n.01', 'name': 'barberry'}, {'id': 17800, 'synset': 'american_barberry.n.01', 'name': 'American_barberry'}, {'id': 17801, 'synset': 'common_barberry.n.01', 'name': 'common_barberry'}, {'id': 17802, 'synset': 'japanese_barberry.n.01', 'name': 'Japanese_barberry'}, {'id': 17803, 'synset': 'oregon_grape.n.02', 'name': 'Oregon_grape'}, {'id': 17804, 'synset': 'oregon_grape.n.01', 'name': 'Oregon_grape'}, {'id': 17805, 'synset': 'mayapple.n.01', 'name': 'mayapple'}, {'id': 17806, 'synset': 'may_apple.n.01', 'name': 'May_apple'}, {'id': 17807, 'synset': 'allspice.n.02', 'name': 'allspice'}, {'id': 17808, 'synset': 'carolina_allspice.n.01', 'name': 'Carolina_allspice'}, {'id': 17809, 'synset': 'spicebush.n.02', 'name': 'spicebush'}, {'id': 17810, 'synset': 'katsura_tree.n.01', 'name': 'katsura_tree'}, {'id': 17811, 'synset': 'laurel.n.01', 'name': 'laurel'}, {'id': 17812, 'synset': 'true_laurel.n.01', 'name': 'true_laurel'}, {'id': 17813, 'synset': 'camphor_tree.n.01', 'name': 'camphor_tree'}, {'id': 17814, 'synset': 'cinnamon.n.02', 'name': 'cinnamon'}, {'id': 17815, 'synset': 'cassia.n.03', 'name': 'cassia'}, {'id': 17816, 'synset': 'cassia_bark.n.01', 'name': 'cassia_bark'}, {'id': 17817, 'synset': 'saigon_cinnamon.n.01', 'name': 'Saigon_cinnamon'}, {'id': 17818, 'synset': 'cinnamon_bark.n.01', 'name': 'cinnamon_bark'}, {'id': 17819, 'synset': 'spicebush.n.01', 'name': 'spicebush'}, {'id': 17820, 'synset': 'avocado.n.02', 'name': 'avocado'}, {'id': 17821, 'synset': 'laurel-tree.n.01', 'name': 'laurel-tree'}, {'id': 17822, 'synset': 'sassafras.n.01', 'name': 'sassafras'}, {'id': 17823, 'synset': 'california_laurel.n.01', 'name': 'California_laurel'}, {'id': 17824, 'synset': 'anise_tree.n.01', 'name': 'anise_tree'}, {'id': 17825, 'synset': 'purple_anise.n.01', 'name': 'purple_anise'}, {'id': 17826, 'synset': 'star_anise.n.02', 'name': 'star_anise'}, {'id': 17827, 'synset': 'star_anise.n.01', 'name': 'star_anise'}, {'id': 17828, 'synset': 'magnolia.n.02', 'name': 'magnolia'}, {'id': 17829, 'synset': 'southern_magnolia.n.01', 'name': 'southern_magnolia'}, {'id': 17830, 'synset': 'umbrella_tree.n.02', 'name': 'umbrella_tree'}, {'id': 17831, 'synset': 'earleaved_umbrella_tree.n.01', 'name': 'earleaved_umbrella_tree'}, {'id': 17832, 'synset': 'cucumber_tree.n.01', 'name': 'cucumber_tree'}, {'id': 17833, 'synset': 'large-leaved_magnolia.n.01', 'name': 'large-leaved_magnolia'}, {'id': 17834, 'synset': 'saucer_magnolia.n.01', 'name': 'saucer_magnolia'}, {'id': 17835, 'synset': 'star_magnolia.n.01', 'name': 'star_magnolia'}, {'id': 17836, 'synset': 'sweet_bay.n.01', 'name': 'sweet_bay'}, {'id': 17837, 'synset': 'manglietia.n.01', 'name': 'manglietia'}, {'id': 17838, 'synset': 'tulip_tree.n.01', 'name': 'tulip_tree'}, {'id': 17839, 'synset': 'moonseed.n.01', 'name': 'moonseed'}, {'id': 17840, 'synset': 'common_moonseed.n.01', 'name': 'common_moonseed'}, {'id': 17841, 'synset': 'carolina_moonseed.n.01', 'name': 'Carolina_moonseed'}, {'id': 17842, 'synset': 'nutmeg.n.01', 'name': 'nutmeg'}, {'id': 17843, 'synset': 'water_nymph.n.02', 'name': 'water_nymph'}, {'id': 17844, 'synset': 'european_white_lily.n.01', 'name': 'European_white_lily'}, {'id': 17845, 'synset': 'southern_spatterdock.n.01', 'name': 'southern_spatterdock'}, {'id': 17846, 'synset': 'lotus.n.01', 'name': 'lotus'}, {'id': 17847, 'synset': 'water_chinquapin.n.01', 'name': 'water_chinquapin'}, {'id': 17848, 'synset': 'water-shield.n.02', 'name': 'water-shield'}, {'id': 17849, 'synset': 'water-shield.n.01', 'name': 'water-shield'}, {'id': 17850, 'synset': 'peony.n.01', 'name': 'peony'}, {'id': 17851, 'synset': 'buttercup.n.01', 'name': 'buttercup'}, {'id': 17852, 'synset': 'meadow_buttercup.n.01', 'name': 'meadow_buttercup'}, {'id': 17853, 'synset': 'water_crowfoot.n.01', 'name': 'water_crowfoot'}, {'id': 17854, 'synset': 'lesser_celandine.n.01', 'name': 'lesser_celandine'}, {'id': 17855, 'synset': 'lesser_spearwort.n.01', 'name': 'lesser_spearwort'}, {'id': 17856, 'synset': 'greater_spearwort.n.01', 'name': 'greater_spearwort'}, {'id': 17857, 'synset': 'western_buttercup.n.01', 'name': 'western_buttercup'}, {'id': 17858, 'synset': 'creeping_buttercup.n.01', 'name': 'creeping_buttercup'}, {'id': 17859, 'synset': 'cursed_crowfoot.n.01', 'name': 'cursed_crowfoot'}, {'id': 17860, 'synset': 'aconite.n.01', 'name': 'aconite'}, {'id': 17861, 'synset': 'monkshood.n.01', 'name': 'monkshood'}, {'id': 17862, 'synset': 'wolfsbane.n.01', 'name': 'wolfsbane'}, {'id': 17863, 'synset': 'baneberry.n.02', 'name': 'baneberry'}, {'id': 17864, 'synset': 'baneberry.n.01', 'name': 'baneberry'}, {'id': 17865, 'synset': 'red_baneberry.n.01', 'name': 'red_baneberry'}, {'id': 17866, 'synset': "pheasant's-eye.n.01", 'name': "pheasant's-eye"}, {'id': 17867, 'synset': 'anemone.n.01', 'name': 'anemone'}, {'id': 17868, 'synset': 'alpine_anemone.n.01', 'name': 'Alpine_anemone'}, {'id': 17869, 'synset': 'canada_anemone.n.01', 'name': 'Canada_anemone'}, {'id': 17870, 'synset': 'thimbleweed.n.01', 'name': 'thimbleweed'}, {'id': 17871, 'synset': 'wood_anemone.n.02', 'name': 'wood_anemone'}, {'id': 17872, 'synset': 'wood_anemone.n.01', 'name': 'wood_anemone'}, {'id': 17873, 'synset': 'longheaded_thimbleweed.n.01', 'name': 'longheaded_thimbleweed'}, {'id': 17874, 'synset': 'snowdrop_anemone.n.01', 'name': 'snowdrop_anemone'}, {'id': 17875, 'synset': 'virginia_thimbleweed.n.01', 'name': 'Virginia_thimbleweed'}, {'id': 17876, 'synset': 'rue_anemone.n.01', 'name': 'rue_anemone'}, {'id': 17877, 'synset': 'columbine.n.01', 'name': 'columbine'}, {'id': 17878, 'synset': 'meeting_house.n.01', 'name': 'meeting_house'}, {'id': 17879, 'synset': 'blue_columbine.n.01', 'name': 'blue_columbine'}, {'id': 17880, 'synset': "granny's_bonnets.n.01", 'name': "granny's_bonnets"}, {'id': 17881, 'synset': 'marsh_marigold.n.01', 'name': 'marsh_marigold'}, {'id': 17882, 'synset': 'american_bugbane.n.01', 'name': 'American_bugbane'}, {'id': 17883, 'synset': 'black_cohosh.n.01', 'name': 'black_cohosh'}, {'id': 17884, 'synset': 'fetid_bugbane.n.01', 'name': 'fetid_bugbane'}, {'id': 17885, 'synset': 'clematis.n.01', 'name': 'clematis'}, {'id': 17886, 'synset': 'pine_hyacinth.n.01', 'name': 'pine_hyacinth'}, {'id': 17887, 'synset': 'blue_jasmine.n.01', 'name': 'blue_jasmine'}, {'id': 17888, 'synset': 'golden_clematis.n.01', 'name': 'golden_clematis'}, {'id': 17889, 'synset': 'scarlet_clematis.n.01', 'name': 'scarlet_clematis'}, {'id': 17890, 'synset': 'leather_flower.n.02', 'name': 'leather_flower'}, {'id': 17891, 'synset': 'leather_flower.n.01', 'name': 'leather_flower'}, {'id': 17892, 'synset': "virgin's_bower.n.01", 'name': "virgin's_bower"}, {'id': 17893, 'synset': 'purple_clematis.n.01', 'name': 'purple_clematis'}, {'id': 17894, 'synset': 'goldthread.n.01', 'name': 'goldthread'}, {'id': 17895, 'synset': 'rocket_larkspur.n.01', 'name': 'rocket_larkspur'}, {'id': 17896, 'synset': 'delphinium.n.01', 'name': 'delphinium'}, {'id': 17897, 'synset': 'larkspur.n.01', 'name': 'larkspur'}, {'id': 17898, 'synset': 'winter_aconite.n.01', 'name': 'winter_aconite'}, {'id': 17899, 'synset': 'lenten_rose.n.01', 'name': 'lenten_rose'}, {'id': 17900, 'synset': 'green_hellebore.n.01', 'name': 'green_hellebore'}, {'id': 17901, 'synset': 'hepatica.n.01', 'name': 'hepatica'}, {'id': 17902, 'synset': 'goldenseal.n.01', 'name': 'goldenseal'}, {'id': 17903, 'synset': 'false_rue_anemone.n.01', 'name': 'false_rue_anemone'}, {'id': 17904, 'synset': 'giant_buttercup.n.01', 'name': 'giant_buttercup'}, {'id': 17905, 'synset': 'nigella.n.01', 'name': 'nigella'}, {'id': 17906, 'synset': 'love-in-a-mist.n.03', 'name': 'love-in-a-mist'}, {'id': 17907, 'synset': 'fennel_flower.n.01', 'name': 'fennel_flower'}, {'id': 17908, 'synset': 'black_caraway.n.01', 'name': 'black_caraway'}, {'id': 17909, 'synset': 'pasqueflower.n.01', 'name': 'pasqueflower'}, {'id': 17910, 'synset': 'meadow_rue.n.01', 'name': 'meadow_rue'}, {'id': 17911, 'synset': 'false_bugbane.n.01', 'name': 'false_bugbane'}, {'id': 17912, 'synset': 'globeflower.n.01', 'name': 'globeflower'}, {'id': 17913, 'synset': "winter's_bark.n.02", 'name': "winter's_bark"}, {'id': 17914, 'synset': 'pepper_shrub.n.01', 'name': 'pepper_shrub'}, {'id': 17915, 'synset': 'sweet_gale.n.01', 'name': 'sweet_gale'}, {'id': 17916, 'synset': 'wax_myrtle.n.01', 'name': 'wax_myrtle'}, {'id': 17917, 'synset': 'bay_myrtle.n.01', 'name': 'bay_myrtle'}, {'id': 17918, 'synset': 'bayberry.n.02', 'name': 'bayberry'}, {'id': 17919, 'synset': 'sweet_fern.n.02', 'name': 'sweet_fern'}, {'id': 17920, 'synset': 'corkwood.n.01', 'name': 'corkwood'}, {'id': 17921, 'synset': 'jointed_rush.n.01', 'name': 'jointed_rush'}, {'id': 17922, 'synset': 'toad_rush.n.01', 'name': 'toad_rush'}, {'id': 17923, 'synset': 'slender_rush.n.01', 'name': 'slender_rush'}, {'id': 17924, 'synset': 'zebrawood.n.02', 'name': 'zebrawood'}, {'id': 17925, 'synset': 'connarus_guianensis.n.01', 'name': 'Connarus_guianensis'}, {'id': 17926, 'synset': 'legume.n.01', 'name': 'legume'}, {'id': 17927, 'synset': 'peanut.n.01', 'name': 'peanut'}, {'id': 17928, 'synset': 'granadilla_tree.n.01', 'name': 'granadilla_tree'}, {'id': 17929, 'synset': 'arariba.n.01', 'name': 'arariba'}, {'id': 17930, 'synset': 'tonka_bean.n.01', 'name': 'tonka_bean'}, {'id': 17931, 'synset': 'courbaril.n.01', 'name': 'courbaril'}, {'id': 17932, 'synset': 'melilotus.n.01', 'name': 'melilotus'}, {'id': 17933, 'synset': 'darling_pea.n.01', 'name': 'darling_pea'}, {'id': 17934, 'synset': 'smooth_darling_pea.n.01', 'name': 'smooth_darling_pea'}, {'id': 17935, 'synset': 'clover.n.01', 'name': 'clover'}, {'id': 17936, 'synset': 'alpine_clover.n.01', 'name': 'alpine_clover'}, {'id': 17937, 'synset': 'hop_clover.n.02', 'name': 'hop_clover'}, {'id': 17938, 'synset': 'crimson_clover.n.01', 'name': 'crimson_clover'}, {'id': 17939, 'synset': 'red_clover.n.01', 'name': 'red_clover'}, {'id': 17940, 'synset': 'buffalo_clover.n.02', 'name': 'buffalo_clover'}, {'id': 17941, 'synset': 'white_clover.n.01', 'name': 'white_clover'}, {'id': 17942, 'synset': 'mimosa.n.02', 'name': 'mimosa'}, {'id': 17943, 'synset': 'acacia.n.01', 'name': 'acacia'}, {'id': 17944, 'synset': 'shittah.n.01', 'name': 'shittah'}, {'id': 17945, 'synset': 'wattle.n.03', 'name': 'wattle'}, {'id': 17946, 'synset': 'black_wattle.n.01', 'name': 'black_wattle'}, {'id': 17947, 'synset': 'gidgee.n.01', 'name': 'gidgee'}, {'id': 17948, 'synset': 'catechu.n.02', 'name': 'catechu'}, {'id': 17949, 'synset': 'silver_wattle.n.01', 'name': 'silver_wattle'}, {'id': 17950, 'synset': 'huisache.n.01', 'name': 'huisache'}, {'id': 17951, 'synset': 'lightwood.n.01', 'name': 'lightwood'}, {'id': 17952, 'synset': 'golden_wattle.n.01', 'name': 'golden_wattle'}, {'id': 17953, 'synset': 'fever_tree.n.04', 'name': 'fever_tree'}, {'id': 17954, 'synset': 'coralwood.n.01', 'name': 'coralwood'}, {'id': 17955, 'synset': 'albizzia.n.01', 'name': 'albizzia'}, {'id': 17956, 'synset': 'silk_tree.n.01', 'name': 'silk_tree'}, {'id': 17957, 'synset': 'siris.n.01', 'name': 'siris'}, {'id': 17958, 'synset': 'rain_tree.n.01', 'name': 'rain_tree'}, {'id': 17959, 'synset': 'calliandra.n.01', 'name': 'calliandra'}, {'id': 17960, 'synset': 'conacaste.n.01', 'name': 'conacaste'}, {'id': 17961, 'synset': 'inga.n.01', 'name': 'inga'}, {'id': 17962, 'synset': 'ice-cream_bean.n.01', 'name': 'ice-cream_bean'}, {'id': 17963, 'synset': 'guama.n.01', 'name': 'guama'}, {'id': 17964, 'synset': 'lead_tree.n.01', 'name': 'lead_tree'}, {'id': 17965, 'synset': 'wild_tamarind.n.02', 'name': 'wild_tamarind'}, {'id': 17966, 'synset': 'sabicu.n.02', 'name': 'sabicu'}, {'id': 17967, 'synset': 'nitta_tree.n.01', 'name': 'nitta_tree'}, {'id': 17968, 'synset': 'parkia_javanica.n.01', 'name': 'Parkia_javanica'}, {'id': 17969, 'synset': 'manila_tamarind.n.01', 'name': 'manila_tamarind'}, {'id': 17970, 'synset': "cat's-claw.n.01", 'name': "cat's-claw"}, {'id': 17971, 'synset': 'honey_mesquite.n.01', 'name': 'honey_mesquite'}, {'id': 17972, 'synset': 'algarroba.n.03', 'name': 'algarroba'}, {'id': 17973, 'synset': 'screw_bean.n.02', 'name': 'screw_bean'}, {'id': 17974, 'synset': 'screw_bean.n.01', 'name': 'screw_bean'}, {'id': 17975, 'synset': 'dogbane.n.01', 'name': 'dogbane'}, {'id': 17976, 'synset': 'indian_hemp.n.03', 'name': 'Indian_hemp'}, {'id': 17977, 'synset': "bushman's_poison.n.01", 'name': "bushman's_poison"}, {'id': 17978, 'synset': 'impala_lily.n.01', 'name': 'impala_lily'}, {'id': 17979, 'synset': 'allamanda.n.01', 'name': 'allamanda'}, {'id': 17980, 'synset': 'common_allamanda.n.01', 'name': 'common_allamanda'}, {'id': 17981, 'synset': 'dita.n.01', 'name': 'dita'}, {'id': 17982, 'synset': 'nepal_trumpet_flower.n.01', 'name': 'Nepal_trumpet_flower'}, {'id': 17983, 'synset': 'carissa.n.01', 'name': 'carissa'}, {'id': 17984, 'synset': 'hedge_thorn.n.01', 'name': 'hedge_thorn'}, {'id': 17985, 'synset': 'natal_plum.n.01', 'name': 'natal_plum'}, {'id': 17986, 'synset': 'periwinkle.n.02', 'name': 'periwinkle'}, {'id': 17987, 'synset': 'ivory_tree.n.01', 'name': 'ivory_tree'}, {'id': 17988, 'synset': 'white_dipladenia.n.01', 'name': 'white_dipladenia'}, {'id': 17989, 'synset': 'chilean_jasmine.n.01', 'name': 'Chilean_jasmine'}, {'id': 17990, 'synset': 'oleander.n.01', 'name': 'oleander'}, {'id': 17991, 'synset': 'frangipani.n.01', 'name': 'frangipani'}, {'id': 17992, 'synset': 'west_indian_jasmine.n.01', 'name': 'West_Indian_jasmine'}, {'id': 17993, 'synset': 'rauwolfia.n.02', 'name': 'rauwolfia'}, {'id': 17994, 'synset': 'snakewood.n.01', 'name': 'snakewood'}, {'id': 17995, 'synset': 'strophanthus_kombe.n.01', 'name': 'Strophanthus_kombe'}, {'id': 17996, 'synset': 'yellow_oleander.n.01', 'name': 'yellow_oleander'}, {'id': 17997, 'synset': 'myrtle.n.01', 'name': 'myrtle'}, {'id': 17998, 'synset': 'large_periwinkle.n.01', 'name': 'large_periwinkle'}, {'id': 17999, 'synset': 'arum.n.02', 'name': 'arum'}, {'id': 18000, 'synset': 'cuckoopint.n.01', 'name': 'cuckoopint'}, {'id': 18001, 'synset': 'black_calla.n.01', 'name': 'black_calla'}, {'id': 18002, 'synset': 'calamus.n.02', 'name': 'calamus'}, {'id': 18003, 'synset': 'alocasia.n.01', 'name': 'alocasia'}, {'id': 18004, 'synset': 'giant_taro.n.01', 'name': 'giant_taro'}, {'id': 18005, 'synset': 'amorphophallus.n.01', 'name': 'amorphophallus'}, {'id': 18006, 'synset': 'pungapung.n.01', 'name': 'pungapung'}, {'id': 18007, 'synset': "devil's_tongue.n.01", 'name': "devil's_tongue"}, {'id': 18008, 'synset': 'anthurium.n.01', 'name': 'anthurium'}, {'id': 18009, 'synset': 'flamingo_flower.n.01', 'name': 'flamingo_flower'}, {'id': 18010, 'synset': 'jack-in-the-pulpit.n.01', 'name': 'jack-in-the-pulpit'}, {'id': 18011, 'synset': "friar's-cowl.n.01", 'name': "friar's-cowl"}, {'id': 18012, 'synset': 'caladium.n.01', 'name': 'caladium'}, {'id': 18013, 'synset': 'caladium_bicolor.n.01', 'name': 'Caladium_bicolor'}, {'id': 18014, 'synset': 'wild_calla.n.01', 'name': 'wild_calla'}, {'id': 18015, 'synset': 'taro.n.02', 'name': 'taro'}, {'id': 18016, 'synset': 'taro.n.01', 'name': 'taro'}, {'id': 18017, 'synset': 'cryptocoryne.n.01', 'name': 'cryptocoryne'}, {'id': 18018, 'synset': 'dracontium.n.01', 'name': 'dracontium'}, {'id': 18019, 'synset': 'golden_pothos.n.01', 'name': 'golden_pothos'}, {'id': 18020, 'synset': 'skunk_cabbage.n.02', 'name': 'skunk_cabbage'}, {'id': 18021, 'synset': 'monstera.n.01', 'name': 'monstera'}, {'id': 18022, 'synset': 'ceriman.n.01', 'name': 'ceriman'}, {'id': 18023, 'synset': 'nephthytis.n.01', 'name': 'nephthytis'}, {'id': 18024, 'synset': 'nephthytis_afzelii.n.01', 'name': 'Nephthytis_afzelii'}, {'id': 18025, 'synset': 'arrow_arum.n.01', 'name': 'arrow_arum'}, {'id': 18026, 'synset': 'green_arrow_arum.n.01', 'name': 'green_arrow_arum'}, {'id': 18027, 'synset': 'philodendron.n.01', 'name': 'philodendron'}, {'id': 18028, 'synset': 'pistia.n.01', 'name': 'pistia'}, {'id': 18029, 'synset': 'pothos.n.01', 'name': 'pothos'}, {'id': 18030, 'synset': 'spathiphyllum.n.01', 'name': 'spathiphyllum'}, {'id': 18031, 'synset': 'skunk_cabbage.n.01', 'name': 'skunk_cabbage'}, {'id': 18032, 'synset': 'yautia.n.01', 'name': 'yautia'}, {'id': 18033, 'synset': 'calla_lily.n.01', 'name': 'calla_lily'}, {'id': 18034, 'synset': 'pink_calla.n.01', 'name': 'pink_calla'}, {'id': 18035, 'synset': 'golden_calla.n.01', 'name': 'golden_calla'}, {'id': 18036, 'synset': 'duckweed.n.01', 'name': 'duckweed'}, {'id': 18037, 'synset': 'common_duckweed.n.01', 'name': 'common_duckweed'}, {'id': 18038, 'synset': 'star-duckweed.n.01', 'name': 'star-duckweed'}, {'id': 18039, 'synset': 'great_duckweed.n.01', 'name': 'great_duckweed'}, {'id': 18040, 'synset': 'watermeal.n.01', 'name': 'watermeal'}, {'id': 18041, 'synset': 'common_wolffia.n.01', 'name': 'common_wolffia'}, {'id': 18042, 'synset': 'aralia.n.01', 'name': 'aralia'}, {'id': 18043, 'synset': 'american_angelica_tree.n.01', 'name': 'American_angelica_tree'}, {'id': 18044, 'synset': 'american_spikenard.n.01', 'name': 'American_spikenard'}, {'id': 18045, 'synset': 'bristly_sarsaparilla.n.01', 'name': 'bristly_sarsaparilla'}, {'id': 18046, 'synset': 'japanese_angelica_tree.n.01', 'name': 'Japanese_angelica_tree'}, {'id': 18047, 'synset': 'chinese_angelica.n.01', 'name': 'Chinese_angelica'}, {'id': 18048, 'synset': 'ivy.n.01', 'name': 'ivy'}, {'id': 18049, 'synset': 'puka.n.02', 'name': 'puka'}, {'id': 18050, 'synset': 'ginseng.n.02', 'name': 'ginseng'}, {'id': 18051, 'synset': 'ginseng.n.01', 'name': 'ginseng'}, {'id': 18052, 'synset': 'umbrella_tree.n.01', 'name': 'umbrella_tree'}, {'id': 18053, 'synset': 'birthwort.n.01', 'name': 'birthwort'}, {'id': 18054, 'synset': "dutchman's-pipe.n.01", 'name': "Dutchman's-pipe"}, {'id': 18055, 'synset': 'virginia_snakeroot.n.01', 'name': 'Virginia_snakeroot'}, {'id': 18056, 'synset': 'canada_ginger.n.01', 'name': 'Canada_ginger'}, {'id': 18057, 'synset': 'heartleaf.n.02', 'name': 'heartleaf'}, {'id': 18058, 'synset': 'heartleaf.n.01', 'name': 'heartleaf'}, {'id': 18059, 'synset': 'asarabacca.n.01', 'name': 'asarabacca'}, {'id': 18060, 'synset': 'caryophyllaceous_plant.n.01', 'name': 'caryophyllaceous_plant'}, {'id': 18061, 'synset': 'corn_cockle.n.01', 'name': 'corn_cockle'}, {'id': 18062, 'synset': 'sandwort.n.03', 'name': 'sandwort'}, {'id': 18063, 'synset': 'mountain_sandwort.n.01', 'name': 'mountain_sandwort'}, {'id': 18064, 'synset': 'pine-barren_sandwort.n.01', 'name': 'pine-barren_sandwort'}, {'id': 18065, 'synset': 'seabeach_sandwort.n.01', 'name': 'seabeach_sandwort'}, {'id': 18066, 'synset': 'rock_sandwort.n.01', 'name': 'rock_sandwort'}, {'id': 18067, 'synset': 'thyme-leaved_sandwort.n.01', 'name': 'thyme-leaved_sandwort'}, {'id': 18068, 'synset': 'mouse-ear_chickweed.n.01', 'name': 'mouse-ear_chickweed'}, {'id': 18069, 'synset': 'snow-in-summer.n.02', 'name': 'snow-in-summer'}, {'id': 18070, 'synset': 'alpine_mouse-ear.n.01', 'name': 'Alpine_mouse-ear'}, {'id': 18071, 'synset': 'pink.n.02', 'name': 'pink'}, {'id': 18072, 'synset': 'sweet_william.n.01', 'name': 'sweet_William'}, {'id': 18073, 'synset': 'china_pink.n.01', 'name': 'china_pink'}, {'id': 18074, 'synset': 'japanese_pink.n.01', 'name': 'Japanese_pink'}, {'id': 18075, 'synset': 'maiden_pink.n.01', 'name': 'maiden_pink'}, {'id': 18076, 'synset': 'cheddar_pink.n.01', 'name': 'cheddar_pink'}, {'id': 18077, 'synset': 'button_pink.n.01', 'name': 'button_pink'}, {'id': 18078, 'synset': 'cottage_pink.n.01', 'name': 'cottage_pink'}, {'id': 18079, 'synset': 'fringed_pink.n.02', 'name': 'fringed_pink'}, {'id': 18080, 'synset': 'drypis.n.01', 'name': 'drypis'}, {'id': 18081, 'synset': "baby's_breath.n.01", 'name': "baby's_breath"}, {'id': 18082, 'synset': 'coral_necklace.n.01', 'name': 'coral_necklace'}, {'id': 18083, 'synset': 'lychnis.n.01', 'name': 'lychnis'}, {'id': 18084, 'synset': 'ragged_robin.n.01', 'name': 'ragged_robin'}, {'id': 18085, 'synset': 'scarlet_lychnis.n.01', 'name': 'scarlet_lychnis'}, {'id': 18086, 'synset': 'mullein_pink.n.01', 'name': 'mullein_pink'}, {'id': 18087, 'synset': 'sandwort.n.02', 'name': 'sandwort'}, {'id': 18088, 'synset': 'sandwort.n.01', 'name': 'sandwort'}, {'id': 18089, 'synset': 'soapwort.n.01', 'name': 'soapwort'}, {'id': 18090, 'synset': 'knawel.n.01', 'name': 'knawel'}, {'id': 18091, 'synset': 'silene.n.01', 'name': 'silene'}, {'id': 18092, 'synset': 'moss_campion.n.01', 'name': 'moss_campion'}, {'id': 18093, 'synset': 'wild_pink.n.02', 'name': 'wild_pink'}, {'id': 18094, 'synset': 'red_campion.n.01', 'name': 'red_campion'}, {'id': 18095, 'synset': 'white_campion.n.01', 'name': 'white_campion'}, {'id': 18096, 'synset': 'fire_pink.n.01', 'name': 'fire_pink'}, {'id': 18097, 'synset': 'bladder_campion.n.01', 'name': 'bladder_campion'}, {'id': 18098, 'synset': 'corn_spurry.n.01', 'name': 'corn_spurry'}, {'id': 18099, 'synset': 'sand_spurry.n.01', 'name': 'sand_spurry'}, {'id': 18100, 'synset': 'chickweed.n.01', 'name': 'chickweed'}, {'id': 18101, 'synset': 'common_chickweed.n.01', 'name': 'common_chickweed'}, {'id': 18102, 'synset': 'cowherb.n.01', 'name': 'cowherb'}, {'id': 18103, 'synset': 'hottentot_fig.n.01', 'name': 'Hottentot_fig'}, {'id': 18104, 'synset': 'livingstone_daisy.n.01', 'name': 'livingstone_daisy'}, {'id': 18105, 'synset': 'fig_marigold.n.01', 'name': 'fig_marigold'}, {'id': 18106, 'synset': 'ice_plant.n.01', 'name': 'ice_plant'}, {'id': 18107, 'synset': 'new_zealand_spinach.n.01', 'name': 'New_Zealand_spinach'}, {'id': 18108, 'synset': 'amaranth.n.02', 'name': 'amaranth'}, {'id': 18109, 'synset': 'amaranth.n.01', 'name': 'amaranth'}, {'id': 18110, 'synset': 'tumbleweed.n.04', 'name': 'tumbleweed'}, {'id': 18111, 'synset': "prince's-feather.n.02", 'name': "prince's-feather"}, {'id': 18112, 'synset': 'pigweed.n.02', 'name': 'pigweed'}, {'id': 18113, 'synset': 'thorny_amaranth.n.01', 'name': 'thorny_amaranth'}, {'id': 18114, 'synset': 'alligator_weed.n.01', 'name': 'alligator_weed'}, {'id': 18115, 'synset': 'cockscomb.n.01', 'name': 'cockscomb'}, {'id': 18116, 'synset': 'cottonweed.n.02', 'name': 'cottonweed'}, {'id': 18117, 'synset': 'globe_amaranth.n.01', 'name': 'globe_amaranth'}, {'id': 18118, 'synset': 'bloodleaf.n.01', 'name': 'bloodleaf'}, {'id': 18119, 'synset': 'saltwort.n.02', 'name': 'saltwort'}, {'id': 18120, 'synset': "lamb's-quarters.n.01", 'name': "lamb's-quarters"}, {'id': 18121, 'synset': 'good-king-henry.n.01', 'name': 'good-king-henry'}, {'id': 18122, 'synset': 'jerusalem_oak.n.01', 'name': 'Jerusalem_oak'}, {'id': 18123, 'synset': 'oak-leaved_goosefoot.n.01', 'name': 'oak-leaved_goosefoot'}, {'id': 18124, 'synset': 'sowbane.n.01', 'name': 'sowbane'}, {'id': 18125, 'synset': 'nettle-leaved_goosefoot.n.01', 'name': 'nettle-leaved_goosefoot'}, {'id': 18126, 'synset': 'red_goosefoot.n.01', 'name': 'red_goosefoot'}, {'id': 18127, 'synset': 'stinking_goosefoot.n.01', 'name': 'stinking_goosefoot'}, {'id': 18128, 'synset': 'orach.n.01', 'name': 'orach'}, {'id': 18129, 'synset': 'saltbush.n.01', 'name': 'saltbush'}, {'id': 18130, 'synset': 'garden_orache.n.01', 'name': 'garden_orache'}, {'id': 18131, 'synset': 'desert_holly.n.01', 'name': 'desert_holly'}, {'id': 18132, 'synset': 'quail_bush.n.01', 'name': 'quail_bush'}, {'id': 18133, 'synset': 'beet.n.01', 'name': 'beet'}, {'id': 18134, 'synset': 'beetroot.n.01', 'name': 'beetroot'}, {'id': 18135, 'synset': 'chard.n.01', 'name': 'chard'}, {'id': 18136, 'synset': 'mangel-wurzel.n.01', 'name': 'mangel-wurzel'}, {'id': 18137, 'synset': 'winged_pigweed.n.01', 'name': 'winged_pigweed'}, {'id': 18138, 'synset': 'halogeton.n.01', 'name': 'halogeton'}, {'id': 18139, 'synset': 'glasswort.n.02', 'name': 'glasswort'}, {'id': 18140, 'synset': 'saltwort.n.01', 'name': 'saltwort'}, {'id': 18141, 'synset': 'russian_thistle.n.01', 'name': 'Russian_thistle'}, {'id': 18142, 'synset': 'greasewood.n.01', 'name': 'greasewood'}, {'id': 18143, 'synset': 'scarlet_musk_flower.n.01', 'name': 'scarlet_musk_flower'}, {'id': 18144, 'synset': 'sand_verbena.n.01', 'name': 'sand_verbena'}, {'id': 18145, 'synset': 'sweet_sand_verbena.n.01', 'name': 'sweet_sand_verbena'}, {'id': 18146, 'synset': 'yellow_sand_verbena.n.01', 'name': 'yellow_sand_verbena'}, {'id': 18147, 'synset': 'beach_pancake.n.01', 'name': 'beach_pancake'}, {'id': 18148, 'synset': 'beach_sand_verbena.n.01', 'name': 'beach_sand_verbena'}, {'id': 18149, 'synset': 'desert_sand_verbena.n.01', 'name': 'desert_sand_verbena'}, {'id': 18150, 'synset': "trailing_four_o'clock.n.01", 'name': "trailing_four_o'clock"}, {'id': 18151, 'synset': 'bougainvillea.n.01', 'name': 'bougainvillea'}, {'id': 18152, 'synset': 'umbrellawort.n.01', 'name': 'umbrellawort'}, {'id': 18153, 'synset': "four_o'clock.n.01", 'name': "four_o'clock"}, {'id': 18154, 'synset': "common_four-o'clock.n.01", 'name': "common_four-o'clock"}, {'id': 18155, 'synset': "california_four_o'clock.n.01", 'name': "California_four_o'clock"}, {'id': 18156, 'synset': "sweet_four_o'clock.n.01", 'name': "sweet_four_o'clock"}, {'id': 18157, 'synset': "desert_four_o'clock.n.01", 'name': "desert_four_o'clock"}, {'id': 18158, 'synset': "mountain_four_o'clock.n.01", 'name': "mountain_four_o'clock"}, {'id': 18159, 'synset': 'cockspur.n.02', 'name': 'cockspur'}, {'id': 18160, 'synset': 'rattail_cactus.n.01', 'name': 'rattail_cactus'}, {'id': 18161, 'synset': 'saguaro.n.01', 'name': 'saguaro'}, {'id': 18162, 'synset': 'night-blooming_cereus.n.03', 'name': 'night-blooming_cereus'}, {'id': 18163, 'synset': 'echinocactus.n.01', 'name': 'echinocactus'}, {'id': 18164, 'synset': 'hedgehog_cactus.n.01', 'name': 'hedgehog_cactus'}, {'id': 18165, 'synset': 'golden_barrel_cactus.n.01', 'name': 'golden_barrel_cactus'}, {'id': 18166, 'synset': 'hedgehog_cereus.n.01', 'name': 'hedgehog_cereus'}, {'id': 18167, 'synset': 'rainbow_cactus.n.01', 'name': 'rainbow_cactus'}, {'id': 18168, 'synset': 'epiphyllum.n.01', 'name': 'epiphyllum'}, {'id': 18169, 'synset': 'barrel_cactus.n.01', 'name': 'barrel_cactus'}, {'id': 18170, 'synset': 'night-blooming_cereus.n.02', 'name': 'night-blooming_cereus'}, {'id': 18171, 'synset': 'chichipe.n.01', 'name': 'chichipe'}, {'id': 18172, 'synset': 'mescal.n.01', 'name': 'mescal'}, {'id': 18173, 'synset': 'mescal_button.n.01', 'name': 'mescal_button'}, {'id': 18174, 'synset': 'mammillaria.n.01', 'name': 'mammillaria'}, {'id': 18175, 'synset': 'feather_ball.n.01', 'name': 'feather_ball'}, {'id': 18176, 'synset': 'garambulla.n.01', 'name': 'garambulla'}, {'id': 18177, 'synset': "knowlton's_cactus.n.01", 'name': "Knowlton's_cactus"}, {'id': 18178, 'synset': 'nopal.n.02', 'name': 'nopal'}, {'id': 18179, 'synset': 'prickly_pear.n.01', 'name': 'prickly_pear'}, {'id': 18180, 'synset': 'cholla.n.01', 'name': 'cholla'}, {'id': 18181, 'synset': 'nopal.n.01', 'name': 'nopal'}, {'id': 18182, 'synset': 'tuna.n.01', 'name': 'tuna'}, {'id': 18183, 'synset': 'barbados_gooseberry.n.01', 'name': 'Barbados_gooseberry'}, {'id': 18184, 'synset': 'mistletoe_cactus.n.01', 'name': 'mistletoe_cactus'}, {'id': 18185, 'synset': 'christmas_cactus.n.01', 'name': 'Christmas_cactus'}, {'id': 18186, 'synset': 'night-blooming_cereus.n.01', 'name': 'night-blooming_cereus'}, {'id': 18187, 'synset': 'crab_cactus.n.01', 'name': 'crab_cactus'}, {'id': 18188, 'synset': 'pokeweed.n.01', 'name': 'pokeweed'}, {'id': 18189, 'synset': 'indian_poke.n.02', 'name': 'Indian_poke'}, {'id': 18190, 'synset': 'poke.n.01', 'name': 'poke'}, {'id': 18191, 'synset': 'ombu.n.01', 'name': 'ombu'}, {'id': 18192, 'synset': 'bloodberry.n.01', 'name': 'bloodberry'}, {'id': 18193, 'synset': 'portulaca.n.01', 'name': 'portulaca'}, {'id': 18194, 'synset': 'rose_moss.n.01', 'name': 'rose_moss'}, {'id': 18195, 'synset': 'common_purslane.n.01', 'name': 'common_purslane'}, {'id': 18196, 'synset': 'rock_purslane.n.01', 'name': 'rock_purslane'}, {'id': 18197, 'synset': 'red_maids.n.01', 'name': 'red_maids'}, {'id': 18198, 'synset': 'carolina_spring_beauty.n.01', 'name': 'Carolina_spring_beauty'}, {'id': 18199, 'synset': 'spring_beauty.n.01', 'name': 'spring_beauty'}, {'id': 18200, 'synset': 'virginia_spring_beauty.n.01', 'name': 'Virginia_spring_beauty'}, {'id': 18201, 'synset': 'siskiyou_lewisia.n.01', 'name': 'siskiyou_lewisia'}, {'id': 18202, 'synset': 'bitterroot.n.01', 'name': 'bitterroot'}, {'id': 18203, 'synset': 'broad-leaved_montia.n.01', 'name': 'broad-leaved_montia'}, {'id': 18204, 'synset': 'blinks.n.01', 'name': 'blinks'}, {'id': 18205, 'synset': 'toad_lily.n.01', 'name': 'toad_lily'}, {'id': 18206, 'synset': 'winter_purslane.n.01', 'name': 'winter_purslane'}, {'id': 18207, 'synset': 'flame_flower.n.02', 'name': 'flame_flower'}, {'id': 18208, 'synset': 'pigmy_talinum.n.01', 'name': 'pigmy_talinum'}, {'id': 18209, 'synset': 'jewels-of-opar.n.01', 'name': 'jewels-of-opar'}, {'id': 18210, 'synset': 'caper.n.01', 'name': 'caper'}, {'id': 18211, 'synset': 'native_pomegranate.n.01', 'name': 'native_pomegranate'}, {'id': 18212, 'synset': 'caper_tree.n.02', 'name': 'caper_tree'}, {'id': 18213, 'synset': 'caper_tree.n.01', 'name': 'caper_tree'}, {'id': 18214, 'synset': 'common_caper.n.01', 'name': 'common_caper'}, {'id': 18215, 'synset': 'spiderflower.n.01', 'name': 'spiderflower'}, {'id': 18216, 'synset': 'rocky_mountain_bee_plant.n.01', 'name': 'Rocky_Mountain_bee_plant'}, {'id': 18217, 'synset': 'clammyweed.n.01', 'name': 'clammyweed'}, {'id': 18218, 'synset': 'crucifer.n.01', 'name': 'crucifer'}, {'id': 18219, 'synset': 'cress.n.01', 'name': 'cress'}, {'id': 18220, 'synset': 'watercress.n.01', 'name': 'watercress'}, {'id': 18221, 'synset': 'stonecress.n.01', 'name': 'stonecress'}, {'id': 18222, 'synset': 'garlic_mustard.n.01', 'name': 'garlic_mustard'}, {'id': 18223, 'synset': 'alyssum.n.01', 'name': 'alyssum'}, {'id': 18224, 'synset': 'rose_of_jericho.n.02', 'name': 'rose_of_Jericho'}, {'id': 18225, 'synset': 'arabidopsis_thaliana.n.01', 'name': 'Arabidopsis_thaliana'}, {'id': 18226, 'synset': 'arabidopsis_lyrata.n.01', 'name': 'Arabidopsis_lyrata'}, {'id': 18227, 'synset': 'rock_cress.n.01', 'name': 'rock_cress'}, {'id': 18228, 'synset': 'sicklepod.n.02', 'name': 'sicklepod'}, {'id': 18229, 'synset': 'tower_mustard.n.01', 'name': 'tower_mustard'}, {'id': 18230, 'synset': 'horseradish.n.01', 'name': 'horseradish'}, {'id': 18231, 'synset': 'winter_cress.n.01', 'name': 'winter_cress'}, {'id': 18232, 'synset': 'yellow_rocket.n.01', 'name': 'yellow_rocket'}, {'id': 18233, 'synset': 'hoary_alison.n.01', 'name': 'hoary_alison'}, {'id': 18234, 'synset': 'buckler_mustard.n.01', 'name': 'buckler_mustard'}, {'id': 18235, 'synset': 'wild_cabbage.n.01', 'name': 'wild_cabbage'}, {'id': 18236, 'synset': 'cabbage.n.03', 'name': 'cabbage'}, {'id': 18237, 'synset': 'head_cabbage.n.01', 'name': 'head_cabbage'}, {'id': 18238, 'synset': 'savoy_cabbage.n.01', 'name': 'savoy_cabbage'}, {'id': 18239, 'synset': 'brussels_sprout.n.01', 'name': 'brussels_sprout'}, {'id': 18240, 'synset': 'cauliflower.n.01', 'name': 'cauliflower'}, {'id': 18241, 'synset': 'collard.n.01', 'name': 'collard'}, {'id': 18242, 'synset': 'kohlrabi.n.01', 'name': 'kohlrabi'}, {'id': 18243, 'synset': 'turnip_plant.n.01', 'name': 'turnip_plant'}, {'id': 18244, 'synset': 'rutabaga.n.02', 'name': 'rutabaga'}, {'id': 18245, 'synset': 'broccoli_raab.n.01', 'name': 'broccoli_raab'}, {'id': 18246, 'synset': 'mustard.n.01', 'name': 'mustard'}, {'id': 18247, 'synset': 'chinese_mustard.n.01', 'name': 'chinese_mustard'}, {'id': 18248, 'synset': 'bok_choy.n.01', 'name': 'bok_choy'}, {'id': 18249, 'synset': 'rape.n.01', 'name': 'rape'}, {'id': 18250, 'synset': 'rapeseed.n.01', 'name': 'rapeseed'}, {'id': 18251, 'synset': "shepherd's_purse.n.01", 'name': "shepherd's_purse"}, {'id': 18252, 'synset': "lady's_smock.n.01", 'name': "lady's_smock"}, {'id': 18253, 'synset': 'coral-root_bittercress.n.01', 'name': 'coral-root_bittercress'}, {'id': 18254, 'synset': 'crinkleroot.n.01', 'name': 'crinkleroot'}, {'id': 18255, 'synset': 'american_watercress.n.01', 'name': 'American_watercress'}, {'id': 18256, 'synset': 'spring_cress.n.01', 'name': 'spring_cress'}, {'id': 18257, 'synset': 'purple_cress.n.01', 'name': 'purple_cress'}, {'id': 18258, 'synset': 'wallflower.n.02', 'name': 'wallflower'}, {'id': 18259, 'synset': 'prairie_rocket.n.02', 'name': 'prairie_rocket'}, {'id': 18260, 'synset': 'scurvy_grass.n.01', 'name': 'scurvy_grass'}, {'id': 18261, 'synset': 'sea_kale.n.01', 'name': 'sea_kale'}, {'id': 18262, 'synset': 'tansy_mustard.n.01', 'name': 'tansy_mustard'}, {'id': 18263, 'synset': 'draba.n.01', 'name': 'draba'}, {'id': 18264, 'synset': 'wallflower.n.01', 'name': 'wallflower'}, {'id': 18265, 'synset': 'prairie_rocket.n.01', 'name': 'prairie_rocket'}, {'id': 18266, 'synset': 'siberian_wall_flower.n.01', 'name': 'Siberian_wall_flower'}, {'id': 18267, 'synset': 'western_wall_flower.n.01', 'name': 'western_wall_flower'}, {'id': 18268, 'synset': 'wormseed_mustard.n.01', 'name': 'wormseed_mustard'}, {'id': 18269, 'synset': 'heliophila.n.01', 'name': 'heliophila'}, {'id': 18270, 'synset': 'damask_violet.n.01', 'name': 'damask_violet'}, {'id': 18271, 'synset': 'tansy-leaved_rocket.n.01', 'name': 'tansy-leaved_rocket'}, {'id': 18272, 'synset': 'candytuft.n.01', 'name': 'candytuft'}, {'id': 18273, 'synset': 'woad.n.02', 'name': 'woad'}, {'id': 18274, 'synset': "dyer's_woad.n.01", 'name': "dyer's_woad"}, {'id': 18275, 'synset': 'bladderpod.n.04', 'name': 'bladderpod'}, {'id': 18276, 'synset': 'sweet_alyssum.n.01', 'name': 'sweet_alyssum'}, {'id': 18277, 'synset': 'malcolm_stock.n.01', 'name': 'Malcolm_stock'}, {'id': 18278, 'synset': 'virginian_stock.n.01', 'name': 'Virginian_stock'}, {'id': 18279, 'synset': 'stock.n.12', 'name': 'stock'}, {'id': 18280, 'synset': 'brompton_stock.n.01', 'name': 'brompton_stock'}, {'id': 18281, 'synset': 'bladderpod.n.03', 'name': 'bladderpod'}, {'id': 18282, 'synset': 'chamois_cress.n.01', 'name': 'chamois_cress'}, {'id': 18283, 'synset': 'radish_plant.n.01', 'name': 'radish_plant'}, {'id': 18284, 'synset': 'jointed_charlock.n.01', 'name': 'jointed_charlock'}, {'id': 18285, 'synset': 'radish.n.04', 'name': 'radish'}, {'id': 18286, 'synset': 'radish.n.02', 'name': 'radish'}, {'id': 18287, 'synset': 'marsh_cress.n.01', 'name': 'marsh_cress'}, {'id': 18288, 'synset': 'great_yellowcress.n.01', 'name': 'great_yellowcress'}, {'id': 18289, 'synset': 'schizopetalon.n.01', 'name': 'schizopetalon'}, {'id': 18290, 'synset': 'field_mustard.n.01', 'name': 'field_mustard'}, {'id': 18291, 'synset': 'hedge_mustard.n.01', 'name': 'hedge_mustard'}, {'id': 18292, 'synset': 'desert_plume.n.01', 'name': 'desert_plume'}, {'id': 18293, 'synset': 'pennycress.n.01', 'name': 'pennycress'}, {'id': 18294, 'synset': 'field_pennycress.n.01', 'name': 'field_pennycress'}, {'id': 18295, 'synset': 'fringepod.n.01', 'name': 'fringepod'}, {'id': 18296, 'synset': 'bladderpod.n.02', 'name': 'bladderpod'}, {'id': 18297, 'synset': 'wasabi.n.01', 'name': 'wasabi'}, {'id': 18298, 'synset': 'poppy.n.01', 'name': 'poppy'}, {'id': 18299, 'synset': 'iceland_poppy.n.02', 'name': 'Iceland_poppy'}, {'id': 18300, 'synset': 'western_poppy.n.01', 'name': 'western_poppy'}, {'id': 18301, 'synset': 'prickly_poppy.n.02', 'name': 'prickly_poppy'}, {'id': 18302, 'synset': 'iceland_poppy.n.01', 'name': 'Iceland_poppy'}, {'id': 18303, 'synset': 'oriental_poppy.n.01', 'name': 'oriental_poppy'}, {'id': 18304, 'synset': 'corn_poppy.n.01', 'name': 'corn_poppy'}, {'id': 18305, 'synset': 'opium_poppy.n.01', 'name': 'opium_poppy'}, {'id': 18306, 'synset': 'prickly_poppy.n.01', 'name': 'prickly_poppy'}, {'id': 18307, 'synset': 'mexican_poppy.n.01', 'name': 'Mexican_poppy'}, {'id': 18308, 'synset': 'bocconia.n.02', 'name': 'bocconia'}, {'id': 18309, 'synset': 'celandine.n.02', 'name': 'celandine'}, {'id': 18310, 'synset': 'corydalis.n.01', 'name': 'corydalis'}, {'id': 18311, 'synset': 'climbing_corydalis.n.01', 'name': 'climbing_corydalis'}, {'id': 18312, 'synset': 'california_poppy.n.01', 'name': 'California_poppy'}, {'id': 18313, 'synset': 'horn_poppy.n.01', 'name': 'horn_poppy'}, {'id': 18314, 'synset': 'golden_cup.n.01', 'name': 'golden_cup'}, {'id': 18315, 'synset': 'plume_poppy.n.01', 'name': 'plume_poppy'}, {'id': 18316, 'synset': 'blue_poppy.n.01', 'name': 'blue_poppy'}, {'id': 18317, 'synset': 'welsh_poppy.n.01', 'name': 'Welsh_poppy'}, {'id': 18318, 'synset': 'creamcups.n.01', 'name': 'creamcups'}, {'id': 18319, 'synset': 'matilija_poppy.n.01', 'name': 'matilija_poppy'}, {'id': 18320, 'synset': 'wind_poppy.n.01', 'name': 'wind_poppy'}, {'id': 18321, 'synset': 'celandine_poppy.n.01', 'name': 'celandine_poppy'}, {'id': 18322, 'synset': 'climbing_fumitory.n.01', 'name': 'climbing_fumitory'}, {'id': 18323, 'synset': 'bleeding_heart.n.01', 'name': 'bleeding_heart'}, {'id': 18324, 'synset': "dutchman's_breeches.n.01", 'name': "Dutchman's_breeches"}, {'id': 18325, 'synset': 'squirrel_corn.n.01', 'name': 'squirrel_corn'}, {'id': 18326, 'synset': 'composite.n.02', 'name': 'composite'}, {'id': 18327, 'synset': 'compass_plant.n.02', 'name': 'compass_plant'}, {'id': 18328, 'synset': 'everlasting.n.01', 'name': 'everlasting'}, {'id': 18329, 'synset': 'achillea.n.01', 'name': 'achillea'}, {'id': 18330, 'synset': 'yarrow.n.01', 'name': 'yarrow'}, {'id': 18331, 'synset': 'pink-and-white_everlasting.n.01', 'name': 'pink-and-white_everlasting'}, {'id': 18332, 'synset': 'white_snakeroot.n.01', 'name': 'white_snakeroot'}, {'id': 18333, 'synset': 'ageratum.n.02', 'name': 'ageratum'}, {'id': 18334, 'synset': 'common_ageratum.n.01', 'name': 'common_ageratum'}, {'id': 18335, 'synset': 'sweet_sultan.n.03', 'name': 'sweet_sultan'}, {'id': 18336, 'synset': 'ragweed.n.02', 'name': 'ragweed'}, {'id': 18337, 'synset': 'common_ragweed.n.01', 'name': 'common_ragweed'}, {'id': 18338, 'synset': 'great_ragweed.n.01', 'name': 'great_ragweed'}, {'id': 18339, 'synset': 'western_ragweed.n.01', 'name': 'western_ragweed'}, {'id': 18340, 'synset': 'ammobium.n.01', 'name': 'ammobium'}, {'id': 18341, 'synset': 'winged_everlasting.n.01', 'name': 'winged_everlasting'}, {'id': 18342, 'synset': 'pellitory.n.02', 'name': 'pellitory'}, {'id': 18343, 'synset': 'pearly_everlasting.n.01', 'name': 'pearly_everlasting'}, {'id': 18344, 'synset': 'andryala.n.01', 'name': 'andryala'}, {'id': 18345, 'synset': 'plantain-leaved_pussytoes.n.01', 'name': 'plantain-leaved_pussytoes'}, {'id': 18346, 'synset': 'field_pussytoes.n.01', 'name': 'field_pussytoes'}, {'id': 18347, 'synset': 'solitary_pussytoes.n.01', 'name': 'solitary_pussytoes'}, {'id': 18348, 'synset': 'mountain_everlasting.n.01', 'name': 'mountain_everlasting'}, {'id': 18349, 'synset': 'mayweed.n.01', 'name': 'mayweed'}, {'id': 18350, 'synset': 'yellow_chamomile.n.01', 'name': 'yellow_chamomile'}, {'id': 18351, 'synset': 'corn_chamomile.n.01', 'name': 'corn_chamomile'}, {'id': 18352, 'synset': 'woolly_daisy.n.01', 'name': 'woolly_daisy'}, {'id': 18353, 'synset': 'burdock.n.01', 'name': 'burdock'}, {'id': 18354, 'synset': 'great_burdock.n.01', 'name': 'great_burdock'}, {'id': 18355, 'synset': 'african_daisy.n.03', 'name': 'African_daisy'}, {'id': 18356, 'synset': 'blue-eyed_african_daisy.n.01', 'name': 'blue-eyed_African_daisy'}, {'id': 18357, 'synset': 'marguerite.n.02', 'name': 'marguerite'}, {'id': 18358, 'synset': 'silversword.n.01', 'name': 'silversword'}, {'id': 18359, 'synset': 'arnica.n.02', 'name': 'arnica'}, {'id': 18360, 'synset': 'heartleaf_arnica.n.01', 'name': 'heartleaf_arnica'}, {'id': 18361, 'synset': 'arnica_montana.n.01', 'name': 'Arnica_montana'}, {'id': 18362, 'synset': 'lamb_succory.n.01', 'name': 'lamb_succory'}, {'id': 18363, 'synset': 'artemisia.n.01', 'name': 'artemisia'}, {'id': 18364, 'synset': 'mugwort.n.01', 'name': 'mugwort'}, {'id': 18365, 'synset': 'sweet_wormwood.n.01', 'name': 'sweet_wormwood'}, {'id': 18366, 'synset': 'field_wormwood.n.01', 'name': 'field_wormwood'}, {'id': 18367, 'synset': 'tarragon.n.01', 'name': 'tarragon'}, {'id': 18368, 'synset': 'sand_sage.n.01', 'name': 'sand_sage'}, {'id': 18369, 'synset': 'wormwood_sage.n.01', 'name': 'wormwood_sage'}, {'id': 18370, 'synset': 'western_mugwort.n.01', 'name': 'western_mugwort'}, {'id': 18371, 'synset': 'roman_wormwood.n.01', 'name': 'Roman_wormwood'}, {'id': 18372, 'synset': 'bud_brush.n.01', 'name': 'bud_brush'}, {'id': 18373, 'synset': 'common_mugwort.n.01', 'name': 'common_mugwort'}, {'id': 18374, 'synset': 'aster.n.01', 'name': 'aster'}, {'id': 18375, 'synset': 'wood_aster.n.01', 'name': 'wood_aster'}, {'id': 18376, 'synset': 'whorled_aster.n.01', 'name': 'whorled_aster'}, {'id': 18377, 'synset': 'heath_aster.n.02', 'name': 'heath_aster'}, {'id': 18378, 'synset': 'heart-leaved_aster.n.01', 'name': 'heart-leaved_aster'}, {'id': 18379, 'synset': 'white_wood_aster.n.01', 'name': 'white_wood_aster'}, {'id': 18380, 'synset': 'bushy_aster.n.01', 'name': 'bushy_aster'}, {'id': 18381, 'synset': 'heath_aster.n.01', 'name': 'heath_aster'}, {'id': 18382, 'synset': 'white_prairie_aster.n.01', 'name': 'white_prairie_aster'}, {'id': 18383, 'synset': 'stiff_aster.n.01', 'name': 'stiff_aster'}, {'id': 18384, 'synset': 'goldilocks.n.01', 'name': 'goldilocks'}, {'id': 18385, 'synset': 'large-leaved_aster.n.01', 'name': 'large-leaved_aster'}, {'id': 18386, 'synset': 'new_england_aster.n.01', 'name': 'New_England_aster'}, {'id': 18387, 'synset': 'michaelmas_daisy.n.01', 'name': 'Michaelmas_daisy'}, {'id': 18388, 'synset': 'upland_white_aster.n.01', 'name': 'upland_white_aster'}, {'id': 18389, 'synset': "short's_aster.n.01", 'name': "Short's_aster"}, {'id': 18390, 'synset': 'sea_aster.n.01', 'name': 'sea_aster'}, {'id': 18391, 'synset': 'prairie_aster.n.01', 'name': 'prairie_aster'}, {'id': 18392, 'synset': 'annual_salt-marsh_aster.n.01', 'name': 'annual_salt-marsh_aster'}, {'id': 18393, 'synset': 'aromatic_aster.n.01', 'name': 'aromatic_aster'}, {'id': 18394, 'synset': 'arrow_leaved_aster.n.01', 'name': 'arrow_leaved_aster'}, {'id': 18395, 'synset': 'azure_aster.n.01', 'name': 'azure_aster'}, {'id': 18396, 'synset': 'bog_aster.n.01', 'name': 'bog_aster'}, {'id': 18397, 'synset': 'crooked-stemmed_aster.n.01', 'name': 'crooked-stemmed_aster'}, {'id': 18398, 'synset': 'eastern_silvery_aster.n.01', 'name': 'Eastern_silvery_aster'}, {'id': 18399, 'synset': 'flat-topped_white_aster.n.01', 'name': 'flat-topped_white_aster'}, {'id': 18400, 'synset': 'late_purple_aster.n.01', 'name': 'late_purple_aster'}, {'id': 18401, 'synset': 'panicled_aster.n.01', 'name': 'panicled_aster'}, {'id': 18402, 'synset': 'perennial_salt_marsh_aster.n.01', 'name': 'perennial_salt_marsh_aster'}, {'id': 18403, 'synset': 'purple-stemmed_aster.n.01', 'name': 'purple-stemmed_aster'}, {'id': 18404, 'synset': 'rough-leaved_aster.n.01', 'name': 'rough-leaved_aster'}, {'id': 18405, 'synset': 'rush_aster.n.01', 'name': 'rush_aster'}, {'id': 18406, 'synset': "schreiber's_aster.n.01", 'name': "Schreiber's_aster"}, {'id': 18407, 'synset': 'small_white_aster.n.01', 'name': 'small_white_aster'}, {'id': 18408, 'synset': 'smooth_aster.n.01', 'name': 'smooth_aster'}, {'id': 18409, 'synset': 'southern_aster.n.01', 'name': 'southern_aster'}, {'id': 18410, 'synset': 'starved_aster.n.01', 'name': 'starved_aster'}, {'id': 18411, 'synset': "tradescant's_aster.n.01", 'name': "tradescant's_aster"}, {'id': 18412, 'synset': 'wavy-leaved_aster.n.01', 'name': 'wavy-leaved_aster'}, {'id': 18413, 'synset': 'western_silvery_aster.n.01', 'name': 'Western_silvery_aster'}, {'id': 18414, 'synset': 'willow_aster.n.01', 'name': 'willow_aster'}, {'id': 18415, 'synset': 'ayapana.n.01', 'name': 'ayapana'}, {'id': 18416, 'synset': 'mule_fat.n.01', 'name': 'mule_fat'}, {'id': 18417, 'synset': 'balsamroot.n.01', 'name': 'balsamroot'}, {'id': 18418, 'synset': 'daisy.n.01', 'name': 'daisy'}, {'id': 18419, 'synset': 'common_daisy.n.01', 'name': 'common_daisy'}, {'id': 18420, 'synset': 'bur_marigold.n.01', 'name': 'bur_marigold'}, {'id': 18421, 'synset': 'spanish_needles.n.02', 'name': 'Spanish_needles'}, {'id': 18422, 'synset': 'tickseed_sunflower.n.01', 'name': 'tickseed_sunflower'}, {'id': 18423, 'synset': 'european_beggar-ticks.n.01', 'name': 'European_beggar-ticks'}, {'id': 18424, 'synset': 'slender_knapweed.n.01', 'name': 'slender_knapweed'}, {'id': 18425, 'synset': 'false_chamomile.n.01', 'name': 'false_chamomile'}, {'id': 18426, 'synset': 'swan_river_daisy.n.01', 'name': 'Swan_River_daisy'}, {'id': 18427, 'synset': 'woodland_oxeye.n.01', 'name': 'woodland_oxeye'}, {'id': 18428, 'synset': 'indian_plantain.n.01', 'name': 'Indian_plantain'}, {'id': 18429, 'synset': 'calendula.n.01', 'name': 'calendula'}, {'id': 18430, 'synset': 'common_marigold.n.01', 'name': 'common_marigold'}, {'id': 18431, 'synset': 'china_aster.n.01', 'name': 'China_aster'}, {'id': 18432, 'synset': 'thistle.n.01', 'name': 'thistle'}, {'id': 18433, 'synset': 'welted_thistle.n.01', 'name': 'welted_thistle'}, {'id': 18434, 'synset': 'musk_thistle.n.01', 'name': 'musk_thistle'}, {'id': 18435, 'synset': 'carline_thistle.n.01', 'name': 'carline_thistle'}, {'id': 18436, 'synset': 'stemless_carline_thistle.n.01', 'name': 'stemless_carline_thistle'}, {'id': 18437, 'synset': 'common_carline_thistle.n.01', 'name': 'common_carline_thistle'}, {'id': 18438, 'synset': 'safflower.n.01', 'name': 'safflower'}, {'id': 18439, 'synset': 'safflower_seed.n.01', 'name': 'safflower_seed'}, {'id': 18440, 'synset': 'catananche.n.01', 'name': 'catananche'}, {'id': 18441, 'synset': 'blue_succory.n.01', 'name': 'blue_succory'}, {'id': 18442, 'synset': 'centaury.n.02', 'name': 'centaury'}, {'id': 18443, 'synset': 'dusty_miller.n.03', 'name': 'dusty_miller'}, {'id': 18444, 'synset': 'cornflower.n.02', 'name': 'cornflower'}, {'id': 18445, 'synset': 'star-thistle.n.01', 'name': 'star-thistle'}, {'id': 18446, 'synset': 'knapweed.n.01', 'name': 'knapweed'}, {'id': 18447, 'synset': 'sweet_sultan.n.02', 'name': 'sweet_sultan'}, {'id': 18448, 'synset': 'great_knapweed.n.01', 'name': 'great_knapweed'}, {'id': 18449, 'synset': "barnaby's_thistle.n.01", 'name': "Barnaby's_thistle"}, {'id': 18450, 'synset': 'chamomile.n.01', 'name': 'chamomile'}, {'id': 18451, 'synset': 'chaenactis.n.01', 'name': 'chaenactis'}, {'id': 18452, 'synset': 'chrysanthemum.n.02', 'name': 'chrysanthemum'}, {'id': 18453, 'synset': 'corn_marigold.n.01', 'name': 'corn_marigold'}, {'id': 18454, 'synset': 'crown_daisy.n.01', 'name': 'crown_daisy'}, {'id': 18455, 'synset': 'chop-suey_greens.n.01', 'name': 'chop-suey_greens'}, {'id': 18456, 'synset': 'golden_aster.n.01', 'name': 'golden_aster'}, {'id': 18457, 'synset': 'maryland_golden_aster.n.01', 'name': 'Maryland_golden_aster'}, {'id': 18458, 'synset': 'goldenbush.n.02', 'name': 'goldenbush'}, {'id': 18459, 'synset': 'rabbit_brush.n.01', 'name': 'rabbit_brush'}, {'id': 18460, 'synset': 'chicory.n.02', 'name': 'chicory'}, {'id': 18461, 'synset': 'endive.n.01', 'name': 'endive'}, {'id': 18462, 'synset': 'chicory.n.01', 'name': 'chicory'}, {'id': 18463, 'synset': 'plume_thistle.n.01', 'name': 'plume_thistle'}, {'id': 18464, 'synset': 'canada_thistle.n.01', 'name': 'Canada_thistle'}, {'id': 18465, 'synset': 'field_thistle.n.01', 'name': 'field_thistle'}, {'id': 18466, 'synset': 'woolly_thistle.n.02', 'name': 'woolly_thistle'}, {'id': 18467, 'synset': 'european_woolly_thistle.n.01', 'name': 'European_woolly_thistle'}, {'id': 18468, 'synset': 'melancholy_thistle.n.01', 'name': 'melancholy_thistle'}, {'id': 18469, 'synset': 'brook_thistle.n.01', 'name': 'brook_thistle'}, {'id': 18470, 'synset': 'bull_thistle.n.01', 'name': 'bull_thistle'}, {'id': 18471, 'synset': 'blessed_thistle.n.02', 'name': 'blessed_thistle'}, {'id': 18472, 'synset': 'mistflower.n.01', 'name': 'mistflower'}, {'id': 18473, 'synset': 'horseweed.n.02', 'name': 'horseweed'}, {'id': 18474, 'synset': 'coreopsis.n.01', 'name': 'coreopsis'}, {'id': 18475, 'synset': 'giant_coreopsis.n.01', 'name': 'giant_coreopsis'}, {'id': 18476, 'synset': 'sea_dahlia.n.01', 'name': 'sea_dahlia'}, {'id': 18477, 'synset': 'calliopsis.n.01', 'name': 'calliopsis'}, {'id': 18478, 'synset': 'cosmos.n.02', 'name': 'cosmos'}, {'id': 18479, 'synset': 'brass_buttons.n.01', 'name': 'brass_buttons'}, {'id': 18480, 'synset': 'billy_buttons.n.01', 'name': 'billy_buttons'}, {'id': 18481, 'synset': "hawk's-beard.n.01", 'name': "hawk's-beard"}, {'id': 18482, 'synset': 'artichoke.n.01', 'name': 'artichoke'}, {'id': 18483, 'synset': 'cardoon.n.01', 'name': 'cardoon'}, {'id': 18484, 'synset': 'dahlia.n.01', 'name': 'dahlia'}, {'id': 18485, 'synset': 'german_ivy.n.01', 'name': 'German_ivy'}, {'id': 18486, 'synset': "florist's_chrysanthemum.n.01", 'name': "florist's_chrysanthemum"}, {'id': 18487, 'synset': 'cape_marigold.n.01', 'name': 'cape_marigold'}, {'id': 18488, 'synset': "leopard's-bane.n.01", 'name': "leopard's-bane"}, {'id': 18489, 'synset': 'coneflower.n.03', 'name': 'coneflower'}, {'id': 18490, 'synset': 'globe_thistle.n.01', 'name': 'globe_thistle'}, {'id': 18491, 'synset': "elephant's-foot.n.02", 'name': "elephant's-foot"}, {'id': 18492, 'synset': 'tassel_flower.n.01', 'name': 'tassel_flower'}, {'id': 18493, 'synset': 'brittlebush.n.01', 'name': 'brittlebush'}, {'id': 18494, 'synset': 'sunray.n.02', 'name': 'sunray'}, {'id': 18495, 'synset': 'engelmannia.n.01', 'name': 'engelmannia'}, {'id': 18496, 'synset': 'fireweed.n.02', 'name': 'fireweed'}, {'id': 18497, 'synset': 'fleabane.n.02', 'name': 'fleabane'}, {'id': 18498, 'synset': 'blue_fleabane.n.01', 'name': 'blue_fleabane'}, {'id': 18499, 'synset': 'daisy_fleabane.n.01', 'name': 'daisy_fleabane'}, {'id': 18500, 'synset': 'orange_daisy.n.01', 'name': 'orange_daisy'}, {'id': 18501, 'synset': 'spreading_fleabane.n.01', 'name': 'spreading_fleabane'}, {'id': 18502, 'synset': 'seaside_daisy.n.01', 'name': 'seaside_daisy'}, {'id': 18503, 'synset': 'philadelphia_fleabane.n.01', 'name': 'Philadelphia_fleabane'}, {'id': 18504, 'synset': "robin's_plantain.n.01", 'name': "robin's_plantain"}, {'id': 18505, 'synset': 'showy_daisy.n.01', 'name': 'showy_daisy'}, {'id': 18506, 'synset': 'woolly_sunflower.n.01', 'name': 'woolly_sunflower'}, {'id': 18507, 'synset': 'golden_yarrow.n.01', 'name': 'golden_yarrow'}, {'id': 18508, 'synset': 'dog_fennel.n.01', 'name': 'dog_fennel'}, {'id': 18509, 'synset': 'joe-pye_weed.n.02', 'name': 'Joe-Pye_weed'}, {'id': 18510, 'synset': 'boneset.n.02', 'name': 'boneset'}, {'id': 18511, 'synset': 'joe-pye_weed.n.01', 'name': 'Joe-Pye_weed'}, {'id': 18512, 'synset': 'blue_daisy.n.01', 'name': 'blue_daisy'}, {'id': 18513, 'synset': 'kingfisher_daisy.n.01', 'name': 'kingfisher_daisy'}, {'id': 18514, 'synset': 'cotton_rose.n.02', 'name': 'cotton_rose'}, {'id': 18515, 'synset': 'herba_impia.n.01', 'name': 'herba_impia'}, {'id': 18516, 'synset': 'gaillardia.n.01', 'name': 'gaillardia'}, {'id': 18517, 'synset': 'gazania.n.01', 'name': 'gazania'}, {'id': 18518, 'synset': 'treasure_flower.n.01', 'name': 'treasure_flower'}, {'id': 18519, 'synset': 'african_daisy.n.02', 'name': 'African_daisy'}, {'id': 18520, 'synset': 'barberton_daisy.n.01', 'name': 'Barberton_daisy'}, {'id': 18521, 'synset': 'desert_sunflower.n.01', 'name': 'desert_sunflower'}, {'id': 18522, 'synset': 'cudweed.n.01', 'name': 'cudweed'}, {'id': 18523, 'synset': 'chafeweed.n.01', 'name': 'chafeweed'}, {'id': 18524, 'synset': 'gumweed.n.01', 'name': 'gumweed'}, {'id': 18525, 'synset': 'grindelia_robusta.n.01', 'name': 'Grindelia_robusta'}, {'id': 18526, 'synset': 'curlycup_gumweed.n.01', 'name': 'curlycup_gumweed'}, {'id': 18527, 'synset': 'little-head_snakeweed.n.01', 'name': 'little-head_snakeweed'}, {'id': 18528, 'synset': 'rabbitweed.n.01', 'name': 'rabbitweed'}, {'id': 18529, 'synset': 'broomweed.n.01', 'name': 'broomweed'}, {'id': 18530, 'synset': 'velvet_plant.n.02', 'name': 'velvet_plant'}, {'id': 18531, 'synset': 'goldenbush.n.01', 'name': 'goldenbush'}, {'id': 18532, 'synset': 'camphor_daisy.n.01', 'name': 'camphor_daisy'}, {'id': 18533, 'synset': 'yellow_spiny_daisy.n.01', 'name': 'yellow_spiny_daisy'}, {'id': 18534, 'synset': 'hoary_golden_bush.n.01', 'name': 'hoary_golden_bush'}, {'id': 18535, 'synset': 'sneezeweed.n.01', 'name': 'sneezeweed'}, {'id': 18536, 'synset': 'orange_sneezeweed.n.01', 'name': 'orange_sneezeweed'}, {'id': 18537, 'synset': 'rosilla.n.01', 'name': 'rosilla'}, {'id': 18538, 'synset': 'swamp_sunflower.n.01', 'name': 'swamp_sunflower'}, {'id': 18539, 'synset': 'common_sunflower.n.01', 'name': 'common_sunflower'}, {'id': 18540, 'synset': 'giant_sunflower.n.01', 'name': 'giant_sunflower'}, {'id': 18541, 'synset': 'showy_sunflower.n.01', 'name': 'showy_sunflower'}, {'id': 18542, 'synset': "maximilian's_sunflower.n.01", 'name': "Maximilian's_sunflower"}, {'id': 18543, 'synset': 'prairie_sunflower.n.01', 'name': 'prairie_sunflower'}, {'id': 18544, 'synset': 'jerusalem_artichoke.n.02', 'name': 'Jerusalem_artichoke'}, {'id': 18545, 'synset': 'jerusalem_artichoke.n.01', 'name': 'Jerusalem_artichoke'}, {'id': 18546, 'synset': 'strawflower.n.03', 'name': 'strawflower'}, {'id': 18547, 'synset': 'heliopsis.n.01', 'name': 'heliopsis'}, {'id': 18548, 'synset': 'strawflower.n.02', 'name': 'strawflower'}, {'id': 18549, 'synset': 'hairy_golden_aster.n.01', 'name': 'hairy_golden_aster'}, {'id': 18550, 'synset': 'hawkweed.n.02', 'name': 'hawkweed'}, {'id': 18551, 'synset': 'rattlesnake_weed.n.01', 'name': 'rattlesnake_weed'}, {'id': 18552, 'synset': 'alpine_coltsfoot.n.01', 'name': 'alpine_coltsfoot'}, {'id': 18553, 'synset': 'alpine_gold.n.01', 'name': 'alpine_gold'}, {'id': 18554, 'synset': 'dwarf_hulsea.n.01', 'name': 'dwarf_hulsea'}, {'id': 18555, 'synset': "cat's-ear.n.02", 'name': "cat's-ear"}, {'id': 18556, 'synset': 'inula.n.01', 'name': 'inula'}, {'id': 18557, 'synset': 'marsh_elder.n.01', 'name': 'marsh_elder'}, {'id': 18558, 'synset': 'burweed_marsh_elder.n.01', 'name': 'burweed_marsh_elder'}, {'id': 18559, 'synset': 'krigia.n.01', 'name': 'krigia'}, {'id': 18560, 'synset': 'dwarf_dandelion.n.01', 'name': 'dwarf_dandelion'}, {'id': 18561, 'synset': 'garden_lettuce.n.01', 'name': 'garden_lettuce'}, {'id': 18562, 'synset': 'cos_lettuce.n.01', 'name': 'cos_lettuce'}, {'id': 18563, 'synset': 'leaf_lettuce.n.01', 'name': 'leaf_lettuce'}, {'id': 18564, 'synset': 'celtuce.n.01', 'name': 'celtuce'}, {'id': 18565, 'synset': 'prickly_lettuce.n.01', 'name': 'prickly_lettuce'}, {'id': 18566, 'synset': 'goldfields.n.01', 'name': 'goldfields'}, {'id': 18567, 'synset': 'tidytips.n.01', 'name': 'tidytips'}, {'id': 18568, 'synset': 'hawkbit.n.01', 'name': 'hawkbit'}, {'id': 18569, 'synset': 'fall_dandelion.n.01', 'name': 'fall_dandelion'}, {'id': 18570, 'synset': 'edelweiss.n.01', 'name': 'edelweiss'}, {'id': 18571, 'synset': 'oxeye_daisy.n.02', 'name': 'oxeye_daisy'}, {'id': 18572, 'synset': 'oxeye_daisy.n.01', 'name': 'oxeye_daisy'}, {'id': 18573, 'synset': 'shasta_daisy.n.01', 'name': 'shasta_daisy'}, {'id': 18574, 'synset': 'pyrenees_daisy.n.01', 'name': 'Pyrenees_daisy'}, {'id': 18575, 'synset': 'north_island_edelweiss.n.01', 'name': 'north_island_edelweiss'}, {'id': 18576, 'synset': 'blazing_star.n.02', 'name': 'blazing_star'}, {'id': 18577, 'synset': 'dotted_gayfeather.n.01', 'name': 'dotted_gayfeather'}, {'id': 18578, 'synset': 'dense_blazing_star.n.01', 'name': 'dense_blazing_star'}, {'id': 18579, 'synset': 'texas_star.n.02', 'name': 'Texas_star'}, {'id': 18580, 'synset': 'african_daisy.n.01', 'name': 'African_daisy'}, {'id': 18581, 'synset': 'tahoka_daisy.n.01', 'name': 'tahoka_daisy'}, {'id': 18582, 'synset': 'sticky_aster.n.01', 'name': 'sticky_aster'}, {'id': 18583, 'synset': 'mojave_aster.n.01', 'name': 'Mojave_aster'}, {'id': 18584, 'synset': 'tarweed.n.01', 'name': 'tarweed'}, {'id': 18585, 'synset': 'sweet_false_chamomile.n.01', 'name': 'sweet_false_chamomile'}, {'id': 18586, 'synset': 'pineapple_weed.n.01', 'name': 'pineapple_weed'}, {'id': 18587, 'synset': 'climbing_hempweed.n.01', 'name': 'climbing_hempweed'}, {'id': 18588, 'synset': 'mutisia.n.01', 'name': 'mutisia'}, {'id': 18589, 'synset': 'rattlesnake_root.n.02', 'name': 'rattlesnake_root'}, {'id': 18590, 'synset': 'white_lettuce.n.01', 'name': 'white_lettuce'}, {'id': 18591, 'synset': 'daisybush.n.01', 'name': 'daisybush'}, {'id': 18592, 'synset': 'new_zealand_daisybush.n.01', 'name': 'New_Zealand_daisybush'}, {'id': 18593, 'synset': 'cotton_thistle.n.01', 'name': 'cotton_thistle'}, {'id': 18594, 'synset': 'othonna.n.01', 'name': 'othonna'}, {'id': 18595, 'synset': 'cascade_everlasting.n.01', 'name': 'cascade_everlasting'}, {'id': 18596, 'synset': 'butterweed.n.02', 'name': 'butterweed'}, {'id': 18597, 'synset': 'american_feverfew.n.01', 'name': 'American_feverfew'}, {'id': 18598, 'synset': 'cineraria.n.01', 'name': 'cineraria'}, {'id': 18599, 'synset': "florest's_cineraria.n.01", 'name': "florest's_cineraria"}, {'id': 18600, 'synset': 'butterbur.n.01', 'name': 'butterbur'}, {'id': 18601, 'synset': 'winter_heliotrope.n.01', 'name': 'winter_heliotrope'}, {'id': 18602, 'synset': 'sweet_coltsfoot.n.01', 'name': 'sweet_coltsfoot'}, {'id': 18603, 'synset': 'oxtongue.n.01', 'name': 'oxtongue'}, {'id': 18604, 'synset': 'hawkweed.n.01', 'name': 'hawkweed'}, {'id': 18605, 'synset': 'mouse-ear_hawkweed.n.01', 'name': 'mouse-ear_hawkweed'}, {'id': 18606, 'synset': 'stevia.n.02', 'name': 'stevia'}, {'id': 18607, 'synset': 'rattlesnake_root.n.01', 'name': 'rattlesnake_root'}, {'id': 18608, 'synset': 'fleabane.n.01', 'name': 'fleabane'}, {'id': 18609, 'synset': 'sheep_plant.n.01', 'name': 'sheep_plant'}, {'id': 18610, 'synset': 'coneflower.n.02', 'name': 'coneflower'}, {'id': 18611, 'synset': 'mexican_hat.n.01', 'name': 'Mexican_hat'}, {'id': 18612, 'synset': 'long-head_coneflower.n.01', 'name': 'long-head_coneflower'}, {'id': 18613, 'synset': 'prairie_coneflower.n.01', 'name': 'prairie_coneflower'}, {'id': 18614, 'synset': 'swan_river_everlasting.n.01', 'name': 'Swan_River_everlasting'}, {'id': 18615, 'synset': 'coneflower.n.01', 'name': 'coneflower'}, {'id': 18616, 'synset': 'black-eyed_susan.n.03', 'name': 'black-eyed_Susan'}, {'id': 18617, 'synset': 'cutleaved_coneflower.n.01', 'name': 'cutleaved_coneflower'}, {'id': 18618, 'synset': 'golden_glow.n.01', 'name': 'golden_glow'}, {'id': 18619, 'synset': 'lavender_cotton.n.01', 'name': 'lavender_cotton'}, {'id': 18620, 'synset': 'creeping_zinnia.n.01', 'name': 'creeping_zinnia'}, {'id': 18621, 'synset': 'golden_thistle.n.01', 'name': 'golden_thistle'}, {'id': 18622, 'synset': 'spanish_oyster_plant.n.01', 'name': 'Spanish_oyster_plant'}, {'id': 18623, 'synset': 'nodding_groundsel.n.01', 'name': 'nodding_groundsel'}, {'id': 18624, 'synset': 'dusty_miller.n.02', 'name': 'dusty_miller'}, {'id': 18625, 'synset': 'butterweed.n.01', 'name': 'butterweed'}, {'id': 18626, 'synset': 'ragwort.n.01', 'name': 'ragwort'}, {'id': 18627, 'synset': 'arrowleaf_groundsel.n.01', 'name': 'arrowleaf_groundsel'}, {'id': 18628, 'synset': 'black_salsify.n.01', 'name': 'black_salsify'}, {'id': 18629, 'synset': 'white-topped_aster.n.01', 'name': 'white-topped_aster'}, {'id': 18630, 'synset': 'narrow-leaved_white-topped_aster.n.01', 'name': 'narrow-leaved_white-topped_aster'}, {'id': 18631, 'synset': 'silver_sage.n.01', 'name': 'silver_sage'}, {'id': 18632, 'synset': 'sea_wormwood.n.01', 'name': 'sea_wormwood'}, {'id': 18633, 'synset': 'sawwort.n.01', 'name': 'sawwort'}, {'id': 18634, 'synset': 'rosinweed.n.01', 'name': 'rosinweed'}, {'id': 18635, 'synset': 'milk_thistle.n.02', 'name': 'milk_thistle'}, {'id': 18636, 'synset': 'goldenrod.n.01', 'name': 'goldenrod'}, {'id': 18637, 'synset': 'silverrod.n.01', 'name': 'silverrod'}, {'id': 18638, 'synset': 'meadow_goldenrod.n.01', 'name': 'meadow_goldenrod'}, {'id': 18639, 'synset': 'missouri_goldenrod.n.01', 'name': 'Missouri_goldenrod'}, {'id': 18640, 'synset': 'alpine_goldenrod.n.01', 'name': 'alpine_goldenrod'}, {'id': 18641, 'synset': 'grey_goldenrod.n.01', 'name': 'grey_goldenrod'}, {'id': 18642, 'synset': 'blue_mountain_tea.n.01', 'name': 'Blue_Mountain_tea'}, {'id': 18643, 'synset': "dyer's_weed.n.01", 'name': "dyer's_weed"}, {'id': 18644, 'synset': 'seaside_goldenrod.n.01', 'name': 'seaside_goldenrod'}, {'id': 18645, 'synset': 'narrow_goldenrod.n.01', 'name': 'narrow_goldenrod'}, {'id': 18646, 'synset': "boott's_goldenrod.n.01", 'name': "Boott's_goldenrod"}, {'id': 18647, 'synset': "elliott's_goldenrod.n.01", 'name': "Elliott's_goldenrod"}, {'id': 18648, 'synset': 'ohio_goldenrod.n.01', 'name': 'Ohio_goldenrod'}, {'id': 18649, 'synset': 'rough-stemmed_goldenrod.n.01', 'name': 'rough-stemmed_goldenrod'}, {'id': 18650, 'synset': 'showy_goldenrod.n.01', 'name': 'showy_goldenrod'}, {'id': 18651, 'synset': 'tall_goldenrod.n.01', 'name': 'tall_goldenrod'}, {'id': 18652, 'synset': 'zigzag_goldenrod.n.01', 'name': 'zigzag_goldenrod'}, {'id': 18653, 'synset': 'sow_thistle.n.01', 'name': 'sow_thistle'}, {'id': 18654, 'synset': 'milkweed.n.02', 'name': 'milkweed'}, {'id': 18655, 'synset': 'stevia.n.01', 'name': 'stevia'}, {'id': 18656, 'synset': "stokes'_aster.n.01", 'name': "stokes'_aster"}, {'id': 18657, 'synset': 'marigold.n.01', 'name': 'marigold'}, {'id': 18658, 'synset': 'african_marigold.n.01', 'name': 'African_marigold'}, {'id': 18659, 'synset': 'french_marigold.n.01', 'name': 'French_marigold'}, {'id': 18660, 'synset': 'painted_daisy.n.01', 'name': 'painted_daisy'}, {'id': 18661, 'synset': 'pyrethrum.n.02', 'name': 'pyrethrum'}, {'id': 18662, 'synset': 'northern_dune_tansy.n.01', 'name': 'northern_dune_tansy'}, {'id': 18663, 'synset': 'feverfew.n.01', 'name': 'feverfew'}, {'id': 18664, 'synset': 'dusty_miller.n.01', 'name': 'dusty_miller'}, {'id': 18665, 'synset': 'tansy.n.01', 'name': 'tansy'}, {'id': 18666, 'synset': 'dandelion.n.01', 'name': 'dandelion'}, {'id': 18667, 'synset': 'common_dandelion.n.01', 'name': 'common_dandelion'}, {'id': 18668, 'synset': 'dandelion_green.n.01', 'name': 'dandelion_green'}, {'id': 18669, 'synset': 'russian_dandelion.n.01', 'name': 'Russian_dandelion'}, {'id': 18670, 'synset': 'stemless_hymenoxys.n.01', 'name': 'stemless_hymenoxys'}, {'id': 18671, 'synset': 'mexican_sunflower.n.01', 'name': 'Mexican_sunflower'}, {'id': 18672, 'synset': 'easter_daisy.n.01', 'name': 'Easter_daisy'}, {'id': 18673, 'synset': 'yellow_salsify.n.01', 'name': 'yellow_salsify'}, {'id': 18674, 'synset': 'salsify.n.02', 'name': 'salsify'}, {'id': 18675, 'synset': 'meadow_salsify.n.01', 'name': 'meadow_salsify'}, {'id': 18676, 'synset': 'scentless_camomile.n.01', 'name': 'scentless_camomile'}, {'id': 18677, 'synset': 'turfing_daisy.n.01', 'name': 'turfing_daisy'}, {'id': 18678, 'synset': 'coltsfoot.n.02', 'name': 'coltsfoot'}, {'id': 18679, 'synset': 'ursinia.n.01', 'name': 'ursinia'}, {'id': 18680, 'synset': 'crownbeard.n.01', 'name': 'crownbeard'}, {'id': 18681, 'synset': 'wingstem.n.01', 'name': 'wingstem'}, {'id': 18682, 'synset': 'cowpen_daisy.n.01', 'name': 'cowpen_daisy'}, {'id': 18683, 'synset': 'gravelweed.n.01', 'name': 'gravelweed'}, {'id': 18684, 'synset': 'virginia_crownbeard.n.01', 'name': 'Virginia_crownbeard'}, {'id': 18685, 'synset': 'ironweed.n.01', 'name': 'ironweed'}, {'id': 18686, 'synset': "mule's_ears.n.01", 'name': "mule's_ears"}, {'id': 18687, 'synset': "white-rayed_mule's_ears.n.01", 'name': "white-rayed_mule's_ears"}, {'id': 18688, 'synset': 'cocklebur.n.01', 'name': 'cocklebur'}, {'id': 18689, 'synset': 'xeranthemum.n.01', 'name': 'xeranthemum'}, {'id': 18690, 'synset': 'immortelle.n.01', 'name': 'immortelle'}, {'id': 18691, 'synset': 'zinnia.n.01', 'name': 'zinnia'}, {'id': 18692, 'synset': 'white_zinnia.n.01', 'name': 'white_zinnia'}, {'id': 18693, 'synset': 'little_golden_zinnia.n.01', 'name': 'little_golden_zinnia'}, {'id': 18694, 'synset': 'blazing_star.n.01', 'name': 'blazing_star'}, {'id': 18695, 'synset': 'bartonia.n.01', 'name': 'bartonia'}, {'id': 18696, 'synset': 'achene.n.01', 'name': 'achene'}, {'id': 18697, 'synset': 'samara.n.01', 'name': 'samara'}, {'id': 18698, 'synset': 'campanula.n.01', 'name': 'campanula'}, {'id': 18699, 'synset': 'creeping_bellflower.n.01', 'name': 'creeping_bellflower'}, {'id': 18700, 'synset': 'canterbury_bell.n.02', 'name': 'Canterbury_bell'}, {'id': 18701, 'synset': 'tall_bellflower.n.01', 'name': 'tall_bellflower'}, {'id': 18702, 'synset': 'marsh_bellflower.n.01', 'name': 'marsh_bellflower'}, {'id': 18703, 'synset': 'clustered_bellflower.n.01', 'name': 'clustered_bellflower'}, {'id': 18704, 'synset': 'peach_bells.n.01', 'name': 'peach_bells'}, {'id': 18705, 'synset': 'chimney_plant.n.01', 'name': 'chimney_plant'}, {'id': 18706, 'synset': 'rampion.n.01', 'name': 'rampion'}, {'id': 18707, 'synset': 'tussock_bellflower.n.01', 'name': 'tussock_bellflower'}, {'id': 18708, 'synset': 'orchid.n.01', 'name': 'orchid'}, {'id': 18709, 'synset': 'orchis.n.01', 'name': 'orchis'}, {'id': 18710, 'synset': 'male_orchis.n.01', 'name': 'male_orchis'}, {'id': 18711, 'synset': 'butterfly_orchid.n.05', 'name': 'butterfly_orchid'}, {'id': 18712, 'synset': 'showy_orchis.n.01', 'name': 'showy_orchis'}, {'id': 18713, 'synset': 'aerides.n.01', 'name': 'aerides'}, {'id': 18714, 'synset': 'angrecum.n.01', 'name': 'angrecum'}, {'id': 18715, 'synset': 'jewel_orchid.n.01', 'name': 'jewel_orchid'}, {'id': 18716, 'synset': 'puttyroot.n.01', 'name': 'puttyroot'}, {'id': 18717, 'synset': 'arethusa.n.01', 'name': 'arethusa'}, {'id': 18718, 'synset': 'bog_rose.n.01', 'name': 'bog_rose'}, {'id': 18719, 'synset': 'bletia.n.01', 'name': 'bletia'}, {'id': 18720, 'synset': 'bletilla_striata.n.01', 'name': 'Bletilla_striata'}, {'id': 18721, 'synset': 'brassavola.n.01', 'name': 'brassavola'}, {'id': 18722, 'synset': 'spider_orchid.n.03', 'name': 'spider_orchid'}, {'id': 18723, 'synset': 'spider_orchid.n.02', 'name': 'spider_orchid'}, {'id': 18724, 'synset': 'caladenia.n.01', 'name': 'caladenia'}, {'id': 18725, 'synset': 'calanthe.n.01', 'name': 'calanthe'}, {'id': 18726, 'synset': 'grass_pink.n.01', 'name': 'grass_pink'}, {'id': 18727, 'synset': 'calypso.n.01', 'name': 'calypso'}, {'id': 18728, 'synset': 'cattleya.n.01', 'name': 'cattleya'}, {'id': 18729, 'synset': 'helleborine.n.03', 'name': 'helleborine'}, {'id': 18730, 'synset': 'red_helleborine.n.01', 'name': 'red_helleborine'}, {'id': 18731, 'synset': 'spreading_pogonia.n.01', 'name': 'spreading_pogonia'}, {'id': 18732, 'synset': 'rosebud_orchid.n.01', 'name': 'rosebud_orchid'}, {'id': 18733, 'synset': 'satyr_orchid.n.01', 'name': 'satyr_orchid'}, {'id': 18734, 'synset': 'frog_orchid.n.02', 'name': 'frog_orchid'}, {'id': 18735, 'synset': 'coelogyne.n.01', 'name': 'coelogyne'}, {'id': 18736, 'synset': 'coral_root.n.01', 'name': 'coral_root'}, {'id': 18737, 'synset': 'spotted_coral_root.n.01', 'name': 'spotted_coral_root'}, {'id': 18738, 'synset': 'striped_coral_root.n.01', 'name': 'striped_coral_root'}, {'id': 18739, 'synset': 'early_coral_root.n.01', 'name': 'early_coral_root'}, {'id': 18740, 'synset': 'swan_orchid.n.01', 'name': 'swan_orchid'}, {'id': 18741, 'synset': 'cymbid.n.01', 'name': 'cymbid'}, {'id': 18742, 'synset': 'cypripedia.n.01', 'name': 'cypripedia'}, {'id': 18743, 'synset': "lady's_slipper.n.01", 'name': "lady's_slipper"}, {'id': 18744, 'synset': 'moccasin_flower.n.01', 'name': 'moccasin_flower'}, {'id': 18745, 'synset': "common_lady's-slipper.n.01", 'name': "common_lady's-slipper"}, {'id': 18746, 'synset': "ram's-head.n.01", 'name': "ram's-head"}, {'id': 18747, 'synset': "yellow_lady's_slipper.n.01", 'name': "yellow_lady's_slipper"}, {'id': 18748, 'synset': "large_yellow_lady's_slipper.n.01", 'name': "large_yellow_lady's_slipper"}, {'id': 18749, 'synset': "california_lady's_slipper.n.01", 'name': "California_lady's_slipper"}, {'id': 18750, 'synset': "clustered_lady's_slipper.n.01", 'name': "clustered_lady's_slipper"}, {'id': 18751, 'synset': "mountain_lady's_slipper.n.01", 'name': "mountain_lady's_slipper"}, {'id': 18752, 'synset': 'marsh_orchid.n.01', 'name': 'marsh_orchid'}, {'id': 18753, 'synset': 'common_spotted_orchid.n.01', 'name': 'common_spotted_orchid'}, {'id': 18754, 'synset': 'dendrobium.n.01', 'name': 'dendrobium'}, {'id': 18755, 'synset': 'disa.n.01', 'name': 'disa'}, {'id': 18756, 'synset': 'phantom_orchid.n.01', 'name': 'phantom_orchid'}, {'id': 18757, 'synset': 'tulip_orchid.n.01', 'name': 'tulip_orchid'}, {'id': 18758, 'synset': 'butterfly_orchid.n.04', 'name': 'butterfly_orchid'}, {'id': 18759, 'synset': 'butterfly_orchid.n.03', 'name': 'butterfly_orchid'}, {'id': 18760, 'synset': 'epidendron.n.01', 'name': 'epidendron'}, {'id': 18761, 'synset': 'helleborine.n.02', 'name': 'helleborine'}, {'id': 18762, 'synset': 'epipactis_helleborine.n.01', 'name': 'Epipactis_helleborine'}, {'id': 18763, 'synset': 'stream_orchid.n.01', 'name': 'stream_orchid'}, {'id': 18764, 'synset': 'tongueflower.n.01', 'name': 'tongueflower'}, {'id': 18765, 'synset': 'rattlesnake_plantain.n.01', 'name': 'rattlesnake_plantain'}, {'id': 18766, 'synset': 'fragrant_orchid.n.01', 'name': 'fragrant_orchid'}, {'id': 18767, 'synset': 'short-spurred_fragrant_orchid.n.01', 'name': 'short-spurred_fragrant_orchid'}, {'id': 18768, 'synset': 'fringed_orchis.n.01', 'name': 'fringed_orchis'}, {'id': 18769, 'synset': 'frog_orchid.n.01', 'name': 'frog_orchid'}, {'id': 18770, 'synset': 'rein_orchid.n.01', 'name': 'rein_orchid'}, {'id': 18771, 'synset': 'bog_rein_orchid.n.01', 'name': 'bog_rein_orchid'}, {'id': 18772, 'synset': 'white_fringed_orchis.n.01', 'name': 'white_fringed_orchis'}, {'id': 18773, 'synset': 'elegant_habenaria.n.01', 'name': 'elegant_Habenaria'}, {'id': 18774, 'synset': 'purple-fringed_orchid.n.02', 'name': 'purple-fringed_orchid'}, {'id': 18775, 'synset': 'coastal_rein_orchid.n.01', 'name': 'coastal_rein_orchid'}, {'id': 18776, 'synset': "hooker's_orchid.n.01", 'name': "Hooker's_orchid"}, {'id': 18777, 'synset': 'ragged_orchid.n.01', 'name': 'ragged_orchid'}, {'id': 18778, 'synset': 'prairie_orchid.n.01', 'name': 'prairie_orchid'}, {'id': 18779, 'synset': 'snowy_orchid.n.01', 'name': 'snowy_orchid'}, {'id': 18780, 'synset': 'round-leaved_rein_orchid.n.01', 'name': 'round-leaved_rein_orchid'}, {'id': 18781, 'synset': 'purple_fringeless_orchid.n.01', 'name': 'purple_fringeless_orchid'}, {'id': 18782, 'synset': 'purple-fringed_orchid.n.01', 'name': 'purple-fringed_orchid'}, {'id': 18783, 'synset': 'alaska_rein_orchid.n.01', 'name': 'Alaska_rein_orchid'}, {'id': 18784, 'synset': 'crested_coral_root.n.01', 'name': 'crested_coral_root'}, {'id': 18785, 'synset': 'texas_purple_spike.n.01', 'name': 'Texas_purple_spike'}, {'id': 18786, 'synset': 'lizard_orchid.n.01', 'name': 'lizard_orchid'}, {'id': 18787, 'synset': 'laelia.n.01', 'name': 'laelia'}, {'id': 18788, 'synset': 'liparis.n.01', 'name': 'liparis'}, {'id': 18789, 'synset': 'twayblade.n.02', 'name': 'twayblade'}, {'id': 18790, 'synset': 'fen_orchid.n.01', 'name': 'fen_orchid'}, {'id': 18791, 'synset': 'broad-leaved_twayblade.n.01', 'name': 'broad-leaved_twayblade'}, {'id': 18792, 'synset': 'lesser_twayblade.n.01', 'name': 'lesser_twayblade'}, {'id': 18793, 'synset': 'twayblade.n.01', 'name': 'twayblade'}, {'id': 18794, 'synset': "green_adder's_mouth.n.01", 'name': "green_adder's_mouth"}, {'id': 18795, 'synset': 'masdevallia.n.01', 'name': 'masdevallia'}, {'id': 18796, 'synset': 'maxillaria.n.01', 'name': 'maxillaria'}, {'id': 18797, 'synset': 'pansy_orchid.n.01', 'name': 'pansy_orchid'}, {'id': 18798, 'synset': 'odontoglossum.n.01', 'name': 'odontoglossum'}, {'id': 18799, 'synset': 'oncidium.n.01', 'name': 'oncidium'}, {'id': 18800, 'synset': 'bee_orchid.n.01', 'name': 'bee_orchid'}, {'id': 18801, 'synset': 'fly_orchid.n.02', 'name': 'fly_orchid'}, {'id': 18802, 'synset': 'spider_orchid.n.01', 'name': 'spider_orchid'}, {'id': 18803, 'synset': 'early_spider_orchid.n.01', 'name': 'early_spider_orchid'}, {'id': 18804, 'synset': "venus'_slipper.n.01", 'name': "Venus'_slipper"}, {'id': 18805, 'synset': 'phaius.n.01', 'name': 'phaius'}, {'id': 18806, 'synset': 'moth_orchid.n.01', 'name': 'moth_orchid'}, {'id': 18807, 'synset': 'butterfly_plant.n.01', 'name': 'butterfly_plant'}, {'id': 18808, 'synset': 'rattlesnake_orchid.n.01', 'name': 'rattlesnake_orchid'}, {'id': 18809, 'synset': 'lesser_butterfly_orchid.n.01', 'name': 'lesser_butterfly_orchid'}, {'id': 18810, 'synset': 'greater_butterfly_orchid.n.01', 'name': 'greater_butterfly_orchid'}, {'id': 18811, 'synset': 'prairie_white-fringed_orchid.n.01', 'name': 'prairie_white-fringed_orchid'}, {'id': 18812, 'synset': 'tangle_orchid.n.01', 'name': 'tangle_orchid'}, {'id': 18813, 'synset': 'indian_crocus.n.01', 'name': 'Indian_crocus'}, {'id': 18814, 'synset': 'pleurothallis.n.01', 'name': 'pleurothallis'}, {'id': 18815, 'synset': 'pogonia.n.01', 'name': 'pogonia'}, {'id': 18816, 'synset': 'butterfly_orchid.n.01', 'name': 'butterfly_orchid'}, {'id': 18817, 'synset': 'psychopsis_krameriana.n.01', 'name': 'Psychopsis_krameriana'}, {'id': 18818, 'synset': 'psychopsis_papilio.n.01', 'name': 'Psychopsis_papilio'}, {'id': 18819, 'synset': 'helmet_orchid.n.01', 'name': 'helmet_orchid'}, {'id': 18820, 'synset': 'foxtail_orchid.n.01', 'name': 'foxtail_orchid'}, {'id': 18821, 'synset': 'orange-blossom_orchid.n.01', 'name': 'orange-blossom_orchid'}, {'id': 18822, 'synset': 'sobralia.n.01', 'name': 'sobralia'}, {'id': 18823, 'synset': "ladies'_tresses.n.01", 'name': "ladies'_tresses"}, {'id': 18824, 'synset': 'screw_augur.n.01', 'name': 'screw_augur'}, {'id': 18825, 'synset': "hooded_ladies'_tresses.n.01", 'name': "hooded_ladies'_tresses"}, {'id': 18826, 'synset': "western_ladies'_tresses.n.01", 'name': "western_ladies'_tresses"}, {'id': 18827, 'synset': "european_ladies'_tresses.n.01", 'name': "European_ladies'_tresses"}, {'id': 18828, 'synset': 'stanhopea.n.01', 'name': 'stanhopea'}, {'id': 18829, 'synset': 'stelis.n.01', 'name': 'stelis'}, {'id': 18830, 'synset': 'fly_orchid.n.01', 'name': 'fly_orchid'}, {'id': 18831, 'synset': 'vanda.n.01', 'name': 'vanda'}, {'id': 18832, 'synset': 'blue_orchid.n.01', 'name': 'blue_orchid'}, {'id': 18833, 'synset': 'vanilla.n.01', 'name': 'vanilla'}, {'id': 18834, 'synset': 'vanilla_orchid.n.01', 'name': 'vanilla_orchid'}, {'id': 18835, 'synset': 'yam.n.02', 'name': 'yam'}, {'id': 18836, 'synset': 'yam.n.01', 'name': 'yam'}, {'id': 18837, 'synset': 'white_yam.n.01', 'name': 'white_yam'}, {'id': 18838, 'synset': 'cinnamon_vine.n.01', 'name': 'cinnamon_vine'}, {'id': 18839, 'synset': "elephant's-foot.n.01", 'name': "elephant's-foot"}, {'id': 18840, 'synset': 'wild_yam.n.01', 'name': 'wild_yam'}, {'id': 18841, 'synset': 'cush-cush.n.01', 'name': 'cush-cush'}, {'id': 18842, 'synset': 'black_bryony.n.01', 'name': 'black_bryony'}, {'id': 18843, 'synset': 'primrose.n.01', 'name': 'primrose'}, {'id': 18844, 'synset': 'english_primrose.n.01', 'name': 'English_primrose'}, {'id': 18845, 'synset': 'cowslip.n.01', 'name': 'cowslip'}, {'id': 18846, 'synset': 'oxlip.n.01', 'name': 'oxlip'}, {'id': 18847, 'synset': 'chinese_primrose.n.01', 'name': 'Chinese_primrose'}, {'id': 18848, 'synset': 'polyanthus.n.01', 'name': 'polyanthus'}, {'id': 18849, 'synset': 'pimpernel.n.02', 'name': 'pimpernel'}, {'id': 18850, 'synset': 'scarlet_pimpernel.n.01', 'name': 'scarlet_pimpernel'}, {'id': 18851, 'synset': 'bog_pimpernel.n.01', 'name': 'bog_pimpernel'}, {'id': 18852, 'synset': 'chaffweed.n.01', 'name': 'chaffweed'}, {'id': 18853, 'synset': 'cyclamen.n.01', 'name': 'cyclamen'}, {'id': 18854, 'synset': 'sowbread.n.01', 'name': 'sowbread'}, {'id': 18855, 'synset': 'sea_milkwort.n.01', 'name': 'sea_milkwort'}, {'id': 18856, 'synset': 'featherfoil.n.01', 'name': 'featherfoil'}, {'id': 18857, 'synset': 'water_gillyflower.n.01', 'name': 'water_gillyflower'}, {'id': 18858, 'synset': 'water_violet.n.01', 'name': 'water_violet'}, {'id': 18859, 'synset': 'loosestrife.n.02', 'name': 'loosestrife'}, {'id': 18860, 'synset': 'gooseneck_loosestrife.n.01', 'name': 'gooseneck_loosestrife'}, {'id': 18861, 'synset': 'yellow_pimpernel.n.01', 'name': 'yellow_pimpernel'}, {'id': 18862, 'synset': 'fringed_loosestrife.n.01', 'name': 'fringed_loosestrife'}, {'id': 18863, 'synset': 'moneywort.n.01', 'name': 'moneywort'}, {'id': 18864, 'synset': 'swamp_candles.n.01', 'name': 'swamp_candles'}, {'id': 18865, 'synset': 'whorled_loosestrife.n.01', 'name': 'whorled_loosestrife'}, {'id': 18866, 'synset': 'water_pimpernel.n.01', 'name': 'water_pimpernel'}, {'id': 18867, 'synset': 'brookweed.n.02', 'name': 'brookweed'}, {'id': 18868, 'synset': 'brookweed.n.01', 'name': 'brookweed'}, {'id': 18869, 'synset': 'coralberry.n.02', 'name': 'coralberry'}, {'id': 18870, 'synset': 'marlberry.n.01', 'name': 'marlberry'}, {'id': 18871, 'synset': 'plumbago.n.02', 'name': 'plumbago'}, {'id': 18872, 'synset': 'leadwort.n.01', 'name': 'leadwort'}, {'id': 18873, 'synset': 'thrift.n.01', 'name': 'thrift'}, {'id': 18874, 'synset': 'sea_lavender.n.01', 'name': 'sea_lavender'}, {'id': 18875, 'synset': 'barbasco.n.01', 'name': 'barbasco'}, {'id': 18876, 'synset': 'gramineous_plant.n.01', 'name': 'gramineous_plant'}, {'id': 18877, 'synset': 'grass.n.01', 'name': 'grass'}, {'id': 18878, 'synset': 'midgrass.n.01', 'name': 'midgrass'}, {'id': 18879, 'synset': 'shortgrass.n.01', 'name': 'shortgrass'}, {'id': 18880, 'synset': 'sword_grass.n.01', 'name': 'sword_grass'}, {'id': 18881, 'synset': 'tallgrass.n.01', 'name': 'tallgrass'}, {'id': 18882, 'synset': 'herbage.n.01', 'name': 'herbage'}, {'id': 18883, 'synset': 'goat_grass.n.01', 'name': 'goat_grass'}, {'id': 18884, 'synset': 'wheatgrass.n.01', 'name': 'wheatgrass'}, {'id': 18885, 'synset': 'crested_wheatgrass.n.01', 'name': 'crested_wheatgrass'}, {'id': 18886, 'synset': 'bearded_wheatgrass.n.01', 'name': 'bearded_wheatgrass'}, {'id': 18887, 'synset': 'western_wheatgrass.n.01', 'name': 'western_wheatgrass'}, {'id': 18888, 'synset': 'intermediate_wheatgrass.n.01', 'name': 'intermediate_wheatgrass'}, {'id': 18889, 'synset': 'slender_wheatgrass.n.01', 'name': 'slender_wheatgrass'}, {'id': 18890, 'synset': 'velvet_bent.n.01', 'name': 'velvet_bent'}, {'id': 18891, 'synset': 'cloud_grass.n.01', 'name': 'cloud_grass'}, {'id': 18892, 'synset': 'meadow_foxtail.n.01', 'name': 'meadow_foxtail'}, {'id': 18893, 'synset': 'foxtail.n.01', 'name': 'foxtail'}, {'id': 18894, 'synset': 'broom_grass.n.01', 'name': 'broom_grass'}, {'id': 18895, 'synset': 'broom_sedge.n.01', 'name': 'broom_sedge'}, {'id': 18896, 'synset': 'tall_oat_grass.n.01', 'name': 'tall_oat_grass'}, {'id': 18897, 'synset': 'toetoe.n.02', 'name': 'toetoe'}, {'id': 18898, 'synset': 'oat.n.01', 'name': 'oat'}, {'id': 18899, 'synset': 'cereal_oat.n.01', 'name': 'cereal_oat'}, {'id': 18900, 'synset': 'wild_oat.n.01', 'name': 'wild_oat'}, {'id': 18901, 'synset': 'slender_wild_oat.n.01', 'name': 'slender_wild_oat'}, {'id': 18902, 'synset': 'wild_red_oat.n.01', 'name': 'wild_red_oat'}, {'id': 18903, 'synset': 'brome.n.01', 'name': 'brome'}, {'id': 18904, 'synset': 'chess.n.01', 'name': 'chess'}, {'id': 18905, 'synset': 'field_brome.n.01', 'name': 'field_brome'}, {'id': 18906, 'synset': 'grama.n.01', 'name': 'grama'}, {'id': 18907, 'synset': 'black_grama.n.01', 'name': 'black_grama'}, {'id': 18908, 'synset': 'buffalo_grass.n.02', 'name': 'buffalo_grass'}, {'id': 18909, 'synset': 'reed_grass.n.01', 'name': 'reed_grass'}, {'id': 18910, 'synset': 'feather_reed_grass.n.01', 'name': 'feather_reed_grass'}, {'id': 18911, 'synset': 'australian_reed_grass.n.01', 'name': 'Australian_reed_grass'}, {'id': 18912, 'synset': 'burgrass.n.01', 'name': 'burgrass'}, {'id': 18913, 'synset': 'buffel_grass.n.01', 'name': 'buffel_grass'}, {'id': 18914, 'synset': 'rhodes_grass.n.01', 'name': 'Rhodes_grass'}, {'id': 18915, 'synset': 'pampas_grass.n.01', 'name': 'pampas_grass'}, {'id': 18916, 'synset': 'giant_star_grass.n.01', 'name': 'giant_star_grass'}, {'id': 18917, 'synset': 'orchard_grass.n.01', 'name': 'orchard_grass'}, {'id': 18918, 'synset': 'egyptian_grass.n.01', 'name': 'Egyptian_grass'}, {'id': 18919, 'synset': 'crabgrass.n.01', 'name': 'crabgrass'}, {'id': 18920, 'synset': 'smooth_crabgrass.n.01', 'name': 'smooth_crabgrass'}, {'id': 18921, 'synset': 'large_crabgrass.n.01', 'name': 'large_crabgrass'}, {'id': 18922, 'synset': 'barnyard_grass.n.01', 'name': 'barnyard_grass'}, {'id': 18923, 'synset': 'japanese_millet.n.01', 'name': 'Japanese_millet'}, {'id': 18924, 'synset': 'yardgrass.n.01', 'name': 'yardgrass'}, {'id': 18925, 'synset': 'finger_millet.n.01', 'name': 'finger_millet'}, {'id': 18926, 'synset': 'lyme_grass.n.01', 'name': 'lyme_grass'}, {'id': 18927, 'synset': 'wild_rye.n.01', 'name': 'wild_rye'}, {'id': 18928, 'synset': 'giant_ryegrass.n.01', 'name': 'giant_ryegrass'}, {'id': 18929, 'synset': 'sea_lyme_grass.n.01', 'name': 'sea_lyme_grass'}, {'id': 18930, 'synset': 'canada_wild_rye.n.01', 'name': 'Canada_wild_rye'}, {'id': 18931, 'synset': 'teff.n.01', 'name': 'teff'}, {'id': 18932, 'synset': 'weeping_love_grass.n.01', 'name': 'weeping_love_grass'}, {'id': 18933, 'synset': 'plume_grass.n.01', 'name': 'plume_grass'}, {'id': 18934, 'synset': 'ravenna_grass.n.01', 'name': 'Ravenna_grass'}, {'id': 18935, 'synset': 'fescue.n.01', 'name': 'fescue'}, {'id': 18936, 'synset': 'reed_meadow_grass.n.01', 'name': 'reed_meadow_grass'}, {'id': 18937, 'synset': 'velvet_grass.n.01', 'name': 'velvet_grass'}, {'id': 18938, 'synset': 'creeping_soft_grass.n.01', 'name': 'creeping_soft_grass'}, {'id': 18939, 'synset': 'barleycorn.n.01', 'name': 'barleycorn'}, {'id': 18940, 'synset': 'barley_grass.n.01', 'name': 'barley_grass'}, {'id': 18941, 'synset': 'little_barley.n.01', 'name': 'little_barley'}, {'id': 18942, 'synset': 'rye_grass.n.01', 'name': 'rye_grass'}, {'id': 18943, 'synset': 'perennial_ryegrass.n.01', 'name': 'perennial_ryegrass'}, {'id': 18944, 'synset': 'italian_ryegrass.n.01', 'name': 'Italian_ryegrass'}, {'id': 18945, 'synset': 'darnel.n.01', 'name': 'darnel'}, {'id': 18946, 'synset': 'nimblewill.n.01', 'name': 'nimblewill'}, {'id': 18947, 'synset': 'cultivated_rice.n.01', 'name': 'cultivated_rice'}, {'id': 18948, 'synset': 'ricegrass.n.01', 'name': 'ricegrass'}, {'id': 18949, 'synset': 'smilo.n.01', 'name': 'smilo'}, {'id': 18950, 'synset': 'switch_grass.n.01', 'name': 'switch_grass'}, {'id': 18951, 'synset': 'broomcorn_millet.n.01', 'name': 'broomcorn_millet'}, {'id': 18952, 'synset': 'goose_grass.n.03', 'name': 'goose_grass'}, {'id': 18953, 'synset': 'dallisgrass.n.01', 'name': 'dallisgrass'}, {'id': 18954, 'synset': 'bahia_grass.n.01', 'name': 'Bahia_grass'}, {'id': 18955, 'synset': 'knotgrass.n.01', 'name': 'knotgrass'}, {'id': 18956, 'synset': 'fountain_grass.n.01', 'name': 'fountain_grass'}, {'id': 18957, 'synset': 'reed_canary_grass.n.01', 'name': 'reed_canary_grass'}, {'id': 18958, 'synset': 'canary_grass.n.01', 'name': 'canary_grass'}, {'id': 18959, 'synset': 'timothy.n.01', 'name': 'timothy'}, {'id': 18960, 'synset': 'bluegrass.n.01', 'name': 'bluegrass'}, {'id': 18961, 'synset': 'meadowgrass.n.01', 'name': 'meadowgrass'}, {'id': 18962, 'synset': 'wood_meadowgrass.n.01', 'name': 'wood_meadowgrass'}, {'id': 18963, 'synset': 'noble_cane.n.01', 'name': 'noble_cane'}, {'id': 18964, 'synset': 'munj.n.01', 'name': 'munj'}, {'id': 18965, 'synset': 'broom_beard_grass.n.01', 'name': 'broom_beard_grass'}, {'id': 18966, 'synset': 'bluestem.n.01', 'name': 'bluestem'}, {'id': 18967, 'synset': 'rye.n.02', 'name': 'rye'}, {'id': 18968, 'synset': 'bristlegrass.n.01', 'name': 'bristlegrass'}, {'id': 18969, 'synset': 'giant_foxtail.n.01', 'name': 'giant_foxtail'}, {'id': 18970, 'synset': 'yellow_bristlegrass.n.01', 'name': 'yellow_bristlegrass'}, {'id': 18971, 'synset': 'green_bristlegrass.n.01', 'name': 'green_bristlegrass'}, {'id': 18972, 'synset': 'siberian_millet.n.01', 'name': 'Siberian_millet'}, {'id': 18973, 'synset': 'german_millet.n.01', 'name': 'German_millet'}, {'id': 18974, 'synset': 'millet.n.01', 'name': 'millet'}, {'id': 18975, 'synset': 'rattan.n.02', 'name': 'rattan'}, {'id': 18976, 'synset': 'malacca.n.01', 'name': 'malacca'}, {'id': 18977, 'synset': 'reed.n.01', 'name': 'reed'}, {'id': 18978, 'synset': 'sorghum.n.01', 'name': 'sorghum'}, {'id': 18979, 'synset': 'grain_sorghum.n.01', 'name': 'grain_sorghum'}, {'id': 18980, 'synset': 'durra.n.01', 'name': 'durra'}, {'id': 18981, 'synset': 'feterita.n.01', 'name': 'feterita'}, {'id': 18982, 'synset': 'hegari.n.01', 'name': 'hegari'}, {'id': 18983, 'synset': 'kaoliang.n.01', 'name': 'kaoliang'}, {'id': 18984, 'synset': 'milo.n.01', 'name': 'milo'}, {'id': 18985, 'synset': 'shallu.n.01', 'name': 'shallu'}, {'id': 18986, 'synset': 'broomcorn.n.01', 'name': 'broomcorn'}, {'id': 18987, 'synset': 'cordgrass.n.01', 'name': 'cordgrass'}, {'id': 18988, 'synset': 'salt_reed_grass.n.01', 'name': 'salt_reed_grass'}, {'id': 18989, 'synset': 'prairie_cordgrass.n.01', 'name': 'prairie_cordgrass'}, {'id': 18990, 'synset': 'smut_grass.n.01', 'name': 'smut_grass'}, {'id': 18991, 'synset': 'sand_dropseed.n.01', 'name': 'sand_dropseed'}, {'id': 18992, 'synset': 'rush_grass.n.01', 'name': 'rush_grass'}, {'id': 18993, 'synset': 'st._augustine_grass.n.01', 'name': 'St._Augustine_grass'}, {'id': 18994, 'synset': 'grain.n.08', 'name': 'grain'}, {'id': 18995, 'synset': 'cereal.n.01', 'name': 'cereal'}, {'id': 18996, 'synset': 'wheat.n.01', 'name': 'wheat'}, {'id': 18997, 'synset': 'wheat_berry.n.01', 'name': 'wheat_berry'}, {'id': 18998, 'synset': 'durum.n.01', 'name': 'durum'}, {'id': 18999, 'synset': 'spelt.n.01', 'name': 'spelt'}, {'id': 19000, 'synset': 'emmer.n.01', 'name': 'emmer'}, {'id': 19001, 'synset': 'wild_wheat.n.01', 'name': 'wild_wheat'}, {'id': 19002, 'synset': 'corn.n.01', 'name': 'corn'}, {'id': 19003, 'synset': 'mealie.n.01', 'name': 'mealie'}, {'id': 19004, 'synset': 'corn.n.02', 'name': 'corn'}, {'id': 19005, 'synset': 'dent_corn.n.01', 'name': 'dent_corn'}, {'id': 19006, 'synset': 'flint_corn.n.01', 'name': 'flint_corn'}, {'id': 19007, 'synset': 'popcorn.n.01', 'name': 'popcorn'}, {'id': 19008, 'synset': 'zoysia.n.01', 'name': 'zoysia'}, {'id': 19009, 'synset': 'manila_grass.n.01', 'name': 'Manila_grass'}, {'id': 19010, 'synset': 'korean_lawn_grass.n.01', 'name': 'Korean_lawn_grass'}, {'id': 19011, 'synset': 'common_bamboo.n.01', 'name': 'common_bamboo'}, {'id': 19012, 'synset': 'giant_bamboo.n.01', 'name': 'giant_bamboo'}, {'id': 19013, 'synset': 'umbrella_plant.n.03', 'name': 'umbrella_plant'}, {'id': 19014, 'synset': 'chufa.n.01', 'name': 'chufa'}, {'id': 19015, 'synset': 'galingale.n.01', 'name': 'galingale'}, {'id': 19016, 'synset': 'nutgrass.n.01', 'name': 'nutgrass'}, {'id': 19017, 'synset': 'sand_sedge.n.01', 'name': 'sand_sedge'}, {'id': 19018, 'synset': 'cypress_sedge.n.01', 'name': 'cypress_sedge'}, {'id': 19019, 'synset': 'cotton_grass.n.01', 'name': 'cotton_grass'}, {'id': 19020, 'synset': 'common_cotton_grass.n.01', 'name': 'common_cotton_grass'}, {'id': 19021, 'synset': 'hardstem_bulrush.n.01', 'name': 'hardstem_bulrush'}, {'id': 19022, 'synset': 'wool_grass.n.01', 'name': 'wool_grass'}, {'id': 19023, 'synset': 'spike_rush.n.01', 'name': 'spike_rush'}, {'id': 19024, 'synset': 'water_chestnut.n.02', 'name': 'water_chestnut'}, {'id': 19025, 'synset': 'needle_spike_rush.n.01', 'name': 'needle_spike_rush'}, {'id': 19026, 'synset': 'creeping_spike_rush.n.01', 'name': 'creeping_spike_rush'}, {'id': 19027, 'synset': 'pandanus.n.02', 'name': 'pandanus'}, {'id': 19028, 'synset': 'textile_screw_pine.n.01', 'name': 'textile_screw_pine'}, {'id': 19029, 'synset': 'cattail.n.01', 'name': 'cattail'}, {'id': 19030, 'synset': "cat's-tail.n.01", 'name': "cat's-tail"}, {'id': 19031, 'synset': 'bur_reed.n.01', 'name': 'bur_reed'}, {'id': 19032, 'synset': 'grain.n.07', 'name': 'grain'}, {'id': 19033, 'synset': 'kernel.n.02', 'name': 'kernel'}, {'id': 19034, 'synset': 'rye.n.01', 'name': 'rye'}, {'id': 19035, 'synset': 'gourd.n.03', 'name': 'gourd'}, {'id': 19036, 'synset': 'pumpkin.n.01', 'name': 'pumpkin'}, {'id': 19037, 'synset': 'squash.n.01', 'name': 'squash'}, {'id': 19038, 'synset': 'summer_squash.n.01', 'name': 'summer_squash'}, {'id': 19039, 'synset': 'yellow_squash.n.01', 'name': 'yellow_squash'}, {'id': 19040, 'synset': 'marrow.n.02', 'name': 'marrow'}, {'id': 19041, 'synset': 'zucchini.n.01', 'name': 'zucchini'}, {'id': 19042, 'synset': 'cocozelle.n.01', 'name': 'cocozelle'}, {'id': 19043, 'synset': 'cymling.n.01', 'name': 'cymling'}, {'id': 19044, 'synset': 'spaghetti_squash.n.01', 'name': 'spaghetti_squash'}, {'id': 19045, 'synset': 'winter_squash.n.01', 'name': 'winter_squash'}, {'id': 19046, 'synset': 'acorn_squash.n.01', 'name': 'acorn_squash'}, {'id': 19047, 'synset': 'hubbard_squash.n.01', 'name': 'hubbard_squash'}, {'id': 19048, 'synset': 'turban_squash.n.01', 'name': 'turban_squash'}, {'id': 19049, 'synset': 'buttercup_squash.n.01', 'name': 'buttercup_squash'}, {'id': 19050, 'synset': 'butternut_squash.n.01', 'name': 'butternut_squash'}, {'id': 19051, 'synset': 'winter_crookneck.n.01', 'name': 'winter_crookneck'}, {'id': 19052, 'synset': 'cushaw.n.01', 'name': 'cushaw'}, {'id': 19053, 'synset': 'prairie_gourd.n.02', 'name': 'prairie_gourd'}, {'id': 19054, 'synset': 'prairie_gourd.n.01', 'name': 'prairie_gourd'}, {'id': 19055, 'synset': 'bryony.n.01', 'name': 'bryony'}, {'id': 19056, 'synset': 'white_bryony.n.01', 'name': 'white_bryony'}, {'id': 19057, 'synset': 'sweet_melon.n.01', 'name': 'sweet_melon'}, {'id': 19058, 'synset': 'cantaloupe.n.01', 'name': 'cantaloupe'}, {'id': 19059, 'synset': 'winter_melon.n.01', 'name': 'winter_melon'}, {'id': 19060, 'synset': 'net_melon.n.01', 'name': 'net_melon'}, {'id': 19061, 'synset': 'cucumber.n.01', 'name': 'cucumber'}, {'id': 19062, 'synset': 'squirting_cucumber.n.01', 'name': 'squirting_cucumber'}, {'id': 19063, 'synset': 'bottle_gourd.n.01', 'name': 'bottle_gourd'}, {'id': 19064, 'synset': 'luffa.n.02', 'name': 'luffa'}, {'id': 19065, 'synset': 'loofah.n.02', 'name': 'loofah'}, {'id': 19066, 'synset': 'angled_loofah.n.01', 'name': 'angled_loofah'}, {'id': 19067, 'synset': 'loofa.n.01', 'name': 'loofa'}, {'id': 19068, 'synset': 'balsam_apple.n.01', 'name': 'balsam_apple'}, {'id': 19069, 'synset': 'balsam_pear.n.01', 'name': 'balsam_pear'}, {'id': 19070, 'synset': 'lobelia.n.01', 'name': 'lobelia'}, {'id': 19071, 'synset': 'water_lobelia.n.01', 'name': 'water_lobelia'}, {'id': 19072, 'synset': 'mallow.n.01', 'name': 'mallow'}, {'id': 19073, 'synset': 'musk_mallow.n.02', 'name': 'musk_mallow'}, {'id': 19074, 'synset': 'common_mallow.n.01', 'name': 'common_mallow'}, {'id': 19075, 'synset': 'okra.n.02', 'name': 'okra'}, {'id': 19076, 'synset': 'okra.n.01', 'name': 'okra'}, {'id': 19077, 'synset': 'abelmosk.n.01', 'name': 'abelmosk'}, {'id': 19078, 'synset': 'flowering_maple.n.01', 'name': 'flowering_maple'}, {'id': 19079, 'synset': 'velvetleaf.n.02', 'name': 'velvetleaf'}, {'id': 19080, 'synset': 'hollyhock.n.02', 'name': 'hollyhock'}, {'id': 19081, 'synset': 'rose_mallow.n.02', 'name': 'rose_mallow'}, {'id': 19082, 'synset': 'althea.n.01', 'name': 'althea'}, {'id': 19083, 'synset': 'marsh_mallow.n.01', 'name': 'marsh_mallow'}, {'id': 19084, 'synset': 'poppy_mallow.n.01', 'name': 'poppy_mallow'}, {'id': 19085, 'synset': 'fringed_poppy_mallow.n.01', 'name': 'fringed_poppy_mallow'}, {'id': 19086, 'synset': 'purple_poppy_mallow.n.01', 'name': 'purple_poppy_mallow'}, {'id': 19087, 'synset': 'clustered_poppy_mallow.n.01', 'name': 'clustered_poppy_mallow'}, {'id': 19088, 'synset': 'sea_island_cotton.n.01', 'name': 'sea_island_cotton'}, {'id': 19089, 'synset': 'levant_cotton.n.01', 'name': 'Levant_cotton'}, {'id': 19090, 'synset': 'upland_cotton.n.01', 'name': 'upland_cotton'}, {'id': 19091, 'synset': 'peruvian_cotton.n.01', 'name': 'Peruvian_cotton'}, {'id': 19092, 'synset': 'wild_cotton.n.01', 'name': 'wild_cotton'}, {'id': 19093, 'synset': 'kenaf.n.02', 'name': 'kenaf'}, {'id': 19094, 'synset': 'sorrel_tree.n.02', 'name': 'sorrel_tree'}, {'id': 19095, 'synset': 'rose_mallow.n.01', 'name': 'rose_mallow'}, {'id': 19096, 'synset': 'cotton_rose.n.01', 'name': 'cotton_rose'}, {'id': 19097, 'synset': 'roselle.n.01', 'name': 'roselle'}, {'id': 19098, 'synset': 'mahoe.n.01', 'name': 'mahoe'}, {'id': 19099, 'synset': 'flower-of-an-hour.n.01', 'name': 'flower-of-an-hour'}, {'id': 19100, 'synset': 'lacebark.n.01', 'name': 'lacebark'}, {'id': 19101, 'synset': 'wild_hollyhock.n.02', 'name': 'wild_hollyhock'}, {'id': 19102, 'synset': 'mountain_hollyhock.n.01', 'name': 'mountain_hollyhock'}, {'id': 19103, 'synset': 'seashore_mallow.n.01', 'name': 'seashore_mallow'}, {'id': 19104, 'synset': 'salt_marsh_mallow.n.01', 'name': 'salt_marsh_mallow'}, {'id': 19105, 'synset': 'chaparral_mallow.n.01', 'name': 'chaparral_mallow'}, {'id': 19106, 'synset': 'malope.n.01', 'name': 'malope'}, {'id': 19107, 'synset': 'false_mallow.n.02', 'name': 'false_mallow'}, {'id': 19108, 'synset': 'waxmallow.n.01', 'name': 'waxmallow'}, {'id': 19109, 'synset': 'glade_mallow.n.01', 'name': 'glade_mallow'}, {'id': 19110, 'synset': 'pavonia.n.01', 'name': 'pavonia'}, {'id': 19111, 'synset': 'ribbon_tree.n.01', 'name': 'ribbon_tree'}, {'id': 19112, 'synset': 'bush_hibiscus.n.01', 'name': 'bush_hibiscus'}, {'id': 19113, 'synset': 'virginia_mallow.n.01', 'name': 'Virginia_mallow'}, {'id': 19114, 'synset': 'queensland_hemp.n.01', 'name': 'Queensland_hemp'}, {'id': 19115, 'synset': 'indian_mallow.n.01', 'name': 'Indian_mallow'}, {'id': 19116, 'synset': 'checkerbloom.n.01', 'name': 'checkerbloom'}, {'id': 19117, 'synset': 'globe_mallow.n.01', 'name': 'globe_mallow'}, {'id': 19118, 'synset': 'prairie_mallow.n.01', 'name': 'prairie_mallow'}, {'id': 19119, 'synset': 'tulipwood_tree.n.01', 'name': 'tulipwood_tree'}, {'id': 19120, 'synset': 'portia_tree.n.01', 'name': 'portia_tree'}, {'id': 19121, 'synset': 'red_silk-cotton_tree.n.01', 'name': 'red_silk-cotton_tree'}, {'id': 19122, 'synset': 'cream-of-tartar_tree.n.01', 'name': 'cream-of-tartar_tree'}, {'id': 19123, 'synset': 'baobab.n.01', 'name': 'baobab'}, {'id': 19124, 'synset': 'kapok.n.02', 'name': 'kapok'}, {'id': 19125, 'synset': 'durian.n.01', 'name': 'durian'}, {'id': 19126, 'synset': 'montezuma.n.01', 'name': 'Montezuma'}, {'id': 19127, 'synset': 'shaving-brush_tree.n.01', 'name': 'shaving-brush_tree'}, {'id': 19128, 'synset': 'quandong.n.03', 'name': 'quandong'}, {'id': 19129, 'synset': 'quandong.n.02', 'name': 'quandong'}, {'id': 19130, 'synset': 'makomako.n.01', 'name': 'makomako'}, {'id': 19131, 'synset': 'jamaican_cherry.n.01', 'name': 'Jamaican_cherry'}, {'id': 19132, 'synset': 'breakax.n.01', 'name': 'breakax'}, {'id': 19133, 'synset': 'sterculia.n.01', 'name': 'sterculia'}, {'id': 19134, 'synset': 'panama_tree.n.01', 'name': 'Panama_tree'}, {'id': 19135, 'synset': 'kalumpang.n.01', 'name': 'kalumpang'}, {'id': 19136, 'synset': 'bottle-tree.n.01', 'name': 'bottle-tree'}, {'id': 19137, 'synset': 'flame_tree.n.04', 'name': 'flame_tree'}, {'id': 19138, 'synset': 'flame_tree.n.03', 'name': 'flame_tree'}, {'id': 19139, 'synset': 'kurrajong.n.01', 'name': 'kurrajong'}, {'id': 19140, 'synset': 'queensland_bottletree.n.01', 'name': 'Queensland_bottletree'}, {'id': 19141, 'synset': 'kola.n.01', 'name': 'kola'}, {'id': 19142, 'synset': 'kola_nut.n.01', 'name': 'kola_nut'}, {'id': 19143, 'synset': 'chinese_parasol_tree.n.01', 'name': 'Chinese_parasol_tree'}, {'id': 19144, 'synset': 'flannelbush.n.01', 'name': 'flannelbush'}, {'id': 19145, 'synset': 'screw_tree.n.01', 'name': 'screw_tree'}, {'id': 19146, 'synset': 'nut-leaved_screw_tree.n.01', 'name': 'nut-leaved_screw_tree'}, {'id': 19147, 'synset': 'red_beech.n.02', 'name': 'red_beech'}, {'id': 19148, 'synset': 'looking_glass_tree.n.01', 'name': 'looking_glass_tree'}, {'id': 19149, 'synset': 'looking-glass_plant.n.01', 'name': 'looking-glass_plant'}, {'id': 19150, 'synset': 'honey_bell.n.01', 'name': 'honey_bell'}, {'id': 19151, 'synset': 'mayeng.n.01', 'name': 'mayeng'}, {'id': 19152, 'synset': 'silver_tree.n.02', 'name': 'silver_tree'}, {'id': 19153, 'synset': 'cacao.n.01', 'name': 'cacao'}, {'id': 19154, 'synset': 'obeche.n.02', 'name': 'obeche'}, {'id': 19155, 'synset': 'linden.n.02', 'name': 'linden'}, {'id': 19156, 'synset': 'american_basswood.n.01', 'name': 'American_basswood'}, {'id': 19157, 'synset': 'small-leaved_linden.n.01', 'name': 'small-leaved_linden'}, {'id': 19158, 'synset': 'white_basswood.n.01', 'name': 'white_basswood'}, {'id': 19159, 'synset': 'japanese_linden.n.01', 'name': 'Japanese_linden'}, {'id': 19160, 'synset': 'silver_lime.n.01', 'name': 'silver_lime'}, {'id': 19161, 'synset': 'corchorus.n.01', 'name': 'corchorus'}, {'id': 19162, 'synset': 'african_hemp.n.02', 'name': 'African_hemp'}, {'id': 19163, 'synset': 'herb.n.01', 'name': 'herb'}, {'id': 19164, 'synset': 'protea.n.01', 'name': 'protea'}, {'id': 19165, 'synset': 'honeypot.n.01', 'name': 'honeypot'}, {'id': 19166, 'synset': 'honeyflower.n.02', 'name': 'honeyflower'}, {'id': 19167, 'synset': 'banksia.n.01', 'name': 'banksia'}, {'id': 19168, 'synset': 'honeysuckle.n.02', 'name': 'honeysuckle'}, {'id': 19169, 'synset': 'smoke_bush.n.02', 'name': 'smoke_bush'}, {'id': 19170, 'synset': 'chilean_firebush.n.01', 'name': 'Chilean_firebush'}, {'id': 19171, 'synset': 'chilean_nut.n.01', 'name': 'Chilean_nut'}, {'id': 19172, 'synset': 'grevillea.n.01', 'name': 'grevillea'}, {'id': 19173, 'synset': 'red-flowered_silky_oak.n.01', 'name': 'red-flowered_silky_oak'}, {'id': 19174, 'synset': 'silky_oak.n.01', 'name': 'silky_oak'}, {'id': 19175, 'synset': 'beefwood.n.05', 'name': 'beefwood'}, {'id': 19176, 'synset': 'cushion_flower.n.01', 'name': 'cushion_flower'}, {'id': 19177, 'synset': 'rewa-rewa.n.01', 'name': 'rewa-rewa'}, {'id': 19178, 'synset': 'honeyflower.n.01', 'name': 'honeyflower'}, {'id': 19179, 'synset': 'silver_tree.n.01', 'name': 'silver_tree'}, {'id': 19180, 'synset': 'lomatia.n.01', 'name': 'lomatia'}, {'id': 19181, 'synset': 'macadamia.n.01', 'name': 'macadamia'}, {'id': 19182, 'synset': 'macadamia_integrifolia.n.01', 'name': 'Macadamia_integrifolia'}, {'id': 19183, 'synset': 'macadamia_nut.n.01', 'name': 'macadamia_nut'}, {'id': 19184, 'synset': 'queensland_nut.n.01', 'name': 'Queensland_nut'}, {'id': 19185, 'synset': 'prickly_ash.n.02', 'name': 'prickly_ash'}, {'id': 19186, 'synset': 'geebung.n.01', 'name': 'geebung'}, {'id': 19187, 'synset': 'wheel_tree.n.01', 'name': 'wheel_tree'}, {'id': 19188, 'synset': 'scrub_beefwood.n.01', 'name': 'scrub_beefwood'}, {'id': 19189, 'synset': 'waratah.n.02', 'name': 'waratah'}, {'id': 19190, 'synset': 'waratah.n.01', 'name': 'waratah'}, {'id': 19191, 'synset': 'casuarina.n.01', 'name': 'casuarina'}, {'id': 19192, 'synset': 'she-oak.n.01', 'name': 'she-oak'}, {'id': 19193, 'synset': 'beefwood.n.03', 'name': 'beefwood'}, {'id': 19194, 'synset': 'australian_pine.n.01', 'name': 'Australian_pine'}, {'id': 19195, 'synset': 'heath.n.01', 'name': 'heath'}, {'id': 19196, 'synset': 'tree_heath.n.02', 'name': 'tree_heath'}, {'id': 19197, 'synset': 'briarroot.n.01', 'name': 'briarroot'}, {'id': 19198, 'synset': 'winter_heath.n.01', 'name': 'winter_heath'}, {'id': 19199, 'synset': 'bell_heather.n.02', 'name': 'bell_heather'}, {'id': 19200, 'synset': 'cornish_heath.n.01', 'name': 'Cornish_heath'}, {'id': 19201, 'synset': 'spanish_heath.n.01', 'name': 'Spanish_heath'}, {'id': 19202, 'synset': "prince-of-wales'-heath.n.01", 'name': "Prince-of-Wales'-heath"}, {'id': 19203, 'synset': 'bog_rosemary.n.01', 'name': 'bog_rosemary'}, {'id': 19204, 'synset': 'marsh_andromeda.n.01', 'name': 'marsh_andromeda'}, {'id': 19205, 'synset': 'madrona.n.01', 'name': 'madrona'}, {'id': 19206, 'synset': 'strawberry_tree.n.01', 'name': 'strawberry_tree'}, {'id': 19207, 'synset': 'bearberry.n.03', 'name': 'bearberry'}, {'id': 19208, 'synset': 'alpine_bearberry.n.01', 'name': 'alpine_bearberry'}, {'id': 19209, 'synset': 'heartleaf_manzanita.n.01', 'name': 'heartleaf_manzanita'}, {'id': 19210, 'synset': 'parry_manzanita.n.01', 'name': 'Parry_manzanita'}, {'id': 19211, 'synset': 'spike_heath.n.01', 'name': 'spike_heath'}, {'id': 19212, 'synset': 'bryanthus.n.01', 'name': 'bryanthus'}, {'id': 19213, 'synset': 'leatherleaf.n.02', 'name': 'leatherleaf'}, {'id': 19214, 'synset': 'connemara_heath.n.01', 'name': 'Connemara_heath'}, {'id': 19215, 'synset': 'trailing_arbutus.n.01', 'name': 'trailing_arbutus'}, {'id': 19216, 'synset': 'creeping_snowberry.n.01', 'name': 'creeping_snowberry'}, {'id': 19217, 'synset': 'salal.n.01', 'name': 'salal'}, {'id': 19218, 'synset': 'huckleberry.n.02', 'name': 'huckleberry'}, {'id': 19219, 'synset': 'black_huckleberry.n.01', 'name': 'black_huckleberry'}, {'id': 19220, 'synset': 'dangleberry.n.01', 'name': 'dangleberry'}, {'id': 19221, 'synset': 'box_huckleberry.n.01', 'name': 'box_huckleberry'}, {'id': 19222, 'synset': 'kalmia.n.01', 'name': 'kalmia'}, {'id': 19223, 'synset': 'mountain_laurel.n.01', 'name': 'mountain_laurel'}, {'id': 19224, 'synset': 'swamp_laurel.n.01', 'name': 'swamp_laurel'}, {'id': 19225, 'synset': "trapper's_tea.n.01", 'name': "trapper's_tea"}, {'id': 19226, 'synset': 'wild_rosemary.n.01', 'name': 'wild_rosemary'}, {'id': 19227, 'synset': 'sand_myrtle.n.01', 'name': 'sand_myrtle'}, {'id': 19228, 'synset': 'leucothoe.n.01', 'name': 'leucothoe'}, {'id': 19229, 'synset': 'dog_laurel.n.01', 'name': 'dog_laurel'}, {'id': 19230, 'synset': 'sweet_bells.n.01', 'name': 'sweet_bells'}, {'id': 19231, 'synset': 'alpine_azalea.n.01', 'name': 'alpine_azalea'}, {'id': 19232, 'synset': 'staggerbush.n.01', 'name': 'staggerbush'}, {'id': 19233, 'synset': 'maleberry.n.01', 'name': 'maleberry'}, {'id': 19234, 'synset': 'fetterbush.n.02', 'name': 'fetterbush'}, {'id': 19235, 'synset': 'false_azalea.n.01', 'name': 'false_azalea'}, {'id': 19236, 'synset': 'minniebush.n.01', 'name': 'minniebush'}, {'id': 19237, 'synset': 'sorrel_tree.n.01', 'name': 'sorrel_tree'}, {'id': 19238, 'synset': 'mountain_heath.n.01', 'name': 'mountain_heath'}, {'id': 19239, 'synset': 'purple_heather.n.01', 'name': 'purple_heather'}, {'id': 19240, 'synset': 'fetterbush.n.01', 'name': 'fetterbush'}, {'id': 19241, 'synset': 'rhododendron.n.01', 'name': 'rhododendron'}, {'id': 19242, 'synset': 'coast_rhododendron.n.01', 'name': 'coast_rhododendron'}, {'id': 19243, 'synset': 'rosebay.n.01', 'name': 'rosebay'}, {'id': 19244, 'synset': 'swamp_azalea.n.01', 'name': 'swamp_azalea'}, {'id': 19245, 'synset': 'azalea.n.01', 'name': 'azalea'}, {'id': 19246, 'synset': 'cranberry.n.01', 'name': 'cranberry'}, {'id': 19247, 'synset': 'american_cranberry.n.01', 'name': 'American_cranberry'}, {'id': 19248, 'synset': 'european_cranberry.n.01', 'name': 'European_cranberry'}, {'id': 19249, 'synset': 'blueberry.n.01', 'name': 'blueberry'}, {'id': 19250, 'synset': 'farkleberry.n.01', 'name': 'farkleberry'}, {'id': 19251, 'synset': 'low-bush_blueberry.n.01', 'name': 'low-bush_blueberry'}, {'id': 19252, 'synset': 'rabbiteye_blueberry.n.01', 'name': 'rabbiteye_blueberry'}, {'id': 19253, 'synset': 'dwarf_bilberry.n.01', 'name': 'dwarf_bilberry'}, {'id': 19254, 'synset': 'evergreen_blueberry.n.01', 'name': 'evergreen_blueberry'}, {'id': 19255, 'synset': 'evergreen_huckleberry.n.01', 'name': 'evergreen_huckleberry'}, {'id': 19256, 'synset': 'bilberry.n.02', 'name': 'bilberry'}, {'id': 19257, 'synset': 'bilberry.n.01', 'name': 'bilberry'}, {'id': 19258, 'synset': 'bog_bilberry.n.01', 'name': 'bog_bilberry'}, {'id': 19259, 'synset': 'dryland_blueberry.n.01', 'name': 'dryland_blueberry'}, {'id': 19260, 'synset': 'grouseberry.n.01', 'name': 'grouseberry'}, {'id': 19261, 'synset': 'deerberry.n.01', 'name': 'deerberry'}, {'id': 19262, 'synset': 'cowberry.n.01', 'name': 'cowberry'}, {'id': 19263, 'synset': 'diapensia.n.01', 'name': 'diapensia'}, {'id': 19264, 'synset': 'galax.n.01', 'name': 'galax'}, {'id': 19265, 'synset': 'pyxie.n.01', 'name': 'pyxie'}, {'id': 19266, 'synset': 'shortia.n.01', 'name': 'shortia'}, {'id': 19267, 'synset': 'oconee_bells.n.01', 'name': 'oconee_bells'}, {'id': 19268, 'synset': 'australian_heath.n.01', 'name': 'Australian_heath'}, {'id': 19269, 'synset': 'epacris.n.01', 'name': 'epacris'}, {'id': 19270, 'synset': 'common_heath.n.02', 'name': 'common_heath'}, {'id': 19271, 'synset': 'common_heath.n.01', 'name': 'common_heath'}, {'id': 19272, 'synset': 'port_jackson_heath.n.01', 'name': 'Port_Jackson_heath'}, {'id': 19273, 'synset': 'native_cranberry.n.01', 'name': 'native_cranberry'}, {'id': 19274, 'synset': 'pink_fivecorner.n.01', 'name': 'pink_fivecorner'}, {'id': 19275, 'synset': 'wintergreen.n.01', 'name': 'wintergreen'}, {'id': 19276, 'synset': 'false_wintergreen.n.01', 'name': 'false_wintergreen'}, {'id': 19277, 'synset': 'lesser_wintergreen.n.01', 'name': 'lesser_wintergreen'}, {'id': 19278, 'synset': 'wild_lily_of_the_valley.n.02', 'name': 'wild_lily_of_the_valley'}, {'id': 19279, 'synset': 'wild_lily_of_the_valley.n.01', 'name': 'wild_lily_of_the_valley'}, {'id': 19280, 'synset': 'pipsissewa.n.01', 'name': 'pipsissewa'}, {'id': 19281, 'synset': 'love-in-winter.n.01', 'name': 'love-in-winter'}, {'id': 19282, 'synset': 'one-flowered_wintergreen.n.01', 'name': 'one-flowered_wintergreen'}, {'id': 19283, 'synset': 'indian_pipe.n.01', 'name': 'Indian_pipe'}, {'id': 19284, 'synset': 'pinesap.n.01', 'name': 'pinesap'}, {'id': 19285, 'synset': 'beech.n.01', 'name': 'beech'}, {'id': 19286, 'synset': 'common_beech.n.01', 'name': 'common_beech'}, {'id': 19287, 'synset': 'copper_beech.n.01', 'name': 'copper_beech'}, {'id': 19288, 'synset': 'american_beech.n.01', 'name': 'American_beech'}, {'id': 19289, 'synset': 'weeping_beech.n.01', 'name': 'weeping_beech'}, {'id': 19290, 'synset': 'japanese_beech.n.01', 'name': 'Japanese_beech'}, {'id': 19291, 'synset': 'chestnut.n.02', 'name': 'chestnut'}, {'id': 19292, 'synset': 'american_chestnut.n.01', 'name': 'American_chestnut'}, {'id': 19293, 'synset': 'european_chestnut.n.01', 'name': 'European_chestnut'}, {'id': 19294, 'synset': 'chinese_chestnut.n.01', 'name': 'Chinese_chestnut'}, {'id': 19295, 'synset': 'japanese_chestnut.n.01', 'name': 'Japanese_chestnut'}, {'id': 19296, 'synset': 'allegheny_chinkapin.n.01', 'name': 'Allegheny_chinkapin'}, {'id': 19297, 'synset': 'ozark_chinkapin.n.01', 'name': 'Ozark_chinkapin'}, {'id': 19298, 'synset': 'oak_chestnut.n.01', 'name': 'oak_chestnut'}, {'id': 19299, 'synset': 'giant_chinkapin.n.01', 'name': 'giant_chinkapin'}, {'id': 19300, 'synset': 'dwarf_golden_chinkapin.n.01', 'name': 'dwarf_golden_chinkapin'}, {'id': 19301, 'synset': 'tanbark_oak.n.01', 'name': 'tanbark_oak'}, {'id': 19302, 'synset': 'japanese_oak.n.02', 'name': 'Japanese_oak'}, {'id': 19303, 'synset': 'southern_beech.n.01', 'name': 'southern_beech'}, {'id': 19304, 'synset': 'myrtle_beech.n.01', 'name': 'myrtle_beech'}, {'id': 19305, 'synset': 'coigue.n.01', 'name': 'Coigue'}, {'id': 19306, 'synset': 'new_zealand_beech.n.01', 'name': 'New_Zealand_beech'}, {'id': 19307, 'synset': 'silver_beech.n.01', 'name': 'silver_beech'}, {'id': 19308, 'synset': 'roble_beech.n.01', 'name': 'roble_beech'}, {'id': 19309, 'synset': 'rauli_beech.n.01', 'name': 'rauli_beech'}, {'id': 19310, 'synset': 'black_beech.n.01', 'name': 'black_beech'}, {'id': 19311, 'synset': 'hard_beech.n.01', 'name': 'hard_beech'}, {'id': 19312, 'synset': 'acorn.n.01', 'name': 'acorn'}, {'id': 19313, 'synset': 'cupule.n.01', 'name': 'cupule'}, {'id': 19314, 'synset': 'oak.n.02', 'name': 'oak'}, {'id': 19315, 'synset': 'live_oak.n.01', 'name': 'live_oak'}, {'id': 19316, 'synset': 'coast_live_oak.n.01', 'name': 'coast_live_oak'}, {'id': 19317, 'synset': 'white_oak.n.01', 'name': 'white_oak'}, {'id': 19318, 'synset': 'american_white_oak.n.01', 'name': 'American_white_oak'}, {'id': 19319, 'synset': 'arizona_white_oak.n.01', 'name': 'Arizona_white_oak'}, {'id': 19320, 'synset': 'swamp_white_oak.n.01', 'name': 'swamp_white_oak'}, {'id': 19321, 'synset': 'european_turkey_oak.n.01', 'name': 'European_turkey_oak'}, {'id': 19322, 'synset': 'canyon_oak.n.01', 'name': 'canyon_oak'}, {'id': 19323, 'synset': 'scarlet_oak.n.01', 'name': 'scarlet_oak'}, {'id': 19324, 'synset': 'jack_oak.n.02', 'name': 'jack_oak'}, {'id': 19325, 'synset': 'red_oak.n.01', 'name': 'red_oak'}, {'id': 19326, 'synset': 'southern_red_oak.n.01', 'name': 'southern_red_oak'}, {'id': 19327, 'synset': 'oregon_white_oak.n.01', 'name': 'Oregon_white_oak'}, {'id': 19328, 'synset': 'holm_oak.n.02', 'name': 'holm_oak'}, {'id': 19329, 'synset': 'bear_oak.n.01', 'name': 'bear_oak'}, {'id': 19330, 'synset': 'shingle_oak.n.01', 'name': 'shingle_oak'}, {'id': 19331, 'synset': 'bluejack_oak.n.01', 'name': 'bluejack_oak'}, {'id': 19332, 'synset': 'california_black_oak.n.01', 'name': 'California_black_oak'}, {'id': 19333, 'synset': 'american_turkey_oak.n.01', 'name': 'American_turkey_oak'}, {'id': 19334, 'synset': 'laurel_oak.n.01', 'name': 'laurel_oak'}, {'id': 19335, 'synset': 'california_white_oak.n.01', 'name': 'California_white_oak'}, {'id': 19336, 'synset': 'overcup_oak.n.01', 'name': 'overcup_oak'}, {'id': 19337, 'synset': 'bur_oak.n.01', 'name': 'bur_oak'}, {'id': 19338, 'synset': 'scrub_oak.n.01', 'name': 'scrub_oak'}, {'id': 19339, 'synset': 'blackjack_oak.n.01', 'name': 'blackjack_oak'}, {'id': 19340, 'synset': 'swamp_chestnut_oak.n.01', 'name': 'swamp_chestnut_oak'}, {'id': 19341, 'synset': 'japanese_oak.n.01', 'name': 'Japanese_oak'}, {'id': 19342, 'synset': 'chestnut_oak.n.01', 'name': 'chestnut_oak'}, {'id': 19343, 'synset': 'chinquapin_oak.n.01', 'name': 'chinquapin_oak'}, {'id': 19344, 'synset': 'myrtle_oak.n.01', 'name': 'myrtle_oak'}, {'id': 19345, 'synset': 'water_oak.n.01', 'name': 'water_oak'}, {'id': 19346, 'synset': 'nuttall_oak.n.01', 'name': 'Nuttall_oak'}, {'id': 19347, 'synset': 'durmast.n.01', 'name': 'durmast'}, {'id': 19348, 'synset': 'basket_oak.n.01', 'name': 'basket_oak'}, {'id': 19349, 'synset': 'pin_oak.n.01', 'name': 'pin_oak'}, {'id': 19350, 'synset': 'willow_oak.n.01', 'name': 'willow_oak'}, {'id': 19351, 'synset': 'dwarf_chinkapin_oak.n.01', 'name': 'dwarf_chinkapin_oak'}, {'id': 19352, 'synset': 'common_oak.n.01', 'name': 'common_oak'}, {'id': 19353, 'synset': 'northern_red_oak.n.01', 'name': 'northern_red_oak'}, {'id': 19354, 'synset': 'shumard_oak.n.01', 'name': 'Shumard_oak'}, {'id': 19355, 'synset': 'post_oak.n.01', 'name': 'post_oak'}, {'id': 19356, 'synset': 'cork_oak.n.01', 'name': 'cork_oak'}, {'id': 19357, 'synset': 'spanish_oak.n.01', 'name': 'Spanish_oak'}, {'id': 19358, 'synset': 'huckleberry_oak.n.01', 'name': 'huckleberry_oak'}, {'id': 19359, 'synset': 'chinese_cork_oak.n.01', 'name': 'Chinese_cork_oak'}, {'id': 19360, 'synset': 'black_oak.n.01', 'name': 'black_oak'}, {'id': 19361, 'synset': 'southern_live_oak.n.01', 'name': 'southern_live_oak'}, {'id': 19362, 'synset': 'interior_live_oak.n.01', 'name': 'interior_live_oak'}, {'id': 19363, 'synset': 'mast.n.02', 'name': 'mast'}, {'id': 19364, 'synset': 'birch.n.02', 'name': 'birch'}, {'id': 19365, 'synset': 'yellow_birch.n.01', 'name': 'yellow_birch'}, {'id': 19366, 'synset': 'american_white_birch.n.01', 'name': 'American_white_birch'}, {'id': 19367, 'synset': 'grey_birch.n.01', 'name': 'grey_birch'}, {'id': 19368, 'synset': 'silver_birch.n.01', 'name': 'silver_birch'}, {'id': 19369, 'synset': 'downy_birch.n.01', 'name': 'downy_birch'}, {'id': 19370, 'synset': 'black_birch.n.02', 'name': 'black_birch'}, {'id': 19371, 'synset': 'sweet_birch.n.01', 'name': 'sweet_birch'}, {'id': 19372, 'synset': 'yukon_white_birch.n.01', 'name': 'Yukon_white_birch'}, {'id': 19373, 'synset': 'swamp_birch.n.01', 'name': 'swamp_birch'}, {'id': 19374, 'synset': 'newfoundland_dwarf_birch.n.01', 'name': 'Newfoundland_dwarf_birch'}, {'id': 19375, 'synset': 'alder.n.02', 'name': 'alder'}, {'id': 19376, 'synset': 'common_alder.n.01', 'name': 'common_alder'}, {'id': 19377, 'synset': 'grey_alder.n.01', 'name': 'grey_alder'}, {'id': 19378, 'synset': 'seaside_alder.n.01', 'name': 'seaside_alder'}, {'id': 19379, 'synset': 'white_alder.n.01', 'name': 'white_alder'}, {'id': 19380, 'synset': 'red_alder.n.01', 'name': 'red_alder'}, {'id': 19381, 'synset': 'speckled_alder.n.01', 'name': 'speckled_alder'}, {'id': 19382, 'synset': 'smooth_alder.n.01', 'name': 'smooth_alder'}, {'id': 19383, 'synset': 'green_alder.n.02', 'name': 'green_alder'}, {'id': 19384, 'synset': 'green_alder.n.01', 'name': 'green_alder'}, {'id': 19385, 'synset': 'hornbeam.n.01', 'name': 'hornbeam'}, {'id': 19386, 'synset': 'european_hornbeam.n.01', 'name': 'European_hornbeam'}, {'id': 19387, 'synset': 'american_hornbeam.n.01', 'name': 'American_hornbeam'}, {'id': 19388, 'synset': 'hop_hornbeam.n.01', 'name': 'hop_hornbeam'}, {'id': 19389, 'synset': 'old_world_hop_hornbeam.n.01', 'name': 'Old_World_hop_hornbeam'}, {'id': 19390, 'synset': 'eastern_hop_hornbeam.n.01', 'name': 'Eastern_hop_hornbeam'}, {'id': 19391, 'synset': 'hazelnut.n.01', 'name': 'hazelnut'}, {'id': 19392, 'synset': 'american_hazel.n.01', 'name': 'American_hazel'}, {'id': 19393, 'synset': 'cobnut.n.01', 'name': 'cobnut'}, {'id': 19394, 'synset': 'beaked_hazelnut.n.01', 'name': 'beaked_hazelnut'}, {'id': 19395, 'synset': 'centaury.n.01', 'name': 'centaury'}, {'id': 19396, 'synset': 'rosita.n.01', 'name': 'rosita'}, {'id': 19397, 'synset': 'lesser_centaury.n.01', 'name': 'lesser_centaury'}, {'id': 19398, 'synset': 'seaside_centaury.n.01', 'name': 'seaside_centaury'}, {'id': 19399, 'synset': 'slender_centaury.n.01', 'name': 'slender_centaury'}, {'id': 19400, 'synset': 'prairie_gentian.n.01', 'name': 'prairie_gentian'}, {'id': 19401, 'synset': 'persian_violet.n.01', 'name': 'Persian_violet'}, {'id': 19402, 'synset': 'columbo.n.01', 'name': 'columbo'}, {'id': 19403, 'synset': 'gentian.n.01', 'name': 'gentian'}, {'id': 19404, 'synset': 'gentianella.n.02', 'name': 'gentianella'}, {'id': 19405, 'synset': 'closed_gentian.n.02', 'name': 'closed_gentian'}, {'id': 19406, 'synset': "explorer's_gentian.n.01", 'name': "explorer's_gentian"}, {'id': 19407, 'synset': 'closed_gentian.n.01', 'name': 'closed_gentian'}, {'id': 19408, 'synset': 'great_yellow_gentian.n.01', 'name': 'great_yellow_gentian'}, {'id': 19409, 'synset': 'marsh_gentian.n.01', 'name': 'marsh_gentian'}, {'id': 19410, 'synset': 'soapwort_gentian.n.01', 'name': 'soapwort_gentian'}, {'id': 19411, 'synset': 'striped_gentian.n.01', 'name': 'striped_gentian'}, {'id': 19412, 'synset': 'agueweed.n.01', 'name': 'agueweed'}, {'id': 19413, 'synset': 'felwort.n.01', 'name': 'felwort'}, {'id': 19414, 'synset': 'fringed_gentian.n.01', 'name': 'fringed_gentian'}, {'id': 19415, 'synset': 'gentianopsis_crinita.n.01', 'name': 'Gentianopsis_crinita'}, {'id': 19416, 'synset': 'gentianopsis_detonsa.n.01', 'name': 'Gentianopsis_detonsa'}, {'id': 19417, 'synset': 'gentianopsid_procera.n.01', 'name': 'Gentianopsid_procera'}, {'id': 19418, 'synset': 'gentianopsis_thermalis.n.01', 'name': 'Gentianopsis_thermalis'}, {'id': 19419, 'synset': 'tufted_gentian.n.01', 'name': 'tufted_gentian'}, {'id': 19420, 'synset': 'spurred_gentian.n.01', 'name': 'spurred_gentian'}, {'id': 19421, 'synset': 'sabbatia.n.01', 'name': 'sabbatia'}, {'id': 19422, 'synset': 'toothbrush_tree.n.01', 'name': 'toothbrush_tree'}, {'id': 19423, 'synset': 'olive_tree.n.01', 'name': 'olive_tree'}, {'id': 19424, 'synset': 'olive.n.02', 'name': 'olive'}, {'id': 19425, 'synset': 'olive.n.01', 'name': 'olive'}, {'id': 19426, 'synset': 'black_maire.n.01', 'name': 'black_maire'}, {'id': 19427, 'synset': 'white_maire.n.01', 'name': 'white_maire'}, {'id': 19428, 'synset': 'fringe_tree.n.01', 'name': 'fringe_tree'}, {'id': 19429, 'synset': 'fringe_bush.n.01', 'name': 'fringe_bush'}, {'id': 19430, 'synset': 'forestiera.n.01', 'name': 'forestiera'}, {'id': 19431, 'synset': 'forsythia.n.01', 'name': 'forsythia'}, {'id': 19432, 'synset': 'ash.n.02', 'name': 'ash'}, {'id': 19433, 'synset': 'white_ash.n.02', 'name': 'white_ash'}, {'id': 19434, 'synset': 'swamp_ash.n.01', 'name': 'swamp_ash'}, {'id': 19435, 'synset': 'flowering_ash.n.03', 'name': 'flowering_ash'}, {'id': 19436, 'synset': 'european_ash.n.01', 'name': 'European_ash'}, {'id': 19437, 'synset': 'oregon_ash.n.01', 'name': 'Oregon_ash'}, {'id': 19438, 'synset': 'black_ash.n.01', 'name': 'black_ash'}, {'id': 19439, 'synset': 'manna_ash.n.01', 'name': 'manna_ash'}, {'id': 19440, 'synset': 'red_ash.n.01', 'name': 'red_ash'}, {'id': 19441, 'synset': 'green_ash.n.01', 'name': 'green_ash'}, {'id': 19442, 'synset': 'blue_ash.n.01', 'name': 'blue_ash'}, {'id': 19443, 'synset': 'mountain_ash.n.03', 'name': 'mountain_ash'}, {'id': 19444, 'synset': 'pumpkin_ash.n.01', 'name': 'pumpkin_ash'}, {'id': 19445, 'synset': 'arizona_ash.n.01', 'name': 'Arizona_ash'}, {'id': 19446, 'synset': 'jasmine.n.01', 'name': 'jasmine'}, {'id': 19447, 'synset': 'primrose_jasmine.n.01', 'name': 'primrose_jasmine'}, {'id': 19448, 'synset': 'winter_jasmine.n.01', 'name': 'winter_jasmine'}, {'id': 19449, 'synset': 'common_jasmine.n.01', 'name': 'common_jasmine'}, {'id': 19450, 'synset': 'privet.n.01', 'name': 'privet'}, {'id': 19451, 'synset': 'amur_privet.n.01', 'name': 'Amur_privet'}, {'id': 19452, 'synset': 'japanese_privet.n.01', 'name': 'Japanese_privet'}, {'id': 19453, 'synset': 'ligustrum_obtusifolium.n.01', 'name': 'Ligustrum_obtusifolium'}, {'id': 19454, 'synset': 'common_privet.n.01', 'name': 'common_privet'}, {'id': 19455, 'synset': 'devilwood.n.01', 'name': 'devilwood'}, {'id': 19456, 'synset': 'mock_privet.n.01', 'name': 'mock_privet'}, {'id': 19457, 'synset': 'lilac.n.01', 'name': 'lilac'}, {'id': 19458, 'synset': 'himalayan_lilac.n.01', 'name': 'Himalayan_lilac'}, {'id': 19459, 'synset': 'persian_lilac.n.02', 'name': 'Persian_lilac'}, {'id': 19460, 'synset': 'japanese_tree_lilac.n.01', 'name': 'Japanese_tree_lilac'}, {'id': 19461, 'synset': 'japanese_lilac.n.01', 'name': 'Japanese_lilac'}, {'id': 19462, 'synset': 'common_lilac.n.01', 'name': 'common_lilac'}, {'id': 19463, 'synset': 'bloodwort.n.01', 'name': 'bloodwort'}, {'id': 19464, 'synset': 'kangaroo_paw.n.01', 'name': 'kangaroo_paw'}, {'id': 19465, 'synset': 'virginian_witch_hazel.n.01', 'name': 'Virginian_witch_hazel'}, {'id': 19466, 'synset': 'vernal_witch_hazel.n.01', 'name': 'vernal_witch_hazel'}, {'id': 19467, 'synset': 'winter_hazel.n.01', 'name': 'winter_hazel'}, {'id': 19468, 'synset': 'fothergilla.n.01', 'name': 'fothergilla'}, {'id': 19469, 'synset': 'liquidambar.n.02', 'name': 'liquidambar'}, {'id': 19470, 'synset': 'sweet_gum.n.03', 'name': 'sweet_gum'}, {'id': 19471, 'synset': 'iron_tree.n.01', 'name': 'iron_tree'}, {'id': 19472, 'synset': 'walnut.n.03', 'name': 'walnut'}, {'id': 19473, 'synset': 'california_black_walnut.n.01', 'name': 'California_black_walnut'}, {'id': 19474, 'synset': 'butternut.n.01', 'name': 'butternut'}, {'id': 19475, 'synset': 'black_walnut.n.01', 'name': 'black_walnut'}, {'id': 19476, 'synset': 'english_walnut.n.01', 'name': 'English_walnut'}, {'id': 19477, 'synset': 'hickory.n.02', 'name': 'hickory'}, {'id': 19478, 'synset': 'water_hickory.n.01', 'name': 'water_hickory'}, {'id': 19479, 'synset': 'pignut.n.01', 'name': 'pignut'}, {'id': 19480, 'synset': 'bitternut.n.01', 'name': 'bitternut'}, {'id': 19481, 'synset': 'pecan.n.02', 'name': 'pecan'}, {'id': 19482, 'synset': 'big_shellbark.n.01', 'name': 'big_shellbark'}, {'id': 19483, 'synset': 'nutmeg_hickory.n.01', 'name': 'nutmeg_hickory'}, {'id': 19484, 'synset': 'shagbark.n.01', 'name': 'shagbark'}, {'id': 19485, 'synset': 'mockernut.n.01', 'name': 'mockernut'}, {'id': 19486, 'synset': 'wing_nut.n.01', 'name': 'wing_nut'}, {'id': 19487, 'synset': 'caucasian_walnut.n.01', 'name': 'Caucasian_walnut'}, {'id': 19488, 'synset': 'dhawa.n.01', 'name': 'dhawa'}, {'id': 19489, 'synset': 'combretum.n.01', 'name': 'combretum'}, {'id': 19490, 'synset': 'hiccup_nut.n.01', 'name': 'hiccup_nut'}, {'id': 19491, 'synset': 'bush_willow.n.02', 'name': 'bush_willow'}, {'id': 19492, 'synset': 'bush_willow.n.01', 'name': 'bush_willow'}, {'id': 19493, 'synset': 'button_tree.n.01', 'name': 'button_tree'}, {'id': 19494, 'synset': 'white_mangrove.n.02', 'name': 'white_mangrove'}, {'id': 19495, 'synset': 'oleaster.n.01', 'name': 'oleaster'}, {'id': 19496, 'synset': 'water_milfoil.n.01', 'name': 'water_milfoil'}, {'id': 19497, 'synset': 'anchovy_pear.n.01', 'name': 'anchovy_pear'}, {'id': 19498, 'synset': 'brazil_nut.n.01', 'name': 'brazil_nut'}, {'id': 19499, 'synset': 'loosestrife.n.01', 'name': 'loosestrife'}, {'id': 19500, 'synset': 'purple_loosestrife.n.01', 'name': 'purple_loosestrife'}, {'id': 19501, 'synset': 'grass_poly.n.01', 'name': 'grass_poly'}, {'id': 19502, 'synset': 'crape_myrtle.n.01', 'name': 'crape_myrtle'}, {'id': 19503, 'synset': "queen's_crape_myrtle.n.01", 'name': "Queen's_crape_myrtle"}, {'id': 19504, 'synset': 'myrtaceous_tree.n.01', 'name': 'myrtaceous_tree'}, {'id': 19505, 'synset': 'myrtle.n.02', 'name': 'myrtle'}, {'id': 19506, 'synset': 'common_myrtle.n.01', 'name': 'common_myrtle'}, {'id': 19507, 'synset': 'bayberry.n.01', 'name': 'bayberry'}, {'id': 19508, 'synset': 'allspice.n.01', 'name': 'allspice'}, {'id': 19509, 'synset': 'allspice_tree.n.01', 'name': 'allspice_tree'}, {'id': 19510, 'synset': 'sour_cherry.n.02', 'name': 'sour_cherry'}, {'id': 19511, 'synset': 'nakedwood.n.02', 'name': 'nakedwood'}, {'id': 19512, 'synset': 'surinam_cherry.n.02', 'name': 'Surinam_cherry'}, {'id': 19513, 'synset': 'rose_apple.n.01', 'name': 'rose_apple'}, {'id': 19514, 'synset': 'feijoa.n.01', 'name': 'feijoa'}, {'id': 19515, 'synset': 'jaboticaba.n.01', 'name': 'jaboticaba'}, {'id': 19516, 'synset': 'guava.n.02', 'name': 'guava'}, {'id': 19517, 'synset': 'guava.n.01', 'name': 'guava'}, {'id': 19518, 'synset': 'cattley_guava.n.01', 'name': 'cattley_guava'}, {'id': 19519, 'synset': 'brazilian_guava.n.01', 'name': 'Brazilian_guava'}, {'id': 19520, 'synset': 'gum_tree.n.01', 'name': 'gum_tree'}, {'id': 19521, 'synset': 'eucalyptus.n.02', 'name': 'eucalyptus'}, {'id': 19522, 'synset': 'flooded_gum.n.01', 'name': 'flooded_gum'}, {'id': 19523, 'synset': 'mallee.n.01', 'name': 'mallee'}, {'id': 19524, 'synset': 'stringybark.n.01', 'name': 'stringybark'}, {'id': 19525, 'synset': 'smoothbark.n.01', 'name': 'smoothbark'}, {'id': 19526, 'synset': 'red_gum.n.03', 'name': 'red_gum'}, {'id': 19527, 'synset': 'red_gum.n.02', 'name': 'red_gum'}, {'id': 19528, 'synset': 'river_red_gum.n.01', 'name': 'river_red_gum'}, {'id': 19529, 'synset': 'mountain_swamp_gum.n.01', 'name': 'mountain_swamp_gum'}, {'id': 19530, 'synset': 'snow_gum.n.01', 'name': 'snow_gum'}, {'id': 19531, 'synset': 'alpine_ash.n.01', 'name': 'alpine_ash'}, {'id': 19532, 'synset': 'white_mallee.n.01', 'name': 'white_mallee'}, {'id': 19533, 'synset': 'white_stringybark.n.01', 'name': 'white_stringybark'}, {'id': 19534, 'synset': 'white_mountain_ash.n.01', 'name': 'white_mountain_ash'}, {'id': 19535, 'synset': 'blue_gum.n.01', 'name': 'blue_gum'}, {'id': 19536, 'synset': 'rose_gum.n.01', 'name': 'rose_gum'}, {'id': 19537, 'synset': 'cider_gum.n.01', 'name': 'cider_gum'}, {'id': 19538, 'synset': 'swamp_gum.n.01', 'name': 'swamp_gum'}, {'id': 19539, 'synset': 'spotted_gum.n.01', 'name': 'spotted_gum'}, {'id': 19540, 'synset': 'lemon-scented_gum.n.01', 'name': 'lemon-scented_gum'}, {'id': 19541, 'synset': 'black_mallee.n.01', 'name': 'black_mallee'}, {'id': 19542, 'synset': 'forest_red_gum.n.01', 'name': 'forest_red_gum'}, {'id': 19543, 'synset': 'mountain_ash.n.02', 'name': 'mountain_ash'}, {'id': 19544, 'synset': 'manna_gum.n.01', 'name': 'manna_gum'}, {'id': 19545, 'synset': 'clove.n.02', 'name': 'clove'}, {'id': 19546, 'synset': 'clove.n.01', 'name': 'clove'}, {'id': 19547, 'synset': 'tupelo.n.02', 'name': 'tupelo'}, {'id': 19548, 'synset': 'water_gum.n.01', 'name': 'water_gum'}, {'id': 19549, 'synset': 'sour_gum.n.01', 'name': 'sour_gum'}, {'id': 19550, 'synset': "enchanter's_nightshade.n.01", 'name': "enchanter's_nightshade"}, {'id': 19551, 'synset': 'circaea_lutetiana.n.01', 'name': 'Circaea_lutetiana'}, {'id': 19552, 'synset': 'willowherb.n.01', 'name': 'willowherb'}, {'id': 19553, 'synset': 'fireweed.n.01', 'name': 'fireweed'}, {'id': 19554, 'synset': 'california_fuchsia.n.01', 'name': 'California_fuchsia'}, {'id': 19555, 'synset': 'fuchsia.n.01', 'name': 'fuchsia'}, {'id': 19556, 'synset': "lady's-eardrop.n.01", 'name': "lady's-eardrop"}, {'id': 19557, 'synset': 'evening_primrose.n.01', 'name': 'evening_primrose'}, {'id': 19558, 'synset': 'common_evening_primrose.n.01', 'name': 'common_evening_primrose'}, {'id': 19559, 'synset': 'sundrops.n.01', 'name': 'sundrops'}, {'id': 19560, 'synset': 'missouri_primrose.n.01', 'name': 'Missouri_primrose'}, {'id': 19561, 'synset': 'pomegranate.n.01', 'name': 'pomegranate'}, {'id': 19562, 'synset': 'mangrove.n.01', 'name': 'mangrove'}, {'id': 19563, 'synset': 'daphne.n.01', 'name': 'daphne'}, {'id': 19564, 'synset': 'garland_flower.n.01', 'name': 'garland_flower'}, {'id': 19565, 'synset': 'spurge_laurel.n.01', 'name': 'spurge_laurel'}, {'id': 19566, 'synset': 'mezereon.n.01', 'name': 'mezereon'}, {'id': 19567, 'synset': 'indian_rhododendron.n.01', 'name': 'Indian_rhododendron'}, {'id': 19568, 'synset': 'medinilla_magnifica.n.01', 'name': 'Medinilla_magnifica'}, {'id': 19569, 'synset': 'deer_grass.n.01', 'name': 'deer_grass'}, {'id': 19570, 'synset': 'canna.n.01', 'name': 'canna'}, {'id': 19571, 'synset': 'achira.n.01', 'name': 'achira'}, {'id': 19572, 'synset': 'arrowroot.n.02', 'name': 'arrowroot'}, {'id': 19573, 'synset': 'banana.n.01', 'name': 'banana'}, {'id': 19574, 'synset': 'dwarf_banana.n.01', 'name': 'dwarf_banana'}, {'id': 19575, 'synset': 'japanese_banana.n.01', 'name': 'Japanese_banana'}, {'id': 19576, 'synset': 'plantain.n.02', 'name': 'plantain'}, {'id': 19577, 'synset': 'edible_banana.n.01', 'name': 'edible_banana'}, {'id': 19578, 'synset': 'abaca.n.02', 'name': 'abaca'}, {'id': 19579, 'synset': 'abyssinian_banana.n.01', 'name': 'Abyssinian_banana'}, {'id': 19580, 'synset': 'ginger.n.01', 'name': 'ginger'}, {'id': 19581, 'synset': 'common_ginger.n.01', 'name': 'common_ginger'}, {'id': 19582, 'synset': 'turmeric.n.01', 'name': 'turmeric'}, {'id': 19583, 'synset': 'galangal.n.01', 'name': 'galangal'}, {'id': 19584, 'synset': 'shellflower.n.02', 'name': 'shellflower'}, {'id': 19585, 'synset': 'grains_of_paradise.n.01', 'name': 'grains_of_paradise'}, {'id': 19586, 'synset': 'cardamom.n.01', 'name': 'cardamom'}, {'id': 19587, 'synset': 'begonia.n.01', 'name': 'begonia'}, {'id': 19588, 'synset': 'fibrous-rooted_begonia.n.01', 'name': 'fibrous-rooted_begonia'}, {'id': 19589, 'synset': 'tuberous_begonia.n.01', 'name': 'tuberous_begonia'}, {'id': 19590, 'synset': 'rhizomatous_begonia.n.01', 'name': 'rhizomatous_begonia'}, {'id': 19591, 'synset': 'christmas_begonia.n.01', 'name': 'Christmas_begonia'}, {'id': 19592, 'synset': 'angel-wing_begonia.n.01', 'name': 'angel-wing_begonia'}, {'id': 19593, 'synset': 'beefsteak_begonia.n.01', 'name': 'beefsteak_begonia'}, {'id': 19594, 'synset': 'star_begonia.n.01', 'name': 'star_begonia'}, {'id': 19595, 'synset': 'rex_begonia.n.01', 'name': 'rex_begonia'}, {'id': 19596, 'synset': 'wax_begonia.n.01', 'name': 'wax_begonia'}, {'id': 19597, 'synset': 'socotra_begonia.n.01', 'name': 'Socotra_begonia'}, {'id': 19598, 'synset': 'hybrid_tuberous_begonia.n.01', 'name': 'hybrid_tuberous_begonia'}, {'id': 19599, 'synset': 'dillenia.n.01', 'name': 'dillenia'}, {'id': 19600, 'synset': 'guinea_gold_vine.n.01', 'name': 'guinea_gold_vine'}, {'id': 19601, 'synset': 'poon.n.02', 'name': 'poon'}, {'id': 19602, 'synset': 'calaba.n.01', 'name': 'calaba'}, {'id': 19603, 'synset': 'maria.n.02', 'name': 'Maria'}, {'id': 19604, 'synset': 'laurelwood.n.01', 'name': 'laurelwood'}, {'id': 19605, 'synset': 'alexandrian_laurel.n.01', 'name': 'Alexandrian_laurel'}, {'id': 19606, 'synset': 'clusia.n.01', 'name': 'clusia'}, {'id': 19607, 'synset': 'wild_fig.n.02', 'name': 'wild_fig'}, {'id': 19608, 'synset': 'waxflower.n.02', 'name': 'waxflower'}, {'id': 19609, 'synset': 'pitch_apple.n.01', 'name': 'pitch_apple'}, {'id': 19610, 'synset': 'mangosteen.n.01', 'name': 'mangosteen'}, {'id': 19611, 'synset': 'gamboge_tree.n.01', 'name': 'gamboge_tree'}, {'id': 19612, 'synset': "st_john's_wort.n.01", 'name': "St_John's_wort"}, {'id': 19613, 'synset': "common_st_john's_wort.n.01", 'name': "common_St_John's_wort"}, {'id': 19614, 'synset': "great_st_john's_wort.n.01", 'name': "great_St_John's_wort"}, {'id': 19615, 'synset': "creeping_st_john's_wort.n.01", 'name': "creeping_St_John's_wort"}, {'id': 19616, 'synset': "low_st_andrew's_cross.n.01", 'name': "low_St_Andrew's_cross"}, {'id': 19617, 'synset': 'klammath_weed.n.01', 'name': 'klammath_weed'}, {'id': 19618, 'synset': "shrubby_st_john's_wort.n.01", 'name': "shrubby_St_John's_wort"}, {'id': 19619, 'synset': "st_peter's_wort.n.01", 'name': "St_Peter's_wort"}, {'id': 19620, 'synset': "marsh_st-john's_wort.n.01", 'name': "marsh_St-John's_wort"}, {'id': 19621, 'synset': 'mammee_apple.n.01', 'name': 'mammee_apple'}, {'id': 19622, 'synset': 'rose_chestnut.n.01', 'name': 'rose_chestnut'}, {'id': 19623, 'synset': 'bower_actinidia.n.01', 'name': 'bower_actinidia'}, {'id': 19624, 'synset': 'chinese_gooseberry.n.01', 'name': 'Chinese_gooseberry'}, {'id': 19625, 'synset': 'silvervine.n.01', 'name': 'silvervine'}, {'id': 19626, 'synset': 'wild_cinnamon.n.01', 'name': 'wild_cinnamon'}, {'id': 19627, 'synset': 'papaya.n.01', 'name': 'papaya'}, {'id': 19628, 'synset': 'souari.n.01', 'name': 'souari'}, {'id': 19629, 'synset': 'rockrose.n.02', 'name': 'rockrose'}, {'id': 19630, 'synset': 'white-leaved_rockrose.n.01', 'name': 'white-leaved_rockrose'}, {'id': 19631, 'synset': 'common_gum_cistus.n.01', 'name': 'common_gum_cistus'}, {'id': 19632, 'synset': 'frostweed.n.01', 'name': 'frostweed'}, {'id': 19633, 'synset': 'dipterocarp.n.01', 'name': 'dipterocarp'}, {'id': 19634, 'synset': 'red_lauan.n.02', 'name': 'red_lauan'}, {'id': 19635, 'synset': "governor's_plum.n.01", 'name': "governor's_plum"}, {'id': 19636, 'synset': 'kei_apple.n.01', 'name': 'kei_apple'}, {'id': 19637, 'synset': 'ketembilla.n.01', 'name': 'ketembilla'}, {'id': 19638, 'synset': 'chaulmoogra.n.01', 'name': 'chaulmoogra'}, {'id': 19639, 'synset': 'wild_peach.n.01', 'name': 'wild_peach'}, {'id': 19640, 'synset': 'candlewood.n.01', 'name': 'candlewood'}, {'id': 19641, 'synset': 'boojum_tree.n.01', 'name': 'boojum_tree'}, {'id': 19642, 'synset': "bird's-eye_bush.n.01", 'name': "bird's-eye_bush"}, {'id': 19643, 'synset': 'granadilla.n.03', 'name': 'granadilla'}, {'id': 19644, 'synset': 'granadilla.n.02', 'name': 'granadilla'}, {'id': 19645, 'synset': 'granadilla.n.01', 'name': 'granadilla'}, {'id': 19646, 'synset': 'maypop.n.01', 'name': 'maypop'}, {'id': 19647, 'synset': 'jamaica_honeysuckle.n.01', 'name': 'Jamaica_honeysuckle'}, {'id': 19648, 'synset': 'banana_passion_fruit.n.01', 'name': 'banana_passion_fruit'}, {'id': 19649, 'synset': 'sweet_calabash.n.01', 'name': 'sweet_calabash'}, {'id': 19650, 'synset': 'love-in-a-mist.n.01', 'name': 'love-in-a-mist'}, {'id': 19651, 'synset': 'reseda.n.01', 'name': 'reseda'}, {'id': 19652, 'synset': 'mignonette.n.01', 'name': 'mignonette'}, {'id': 19653, 'synset': "dyer's_rocket.n.01", 'name': "dyer's_rocket"}, {'id': 19654, 'synset': 'false_tamarisk.n.01', 'name': 'false_tamarisk'}, {'id': 19655, 'synset': 'halophyte.n.01', 'name': 'halophyte'}, {'id': 19656, 'synset': 'viola.n.01', 'name': 'viola'}, {'id': 19657, 'synset': 'violet.n.01', 'name': 'violet'}, {'id': 19658, 'synset': 'field_pansy.n.01', 'name': 'field_pansy'}, {'id': 19659, 'synset': 'american_dog_violet.n.01', 'name': 'American_dog_violet'}, {'id': 19660, 'synset': 'dog_violet.n.01', 'name': 'dog_violet'}, {'id': 19661, 'synset': 'horned_violet.n.01', 'name': 'horned_violet'}, {'id': 19662, 'synset': 'two-eyed_violet.n.01', 'name': 'two-eyed_violet'}, {'id': 19663, 'synset': "bird's-foot_violet.n.01", 'name': "bird's-foot_violet"}, {'id': 19664, 'synset': 'downy_yellow_violet.n.01', 'name': 'downy_yellow_violet'}, {'id': 19665, 'synset': 'long-spurred_violet.n.01', 'name': 'long-spurred_violet'}, {'id': 19666, 'synset': 'pale_violet.n.01', 'name': 'pale_violet'}, {'id': 19667, 'synset': 'hedge_violet.n.01', 'name': 'hedge_violet'}, {'id': 19668, 'synset': 'nettle.n.01', 'name': 'nettle'}, {'id': 19669, 'synset': 'stinging_nettle.n.01', 'name': 'stinging_nettle'}, {'id': 19670, 'synset': 'roman_nettle.n.01', 'name': 'Roman_nettle'}, {'id': 19671, 'synset': 'ramie.n.01', 'name': 'ramie'}, {'id': 19672, 'synset': 'wood_nettle.n.01', 'name': 'wood_nettle'}, {'id': 19673, 'synset': 'australian_nettle.n.01', 'name': 'Australian_nettle'}, {'id': 19674, 'synset': 'pellitory-of-the-wall.n.01', 'name': 'pellitory-of-the-wall'}, {'id': 19675, 'synset': 'richweed.n.02', 'name': 'richweed'}, {'id': 19676, 'synset': 'artillery_plant.n.01', 'name': 'artillery_plant'}, {'id': 19677, 'synset': 'friendship_plant.n.01', 'name': 'friendship_plant'}, {'id': 19678, 'synset': 'queensland_grass-cloth_plant.n.01', 'name': 'Queensland_grass-cloth_plant'}, {'id': 19679, 'synset': 'pipturus_albidus.n.01', 'name': 'Pipturus_albidus'}, {'id': 19680, 'synset': 'cannabis.n.01', 'name': 'cannabis'}, {'id': 19681, 'synset': 'indian_hemp.n.01', 'name': 'Indian_hemp'}, {'id': 19682, 'synset': 'mulberry.n.01', 'name': 'mulberry'}, {'id': 19683, 'synset': 'white_mulberry.n.01', 'name': 'white_mulberry'}, {'id': 19684, 'synset': 'black_mulberry.n.01', 'name': 'black_mulberry'}, {'id': 19685, 'synset': 'red_mulberry.n.01', 'name': 'red_mulberry'}, {'id': 19686, 'synset': 'osage_orange.n.01', 'name': 'osage_orange'}, {'id': 19687, 'synset': 'breadfruit.n.01', 'name': 'breadfruit'}, {'id': 19688, 'synset': 'jackfruit.n.01', 'name': 'jackfruit'}, {'id': 19689, 'synset': 'marang.n.01', 'name': 'marang'}, {'id': 19690, 'synset': 'fig_tree.n.01', 'name': 'fig_tree'}, {'id': 19691, 'synset': 'fig.n.02', 'name': 'fig'}, {'id': 19692, 'synset': 'caprifig.n.01', 'name': 'caprifig'}, {'id': 19693, 'synset': 'golden_fig.n.01', 'name': 'golden_fig'}, {'id': 19694, 'synset': 'banyan.n.01', 'name': 'banyan'}, {'id': 19695, 'synset': 'pipal.n.01', 'name': 'pipal'}, {'id': 19696, 'synset': 'india-rubber_tree.n.01', 'name': 'India-rubber_tree'}, {'id': 19697, 'synset': 'mistletoe_fig.n.01', 'name': 'mistletoe_fig'}, {'id': 19698, 'synset': 'port_jackson_fig.n.01', 'name': 'Port_Jackson_fig'}, {'id': 19699, 'synset': 'sycamore.n.04', 'name': 'sycamore'}, {'id': 19700, 'synset': 'paper_mulberry.n.01', 'name': 'paper_mulberry'}, {'id': 19701, 'synset': 'trumpetwood.n.01', 'name': 'trumpetwood'}, {'id': 19702, 'synset': 'elm.n.01', 'name': 'elm'}, {'id': 19703, 'synset': 'winged_elm.n.01', 'name': 'winged_elm'}, {'id': 19704, 'synset': 'american_elm.n.01', 'name': 'American_elm'}, {'id': 19705, 'synset': 'smooth-leaved_elm.n.01', 'name': 'smooth-leaved_elm'}, {'id': 19706, 'synset': 'cedar_elm.n.01', 'name': 'cedar_elm'}, {'id': 19707, 'synset': 'witch_elm.n.01', 'name': 'witch_elm'}, {'id': 19708, 'synset': 'dutch_elm.n.01', 'name': 'Dutch_elm'}, {'id': 19709, 'synset': 'huntingdon_elm.n.01', 'name': 'Huntingdon_elm'}, {'id': 19710, 'synset': 'water_elm.n.01', 'name': 'water_elm'}, {'id': 19711, 'synset': 'chinese_elm.n.02', 'name': 'Chinese_elm'}, {'id': 19712, 'synset': 'english_elm.n.01', 'name': 'English_elm'}, {'id': 19713, 'synset': 'siberian_elm.n.01', 'name': 'Siberian_elm'}, {'id': 19714, 'synset': 'slippery_elm.n.01', 'name': 'slippery_elm'}, {'id': 19715, 'synset': 'jersey_elm.n.01', 'name': 'Jersey_elm'}, {'id': 19716, 'synset': 'september_elm.n.01', 'name': 'September_elm'}, {'id': 19717, 'synset': 'rock_elm.n.01', 'name': 'rock_elm'}, {'id': 19718, 'synset': 'hackberry.n.01', 'name': 'hackberry'}, {'id': 19719, 'synset': 'european_hackberry.n.01', 'name': 'European_hackberry'}, {'id': 19720, 'synset': 'american_hackberry.n.01', 'name': 'American_hackberry'}, {'id': 19721, 'synset': 'sugarberry.n.01', 'name': 'sugarberry'}, {'id': 19722, 'synset': 'iridaceous_plant.n.01', 'name': 'iridaceous_plant'}, {'id': 19723, 'synset': 'bearded_iris.n.01', 'name': 'bearded_iris'}, {'id': 19724, 'synset': 'beardless_iris.n.01', 'name': 'beardless_iris'}, {'id': 19725, 'synset': 'orrisroot.n.01', 'name': 'orrisroot'}, {'id': 19726, 'synset': 'dwarf_iris.n.02', 'name': 'dwarf_iris'}, {'id': 19727, 'synset': 'dutch_iris.n.02', 'name': 'Dutch_iris'}, {'id': 19728, 'synset': 'florentine_iris.n.01', 'name': 'Florentine_iris'}, {'id': 19729, 'synset': 'stinking_iris.n.01', 'name': 'stinking_iris'}, {'id': 19730, 'synset': 'german_iris.n.02', 'name': 'German_iris'}, {'id': 19731, 'synset': 'japanese_iris.n.01', 'name': 'Japanese_iris'}, {'id': 19732, 'synset': 'german_iris.n.01', 'name': 'German_iris'}, {'id': 19733, 'synset': 'dalmatian_iris.n.01', 'name': 'Dalmatian_iris'}, {'id': 19734, 'synset': 'persian_iris.n.01', 'name': 'Persian_iris'}, {'id': 19735, 'synset': 'dutch_iris.n.01', 'name': 'Dutch_iris'}, {'id': 19736, 'synset': 'dwarf_iris.n.01', 'name': 'dwarf_iris'}, {'id': 19737, 'synset': 'spanish_iris.n.01', 'name': 'Spanish_iris'}, {'id': 19738, 'synset': 'blackberry-lily.n.01', 'name': 'blackberry-lily'}, {'id': 19739, 'synset': 'crocus.n.01', 'name': 'crocus'}, {'id': 19740, 'synset': 'saffron.n.01', 'name': 'saffron'}, {'id': 19741, 'synset': 'corn_lily.n.01', 'name': 'corn_lily'}, {'id': 19742, 'synset': 'blue-eyed_grass.n.01', 'name': 'blue-eyed_grass'}, {'id': 19743, 'synset': 'wandflower.n.01', 'name': 'wandflower'}, {'id': 19744, 'synset': 'amaryllis.n.01', 'name': 'amaryllis'}, {'id': 19745, 'synset': 'salsilla.n.02', 'name': 'salsilla'}, {'id': 19746, 'synset': 'salsilla.n.01', 'name': 'salsilla'}, {'id': 19747, 'synset': 'blood_lily.n.01', 'name': 'blood_lily'}, {'id': 19748, 'synset': 'cape_tulip.n.01', 'name': 'Cape_tulip'}, {'id': 19749, 'synset': 'hippeastrum.n.01', 'name': 'hippeastrum'}, {'id': 19750, 'synset': 'narcissus.n.01', 'name': 'narcissus'}, {'id': 19751, 'synset': 'daffodil.n.01', 'name': 'daffodil'}, {'id': 19752, 'synset': 'jonquil.n.01', 'name': 'jonquil'}, {'id': 19753, 'synset': 'jonquil.n.02', 'name': 'jonquil'}, {'id': 19754, 'synset': 'jacobean_lily.n.01', 'name': 'Jacobean_lily'}, {'id': 19755, 'synset': 'liliaceous_plant.n.01', 'name': 'liliaceous_plant'}, {'id': 19756, 'synset': 'mountain_lily.n.01', 'name': 'mountain_lily'}, {'id': 19757, 'synset': 'canada_lily.n.01', 'name': 'Canada_lily'}, {'id': 19758, 'synset': 'tiger_lily.n.02', 'name': 'tiger_lily'}, {'id': 19759, 'synset': 'columbia_tiger_lily.n.01', 'name': 'Columbia_tiger_lily'}, {'id': 19760, 'synset': 'tiger_lily.n.01', 'name': 'tiger_lily'}, {'id': 19761, 'synset': 'easter_lily.n.01', 'name': 'Easter_lily'}, {'id': 19762, 'synset': 'coast_lily.n.01', 'name': 'coast_lily'}, {'id': 19763, 'synset': "turk's-cap.n.02", 'name': "Turk's-cap"}, {'id': 19764, 'synset': 'michigan_lily.n.01', 'name': 'Michigan_lily'}, {'id': 19765, 'synset': 'leopard_lily.n.01', 'name': 'leopard_lily'}, {'id': 19766, 'synset': "turk's-cap.n.01", 'name': "Turk's-cap"}, {'id': 19767, 'synset': 'african_lily.n.01', 'name': 'African_lily'}, {'id': 19768, 'synset': 'colicroot.n.01', 'name': 'colicroot'}, {'id': 19769, 'synset': 'ague_root.n.01', 'name': 'ague_root'}, {'id': 19770, 'synset': 'yellow_colicroot.n.01', 'name': 'yellow_colicroot'}, {'id': 19771, 'synset': 'alliaceous_plant.n.01', 'name': 'alliaceous_plant'}, {'id': 19772, 'synset': "hooker's_onion.n.01", 'name': "Hooker's_onion"}, {'id': 19773, 'synset': 'wild_leek.n.02', 'name': 'wild_leek'}, {'id': 19774, 'synset': 'canada_garlic.n.01', 'name': 'Canada_garlic'}, {'id': 19775, 'synset': 'keeled_garlic.n.01', 'name': 'keeled_garlic'}, {'id': 19776, 'synset': 'shallot.n.02', 'name': 'shallot'}, {'id': 19777, 'synset': 'nodding_onion.n.01', 'name': 'nodding_onion'}, {'id': 19778, 'synset': 'welsh_onion.n.01', 'name': 'Welsh_onion'}, {'id': 19779, 'synset': 'red-skinned_onion.n.01', 'name': 'red-skinned_onion'}, {'id': 19780, 'synset': 'daffodil_garlic.n.01', 'name': 'daffodil_garlic'}, {'id': 19781, 'synset': 'few-flowered_leek.n.01', 'name': 'few-flowered_leek'}, {'id': 19782, 'synset': 'garlic.n.01', 'name': 'garlic'}, {'id': 19783, 'synset': 'sand_leek.n.01', 'name': 'sand_leek'}, {'id': 19784, 'synset': 'chives.n.01', 'name': 'chives'}, {'id': 19785, 'synset': 'crow_garlic.n.01', 'name': 'crow_garlic'}, {'id': 19786, 'synset': 'wild_garlic.n.01', 'name': 'wild_garlic'}, {'id': 19787, 'synset': 'garlic_chive.n.01', 'name': 'garlic_chive'}, {'id': 19788, 'synset': 'round-headed_leek.n.01', 'name': 'round-headed_leek'}, {'id': 19789, 'synset': 'three-cornered_leek.n.01', 'name': 'three-cornered_leek'}, {'id': 19790, 'synset': 'cape_aloe.n.01', 'name': 'cape_aloe'}, {'id': 19791, 'synset': 'kniphofia.n.01', 'name': 'kniphofia'}, {'id': 19792, 'synset': 'poker_plant.n.01', 'name': 'poker_plant'}, {'id': 19793, 'synset': 'red-hot_poker.n.01', 'name': 'red-hot_poker'}, {'id': 19794, 'synset': 'fly_poison.n.01', 'name': 'fly_poison'}, {'id': 19795, 'synset': 'amber_lily.n.01', 'name': 'amber_lily'}, {'id': 19796, 'synset': 'asparagus.n.01', 'name': 'asparagus'}, {'id': 19797, 'synset': 'asparagus_fern.n.01', 'name': 'asparagus_fern'}, {'id': 19798, 'synset': 'smilax.n.02', 'name': 'smilax'}, {'id': 19799, 'synset': 'asphodel.n.01', 'name': 'asphodel'}, {'id': 19800, 'synset': "jacob's_rod.n.01", 'name': "Jacob's_rod"}, {'id': 19801, 'synset': 'aspidistra.n.01', 'name': 'aspidistra'}, {'id': 19802, 'synset': 'coral_drops.n.01', 'name': 'coral_drops'}, {'id': 19803, 'synset': 'christmas_bells.n.01', 'name': 'Christmas_bells'}, {'id': 19804, 'synset': 'climbing_onion.n.01', 'name': 'climbing_onion'}, {'id': 19805, 'synset': 'mariposa.n.01', 'name': 'mariposa'}, {'id': 19806, 'synset': 'globe_lily.n.01', 'name': 'globe_lily'}, {'id': 19807, 'synset': "cat's-ear.n.01", 'name': "cat's-ear"}, {'id': 19808, 'synset': 'white_globe_lily.n.01', 'name': 'white_globe_lily'}, {'id': 19809, 'synset': 'yellow_globe_lily.n.01', 'name': 'yellow_globe_lily'}, {'id': 19810, 'synset': 'rose_globe_lily.n.01', 'name': 'rose_globe_lily'}, {'id': 19811, 'synset': 'star_tulip.n.01', 'name': 'star_tulip'}, {'id': 19812, 'synset': 'desert_mariposa_tulip.n.01', 'name': 'desert_mariposa_tulip'}, {'id': 19813, 'synset': 'yellow_mariposa_tulip.n.01', 'name': 'yellow_mariposa_tulip'}, {'id': 19814, 'synset': 'sagebrush_mariposa_tulip.n.01', 'name': 'sagebrush_mariposa_tulip'}, {'id': 19815, 'synset': 'sego_lily.n.01', 'name': 'sego_lily'}, {'id': 19816, 'synset': 'camas.n.01', 'name': 'camas'}, {'id': 19817, 'synset': 'common_camas.n.01', 'name': 'common_camas'}, {'id': 19818, 'synset': "leichtlin's_camas.n.01", 'name': "Leichtlin's_camas"}, {'id': 19819, 'synset': 'wild_hyacinth.n.02', 'name': 'wild_hyacinth'}, {'id': 19820, 'synset': 'dogtooth_violet.n.01', 'name': 'dogtooth_violet'}, {'id': 19821, 'synset': 'white_dogtooth_violet.n.01', 'name': 'white_dogtooth_violet'}, {'id': 19822, 'synset': "yellow_adder's_tongue.n.01", 'name': "yellow_adder's_tongue"}, {'id': 19823, 'synset': 'european_dogtooth.n.01', 'name': 'European_dogtooth'}, {'id': 19824, 'synset': 'fawn_lily.n.01', 'name': 'fawn_lily'}, {'id': 19825, 'synset': 'glacier_lily.n.01', 'name': 'glacier_lily'}, {'id': 19826, 'synset': 'avalanche_lily.n.01', 'name': 'avalanche_lily'}, {'id': 19827, 'synset': 'fritillary.n.01', 'name': 'fritillary'}, {'id': 19828, 'synset': 'mission_bells.n.02', 'name': 'mission_bells'}, {'id': 19829, 'synset': 'mission_bells.n.01', 'name': 'mission_bells'}, {'id': 19830, 'synset': 'stink_bell.n.01', 'name': 'stink_bell'}, {'id': 19831, 'synset': 'crown_imperial.n.01', 'name': 'crown_imperial'}, {'id': 19832, 'synset': 'white_fritillary.n.01', 'name': 'white_fritillary'}, {'id': 19833, 'synset': "snake's_head_fritillary.n.01", 'name': "snake's_head_fritillary"}, {'id': 19834, 'synset': 'adobe_lily.n.01', 'name': 'adobe_lily'}, {'id': 19835, 'synset': 'scarlet_fritillary.n.01', 'name': 'scarlet_fritillary'}, {'id': 19836, 'synset': 'tulip.n.01', 'name': 'tulip'}, {'id': 19837, 'synset': 'dwarf_tulip.n.01', 'name': 'dwarf_tulip'}, {'id': 19838, 'synset': 'lady_tulip.n.01', 'name': 'lady_tulip'}, {'id': 19839, 'synset': 'tulipa_gesneriana.n.01', 'name': 'Tulipa_gesneriana'}, {'id': 19840, 'synset': 'cottage_tulip.n.01', 'name': 'cottage_tulip'}, {'id': 19841, 'synset': 'darwin_tulip.n.01', 'name': 'Darwin_tulip'}, {'id': 19842, 'synset': 'gloriosa.n.01', 'name': 'gloriosa'}, {'id': 19843, 'synset': 'lemon_lily.n.01', 'name': 'lemon_lily'}, {'id': 19844, 'synset': 'common_hyacinth.n.01', 'name': 'common_hyacinth'}, {'id': 19845, 'synset': 'roman_hyacinth.n.01', 'name': 'Roman_hyacinth'}, {'id': 19846, 'synset': 'summer_hyacinth.n.01', 'name': 'summer_hyacinth'}, {'id': 19847, 'synset': 'star-of-bethlehem.n.01', 'name': 'star-of-Bethlehem'}, {'id': 19848, 'synset': 'bath_asparagus.n.01', 'name': 'bath_asparagus'}, {'id': 19849, 'synset': 'grape_hyacinth.n.01', 'name': 'grape_hyacinth'}, {'id': 19850, 'synset': 'common_grape_hyacinth.n.01', 'name': 'common_grape_hyacinth'}, {'id': 19851, 'synset': 'tassel_hyacinth.n.01', 'name': 'tassel_hyacinth'}, {'id': 19852, 'synset': 'scilla.n.01', 'name': 'scilla'}, {'id': 19853, 'synset': 'spring_squill.n.01', 'name': 'spring_squill'}, {'id': 19854, 'synset': 'false_asphodel.n.01', 'name': 'false_asphodel'}, {'id': 19855, 'synset': 'scotch_asphodel.n.01', 'name': 'Scotch_asphodel'}, {'id': 19856, 'synset': 'sea_squill.n.01', 'name': 'sea_squill'}, {'id': 19857, 'synset': 'squill.n.01', 'name': 'squill'}, {'id': 19858, 'synset': "butcher's_broom.n.01", 'name': "butcher's_broom"}, {'id': 19859, 'synset': 'bog_asphodel.n.01', 'name': 'bog_asphodel'}, {'id': 19860, 'synset': 'european_bog_asphodel.n.01', 'name': 'European_bog_asphodel'}, {'id': 19861, 'synset': 'american_bog_asphodel.n.01', 'name': 'American_bog_asphodel'}, {'id': 19862, 'synset': 'hellebore.n.01', 'name': 'hellebore'}, {'id': 19863, 'synset': 'white_hellebore.n.01', 'name': 'white_hellebore'}, {'id': 19864, 'synset': 'squaw_grass.n.01', 'name': 'squaw_grass'}, {'id': 19865, 'synset': 'death_camas.n.01', 'name': 'death_camas'}, {'id': 19866, 'synset': 'alkali_grass.n.01', 'name': 'alkali_grass'}, {'id': 19867, 'synset': 'white_camas.n.01', 'name': 'white_camas'}, {'id': 19868, 'synset': 'poison_camas.n.01', 'name': 'poison_camas'}, {'id': 19869, 'synset': 'grassy_death_camas.n.01', 'name': 'grassy_death_camas'}, {'id': 19870, 'synset': 'prairie_wake-robin.n.01', 'name': 'prairie_wake-robin'}, {'id': 19871, 'synset': 'dwarf-white_trillium.n.01', 'name': 'dwarf-white_trillium'}, {'id': 19872, 'synset': 'herb_paris.n.01', 'name': 'herb_Paris'}, {'id': 19873, 'synset': 'sarsaparilla.n.01', 'name': 'sarsaparilla'}, {'id': 19874, 'synset': 'bullbrier.n.01', 'name': 'bullbrier'}, {'id': 19875, 'synset': 'rough_bindweed.n.01', 'name': 'rough_bindweed'}, {'id': 19876, 'synset': 'clintonia.n.01', 'name': 'clintonia'}, {'id': 19877, 'synset': 'false_lily_of_the_valley.n.02', 'name': 'false_lily_of_the_valley'}, {'id': 19878, 'synset': 'false_lily_of_the_valley.n.01', 'name': 'false_lily_of_the_valley'}, {'id': 19879, 'synset': "solomon's-seal.n.01", 'name': "Solomon's-seal"}, {'id': 19880, 'synset': "great_solomon's-seal.n.01", 'name': "great_Solomon's-seal"}, {'id': 19881, 'synset': 'bellwort.n.01', 'name': 'bellwort'}, {'id': 19882, 'synset': 'strawflower.n.01', 'name': 'strawflower'}, {'id': 19883, 'synset': 'pia.n.01', 'name': 'pia'}, {'id': 19884, 'synset': 'agave.n.01', 'name': 'agave'}, {'id': 19885, 'synset': 'american_agave.n.01', 'name': 'American_agave'}, {'id': 19886, 'synset': 'sisal.n.02', 'name': 'sisal'}, {'id': 19887, 'synset': 'maguey.n.02', 'name': 'maguey'}, {'id': 19888, 'synset': 'maguey.n.01', 'name': 'maguey'}, {'id': 19889, 'synset': 'agave_tequilana.n.01', 'name': 'Agave_tequilana'}, {'id': 19890, 'synset': 'cabbage_tree.n.03', 'name': 'cabbage_tree'}, {'id': 19891, 'synset': 'dracaena.n.01', 'name': 'dracaena'}, {'id': 19892, 'synset': 'tuberose.n.01', 'name': 'tuberose'}, {'id': 19893, 'synset': 'sansevieria.n.01', 'name': 'sansevieria'}, {'id': 19894, 'synset': 'african_bowstring_hemp.n.01', 'name': 'African_bowstring_hemp'}, {'id': 19895, 'synset': 'ceylon_bowstring_hemp.n.01', 'name': 'Ceylon_bowstring_hemp'}, {'id': 19896, 'synset': "mother-in-law's_tongue.n.01", 'name': "mother-in-law's_tongue"}, {'id': 19897, 'synset': 'spanish_bayonet.n.02', 'name': 'Spanish_bayonet'}, {'id': 19898, 'synset': 'spanish_bayonet.n.01', 'name': 'Spanish_bayonet'}, {'id': 19899, 'synset': 'joshua_tree.n.01', 'name': 'Joshua_tree'}, {'id': 19900, 'synset': 'soapweed.n.01', 'name': 'soapweed'}, {'id': 19901, 'synset': "adam's_needle.n.01", 'name': "Adam's_needle"}, {'id': 19902, 'synset': 'bear_grass.n.02', 'name': 'bear_grass'}, {'id': 19903, 'synset': 'spanish_dagger.n.01', 'name': 'Spanish_dagger'}, {'id': 19904, 'synset': "our_lord's_candle.n.01", 'name': "Our_Lord's_candle"}, {'id': 19905, 'synset': 'water_shamrock.n.01', 'name': 'water_shamrock'}, {'id': 19906, 'synset': 'butterfly_bush.n.01', 'name': 'butterfly_bush'}, {'id': 19907, 'synset': 'yellow_jasmine.n.01', 'name': 'yellow_jasmine'}, {'id': 19908, 'synset': 'flax.n.02', 'name': 'flax'}, {'id': 19909, 'synset': 'calabar_bean.n.01', 'name': 'calabar_bean'}, {'id': 19910, 'synset': 'bonduc.n.02', 'name': 'bonduc'}, {'id': 19911, 'synset': 'divi-divi.n.02', 'name': 'divi-divi'}, {'id': 19912, 'synset': 'mysore_thorn.n.01', 'name': 'Mysore_thorn'}, {'id': 19913, 'synset': 'brazilian_ironwood.n.01', 'name': 'brazilian_ironwood'}, {'id': 19914, 'synset': 'bird_of_paradise.n.01', 'name': 'bird_of_paradise'}, {'id': 19915, 'synset': 'shingle_tree.n.01', 'name': 'shingle_tree'}, {'id': 19916, 'synset': 'mountain_ebony.n.01', 'name': 'mountain_ebony'}, {'id': 19917, 'synset': 'msasa.n.01', 'name': 'msasa'}, {'id': 19918, 'synset': 'cassia.n.01', 'name': 'cassia'}, {'id': 19919, 'synset': 'golden_shower_tree.n.01', 'name': 'golden_shower_tree'}, {'id': 19920, 'synset': 'pink_shower.n.01', 'name': 'pink_shower'}, {'id': 19921, 'synset': 'rainbow_shower.n.01', 'name': 'rainbow_shower'}, {'id': 19922, 'synset': 'horse_cassia.n.01', 'name': 'horse_cassia'}, {'id': 19923, 'synset': 'carob.n.02', 'name': 'carob'}, {'id': 19924, 'synset': 'carob.n.01', 'name': 'carob'}, {'id': 19925, 'synset': 'paloverde.n.01', 'name': 'paloverde'}, {'id': 19926, 'synset': 'royal_poinciana.n.01', 'name': 'royal_poinciana'}, {'id': 19927, 'synset': 'locust_tree.n.01', 'name': 'locust_tree'}, {'id': 19928, 'synset': 'water_locust.n.01', 'name': 'water_locust'}, {'id': 19929, 'synset': 'honey_locust.n.01', 'name': 'honey_locust'}, {'id': 19930, 'synset': 'kentucky_coffee_tree.n.01', 'name': 'Kentucky_coffee_tree'}, {'id': 19931, 'synset': 'logwood.n.02', 'name': 'logwood'}, {'id': 19932, 'synset': 'jerusalem_thorn.n.03', 'name': 'Jerusalem_thorn'}, {'id': 19933, 'synset': 'palo_verde.n.01', 'name': 'palo_verde'}, {'id': 19934, 'synset': 'dalmatian_laburnum.n.01', 'name': 'Dalmatian_laburnum'}, {'id': 19935, 'synset': 'senna.n.01', 'name': 'senna'}, {'id': 19936, 'synset': 'avaram.n.01', 'name': 'avaram'}, {'id': 19937, 'synset': 'alexandria_senna.n.01', 'name': 'Alexandria_senna'}, {'id': 19938, 'synset': 'wild_senna.n.01', 'name': 'wild_senna'}, {'id': 19939, 'synset': 'sicklepod.n.01', 'name': 'sicklepod'}, {'id': 19940, 'synset': 'coffee_senna.n.01', 'name': 'coffee_senna'}, {'id': 19941, 'synset': 'tamarind.n.01', 'name': 'tamarind'}, {'id': 19942, 'synset': 'false_indigo.n.03', 'name': 'false_indigo'}, {'id': 19943, 'synset': 'false_indigo.n.02', 'name': 'false_indigo'}, {'id': 19944, 'synset': 'hog_peanut.n.01', 'name': 'hog_peanut'}, {'id': 19945, 'synset': 'angelim.n.01', 'name': 'angelim'}, {'id': 19946, 'synset': 'cabbage_bark.n.01', 'name': 'cabbage_bark'}, {'id': 19947, 'synset': 'kidney_vetch.n.01', 'name': 'kidney_vetch'}, {'id': 19948, 'synset': 'groundnut.n.01', 'name': 'groundnut'}, {'id': 19949, 'synset': 'rooibos.n.01', 'name': 'rooibos'}, {'id': 19950, 'synset': 'milk_vetch.n.01', 'name': 'milk_vetch'}, {'id': 19951, 'synset': 'alpine_milk_vetch.n.01', 'name': 'alpine_milk_vetch'}, {'id': 19952, 'synset': 'purple_milk_vetch.n.01', 'name': 'purple_milk_vetch'}, {'id': 19953, 'synset': 'camwood.n.01', 'name': 'camwood'}, {'id': 19954, 'synset': 'wild_indigo.n.01', 'name': 'wild_indigo'}, {'id': 19955, 'synset': 'blue_false_indigo.n.01', 'name': 'blue_false_indigo'}, {'id': 19956, 'synset': 'white_false_indigo.n.01', 'name': 'white_false_indigo'}, {'id': 19957, 'synset': 'indigo_broom.n.01', 'name': 'indigo_broom'}, {'id': 19958, 'synset': 'dhak.n.01', 'name': 'dhak'}, {'id': 19959, 'synset': 'pigeon_pea.n.01', 'name': 'pigeon_pea'}, {'id': 19960, 'synset': 'sword_bean.n.01', 'name': 'sword_bean'}, {'id': 19961, 'synset': 'pea_tree.n.01', 'name': 'pea_tree'}, {'id': 19962, 'synset': 'siberian_pea_tree.n.01', 'name': 'Siberian_pea_tree'}, {'id': 19963, 'synset': 'chinese_pea_tree.n.01', 'name': 'Chinese_pea_tree'}, {'id': 19964, 'synset': 'moreton_bay_chestnut.n.01', 'name': 'Moreton_Bay_chestnut'}, {'id': 19965, 'synset': 'butterfly_pea.n.03', 'name': 'butterfly_pea'}, {'id': 19966, 'synset': 'judas_tree.n.01', 'name': 'Judas_tree'}, {'id': 19967, 'synset': 'redbud.n.01', 'name': 'redbud'}, {'id': 19968, 'synset': 'western_redbud.n.01', 'name': 'western_redbud'}, {'id': 19969, 'synset': 'tagasaste.n.01', 'name': 'tagasaste'}, {'id': 19970, 'synset': 'weeping_tree_broom.n.01', 'name': 'weeping_tree_broom'}, {'id': 19971, 'synset': 'flame_pea.n.01', 'name': 'flame_pea'}, {'id': 19972, 'synset': 'chickpea.n.02', 'name': 'chickpea'}, {'id': 19973, 'synset': 'kentucky_yellowwood.n.01', 'name': 'Kentucky_yellowwood'}, {'id': 19974, 'synset': 'glory_pea.n.01', 'name': 'glory_pea'}, {'id': 19975, 'synset': 'desert_pea.n.01', 'name': 'desert_pea'}, {'id': 19976, 'synset': "parrot's_beak.n.01", 'name': "parrot's_beak"}, {'id': 19977, 'synset': 'butterfly_pea.n.02', 'name': 'butterfly_pea'}, {'id': 19978, 'synset': 'blue_pea.n.01', 'name': 'blue_pea'}, {'id': 19979, 'synset': 'telegraph_plant.n.01', 'name': 'telegraph_plant'}, {'id': 19980, 'synset': 'bladder_senna.n.01', 'name': 'bladder_senna'}, {'id': 19981, 'synset': 'axseed.n.01', 'name': 'axseed'}, {'id': 19982, 'synset': 'crotalaria.n.01', 'name': 'crotalaria'}, {'id': 19983, 'synset': 'guar.n.01', 'name': 'guar'}, {'id': 19984, 'synset': 'white_broom.n.01', 'name': 'white_broom'}, {'id': 19985, 'synset': 'common_broom.n.01', 'name': 'common_broom'}, {'id': 19986, 'synset': 'rosewood.n.02', 'name': 'rosewood'}, {'id': 19987, 'synset': 'indian_blackwood.n.01', 'name': 'Indian_blackwood'}, {'id': 19988, 'synset': 'sissoo.n.01', 'name': 'sissoo'}, {'id': 19989, 'synset': 'kingwood.n.02', 'name': 'kingwood'}, {'id': 19990, 'synset': 'brazilian_rosewood.n.01', 'name': 'Brazilian_rosewood'}, {'id': 19991, 'synset': 'cocobolo.n.01', 'name': 'cocobolo'}, {'id': 19992, 'synset': 'blackwood.n.02', 'name': 'blackwood'}, {'id': 19993, 'synset': 'bitter_pea.n.01', 'name': 'bitter_pea'}, {'id': 19994, 'synset': 'derris.n.01', 'name': 'derris'}, {'id': 19995, 'synset': 'derris_root.n.01', 'name': 'derris_root'}, {'id': 19996, 'synset': 'prairie_mimosa.n.01', 'name': 'prairie_mimosa'}, {'id': 19997, 'synset': 'tick_trefoil.n.01', 'name': 'tick_trefoil'}, {'id': 19998, 'synset': 'beggarweed.n.01', 'name': 'beggarweed'}, {'id': 19999, 'synset': 'australian_pea.n.01', 'name': 'Australian_pea'}, {'id': 20000, 'synset': 'coral_tree.n.01', 'name': 'coral_tree'}, {'id': 20001, 'synset': 'kaffir_boom.n.02', 'name': 'kaffir_boom'}, {'id': 20002, 'synset': 'coral_bean_tree.n.01', 'name': 'coral_bean_tree'}, {'id': 20003, 'synset': 'ceibo.n.01', 'name': 'ceibo'}, {'id': 20004, 'synset': 'kaffir_boom.n.01', 'name': 'kaffir_boom'}, {'id': 20005, 'synset': 'indian_coral_tree.n.01', 'name': 'Indian_coral_tree'}, {'id': 20006, 'synset': 'cork_tree.n.02', 'name': 'cork_tree'}, {'id': 20007, 'synset': "goat's_rue.n.02", 'name': "goat's_rue"}, {'id': 20008, 'synset': 'poison_bush.n.01', 'name': 'poison_bush'}, {'id': 20009, 'synset': 'spanish_broom.n.02', 'name': 'Spanish_broom'}, {'id': 20010, 'synset': 'woodwaxen.n.01', 'name': 'woodwaxen'}, {'id': 20011, 'synset': 'chanar.n.01', 'name': 'chanar'}, {'id': 20012, 'synset': 'gliricidia.n.01', 'name': 'gliricidia'}, {'id': 20013, 'synset': 'soy.n.01', 'name': 'soy'}, {'id': 20014, 'synset': 'licorice.n.01', 'name': 'licorice'}, {'id': 20015, 'synset': 'wild_licorice.n.02', 'name': 'wild_licorice'}, {'id': 20016, 'synset': 'licorice_root.n.01', 'name': 'licorice_root'}, {'id': 20017, 'synset': 'western_australia_coral_pea.n.01', 'name': 'Western_Australia_coral_pea'}, {'id': 20018, 'synset': 'sweet_vetch.n.01', 'name': 'sweet_vetch'}, {'id': 20019, 'synset': 'french_honeysuckle.n.02', 'name': 'French_honeysuckle'}, {'id': 20020, 'synset': 'anil.n.02', 'name': 'anil'}, {'id': 20021, 'synset': 'scarlet_runner.n.02', 'name': 'scarlet_runner'}, {'id': 20022, 'synset': 'hyacinth_bean.n.01', 'name': 'hyacinth_bean'}, {'id': 20023, 'synset': 'scotch_laburnum.n.01', 'name': 'Scotch_laburnum'}, {'id': 20024, 'synset': 'vetchling.n.01', 'name': 'vetchling'}, {'id': 20025, 'synset': 'wild_pea.n.01', 'name': 'wild_pea'}, {'id': 20026, 'synset': 'everlasting_pea.n.01', 'name': 'everlasting_pea'}, {'id': 20027, 'synset': 'beach_pea.n.01', 'name': 'beach_pea'}, {'id': 20028, 'synset': 'grass_vetch.n.01', 'name': 'grass_vetch'}, {'id': 20029, 'synset': 'marsh_pea.n.01', 'name': 'marsh_pea'}, {'id': 20030, 'synset': 'common_vetchling.n.01', 'name': 'common_vetchling'}, {'id': 20031, 'synset': 'grass_pea.n.01', 'name': 'grass_pea'}, {'id': 20032, 'synset': 'tangier_pea.n.01', 'name': 'Tangier_pea'}, {'id': 20033, 'synset': 'heath_pea.n.01', 'name': 'heath_pea'}, {'id': 20034, 'synset': 'bicolor_lespediza.n.01', 'name': 'bicolor_lespediza'}, {'id': 20035, 'synset': 'japanese_clover.n.01', 'name': 'japanese_clover'}, {'id': 20036, 'synset': 'korean_lespedeza.n.01', 'name': 'Korean_lespedeza'}, {'id': 20037, 'synset': 'sericea_lespedeza.n.01', 'name': 'sericea_lespedeza'}, {'id': 20038, 'synset': 'lentil.n.03', 'name': 'lentil'}, {'id': 20039, 'synset': 'lentil.n.02', 'name': 'lentil'}, {'id': 20040, 'synset': "prairie_bird's-foot_trefoil.n.01", 'name': "prairie_bird's-foot_trefoil"}, {'id': 20041, 'synset': "bird's_foot_trefoil.n.02", 'name': "bird's_foot_trefoil"}, {'id': 20042, 'synset': 'winged_pea.n.02', 'name': 'winged_pea'}, {'id': 20043, 'synset': 'lupine.n.01', 'name': 'lupine'}, {'id': 20044, 'synset': 'white_lupine.n.01', 'name': 'white_lupine'}, {'id': 20045, 'synset': 'tree_lupine.n.01', 'name': 'tree_lupine'}, {'id': 20046, 'synset': 'wild_lupine.n.01', 'name': 'wild_lupine'}, {'id': 20047, 'synset': 'bluebonnet.n.01', 'name': 'bluebonnet'}, {'id': 20048, 'synset': 'texas_bluebonnet.n.01', 'name': 'Texas_bluebonnet'}, {'id': 20049, 'synset': 'medic.n.01', 'name': 'medic'}, {'id': 20050, 'synset': 'moon_trefoil.n.01', 'name': 'moon_trefoil'}, {'id': 20051, 'synset': 'sickle_alfalfa.n.01', 'name': 'sickle_alfalfa'}, {'id': 20052, 'synset': 'calvary_clover.n.01', 'name': 'Calvary_clover'}, {'id': 20053, 'synset': 'black_medick.n.01', 'name': 'black_medick'}, {'id': 20054, 'synset': 'alfalfa.n.01', 'name': 'alfalfa'}, {'id': 20055, 'synset': 'millettia.n.01', 'name': 'millettia'}, {'id': 20056, 'synset': 'mucuna.n.01', 'name': 'mucuna'}, {'id': 20057, 'synset': 'cowage.n.02', 'name': 'cowage'}, {'id': 20058, 'synset': 'tolu_tree.n.01', 'name': 'tolu_tree'}, {'id': 20059, 'synset': 'peruvian_balsam.n.01', 'name': 'Peruvian_balsam'}, {'id': 20060, 'synset': 'sainfoin.n.01', 'name': 'sainfoin'}, {'id': 20061, 'synset': 'restharrow.n.02', 'name': 'restharrow'}, {'id': 20062, 'synset': 'bead_tree.n.01', 'name': 'bead_tree'}, {'id': 20063, 'synset': 'jumby_bead.n.01', 'name': 'jumby_bead'}, {'id': 20064, 'synset': 'locoweed.n.01', 'name': 'locoweed'}, {'id': 20065, 'synset': 'purple_locoweed.n.01', 'name': 'purple_locoweed'}, {'id': 20066, 'synset': 'tumbleweed.n.01', 'name': 'tumbleweed'}, {'id': 20067, 'synset': 'yam_bean.n.02', 'name': 'yam_bean'}, {'id': 20068, 'synset': 'shamrock_pea.n.01', 'name': 'shamrock_pea'}, {'id': 20069, 'synset': 'pole_bean.n.01', 'name': 'pole_bean'}, {'id': 20070, 'synset': 'kidney_bean.n.01', 'name': 'kidney_bean'}, {'id': 20071, 'synset': 'haricot.n.01', 'name': 'haricot'}, {'id': 20072, 'synset': 'wax_bean.n.01', 'name': 'wax_bean'}, {'id': 20073, 'synset': 'scarlet_runner.n.01', 'name': 'scarlet_runner'}, {'id': 20074, 'synset': 'lima_bean.n.02', 'name': 'lima_bean'}, {'id': 20075, 'synset': 'sieva_bean.n.01', 'name': 'sieva_bean'}, {'id': 20076, 'synset': 'tepary_bean.n.01', 'name': 'tepary_bean'}, {'id': 20077, 'synset': 'chaparral_pea.n.01', 'name': 'chaparral_pea'}, {'id': 20078, 'synset': 'jamaica_dogwood.n.01', 'name': 'Jamaica_dogwood'}, {'id': 20079, 'synset': 'pea.n.02', 'name': 'pea'}, {'id': 20080, 'synset': 'garden_pea.n.01', 'name': 'garden_pea'}, {'id': 20081, 'synset': 'edible-pod_pea.n.01', 'name': 'edible-pod_pea'}, {'id': 20082, 'synset': 'sugar_snap_pea.n.01', 'name': 'sugar_snap_pea'}, {'id': 20083, 'synset': 'field_pea.n.02', 'name': 'field_pea'}, {'id': 20084, 'synset': 'field_pea.n.01', 'name': 'field_pea'}, {'id': 20085, 'synset': 'common_flat_pea.n.01', 'name': 'common_flat_pea'}, {'id': 20086, 'synset': 'quira.n.02', 'name': 'quira'}, {'id': 20087, 'synset': 'roble.n.01', 'name': 'roble'}, {'id': 20088, 'synset': 'panama_redwood_tree.n.01', 'name': 'Panama_redwood_tree'}, {'id': 20089, 'synset': 'indian_beech.n.01', 'name': 'Indian_beech'}, {'id': 20090, 'synset': 'winged_bean.n.01', 'name': 'winged_bean'}, {'id': 20091, 'synset': 'breadroot.n.01', 'name': 'breadroot'}, {'id': 20092, 'synset': 'bloodwood_tree.n.01', 'name': 'bloodwood_tree'}, {'id': 20093, 'synset': 'kino.n.02', 'name': 'kino'}, {'id': 20094, 'synset': 'red_sandalwood.n.02', 'name': 'red_sandalwood'}, {'id': 20095, 'synset': 'kudzu.n.01', 'name': 'kudzu'}, {'id': 20096, 'synset': 'bristly_locust.n.01', 'name': 'bristly_locust'}, {'id': 20097, 'synset': 'black_locust.n.02', 'name': 'black_locust'}, {'id': 20098, 'synset': 'clammy_locust.n.01', 'name': 'clammy_locust'}, {'id': 20099, 'synset': 'carib_wood.n.01', 'name': 'carib_wood'}, {'id': 20100, 'synset': 'colorado_river_hemp.n.01', 'name': 'Colorado_River_hemp'}, {'id': 20101, 'synset': 'scarlet_wisteria_tree.n.01', 'name': 'scarlet_wisteria_tree'}, {'id': 20102, 'synset': 'japanese_pagoda_tree.n.01', 'name': 'Japanese_pagoda_tree'}, {'id': 20103, 'synset': 'mescal_bean.n.01', 'name': 'mescal_bean'}, {'id': 20104, 'synset': 'kowhai.n.01', 'name': 'kowhai'}, {'id': 20105, 'synset': 'jade_vine.n.01', 'name': 'jade_vine'}, {'id': 20106, 'synset': 'hoary_pea.n.01', 'name': 'hoary_pea'}, {'id': 20107, 'synset': 'bastard_indigo.n.01', 'name': 'bastard_indigo'}, {'id': 20108, 'synset': 'catgut.n.01', 'name': 'catgut'}, {'id': 20109, 'synset': 'bush_pea.n.01', 'name': 'bush_pea'}, {'id': 20110, 'synset': 'false_lupine.n.01', 'name': 'false_lupine'}, {'id': 20111, 'synset': 'carolina_lupine.n.01', 'name': 'Carolina_lupine'}, {'id': 20112, 'synset': 'tipu.n.01', 'name': 'tipu'}, {'id': 20113, 'synset': "bird's_foot_trefoil.n.01", 'name': "bird's_foot_trefoil"}, {'id': 20114, 'synset': 'fenugreek.n.01', 'name': 'fenugreek'}, {'id': 20115, 'synset': 'gorse.n.01', 'name': 'gorse'}, {'id': 20116, 'synset': 'vetch.n.01', 'name': 'vetch'}, {'id': 20117, 'synset': 'tufted_vetch.n.01', 'name': 'tufted_vetch'}, {'id': 20118, 'synset': 'broad_bean.n.01', 'name': 'broad_bean'}, {'id': 20119, 'synset': 'bitter_betch.n.01', 'name': 'bitter_betch'}, {'id': 20120, 'synset': 'bush_vetch.n.01', 'name': 'bush_vetch'}, {'id': 20121, 'synset': 'moth_bean.n.01', 'name': 'moth_bean'}, {'id': 20122, 'synset': 'snailflower.n.01', 'name': 'snailflower'}, {'id': 20123, 'synset': 'mung.n.01', 'name': 'mung'}, {'id': 20124, 'synset': 'cowpea.n.02', 'name': 'cowpea'}, {'id': 20125, 'synset': 'cowpea.n.01', 'name': 'cowpea'}, {'id': 20126, 'synset': 'asparagus_bean.n.01', 'name': 'asparagus_bean'}, {'id': 20127, 'synset': 'swamp_oak.n.01', 'name': 'swamp_oak'}, {'id': 20128, 'synset': 'keurboom.n.02', 'name': 'keurboom'}, {'id': 20129, 'synset': 'keurboom.n.01', 'name': 'keurboom'}, {'id': 20130, 'synset': 'japanese_wistaria.n.01', 'name': 'Japanese_wistaria'}, {'id': 20131, 'synset': 'chinese_wistaria.n.01', 'name': 'Chinese_wistaria'}, {'id': 20132, 'synset': 'american_wistaria.n.01', 'name': 'American_wistaria'}, {'id': 20133, 'synset': 'silky_wisteria.n.01', 'name': 'silky_wisteria'}, {'id': 20134, 'synset': 'palm.n.03', 'name': 'palm'}, {'id': 20135, 'synset': 'sago_palm.n.01', 'name': 'sago_palm'}, {'id': 20136, 'synset': 'feather_palm.n.01', 'name': 'feather_palm'}, {'id': 20137, 'synset': 'fan_palm.n.01', 'name': 'fan_palm'}, {'id': 20138, 'synset': 'palmetto.n.01', 'name': 'palmetto'}, {'id': 20139, 'synset': 'coyol.n.01', 'name': 'coyol'}, {'id': 20140, 'synset': 'grugru.n.01', 'name': 'grugru'}, {'id': 20141, 'synset': 'areca.n.01', 'name': 'areca'}, {'id': 20142, 'synset': 'betel_palm.n.01', 'name': 'betel_palm'}, {'id': 20143, 'synset': 'sugar_palm.n.01', 'name': 'sugar_palm'}, {'id': 20144, 'synset': 'piassava_palm.n.01', 'name': 'piassava_palm'}, {'id': 20145, 'synset': 'coquilla_nut.n.01', 'name': 'coquilla_nut'}, {'id': 20146, 'synset': 'palmyra.n.01', 'name': 'palmyra'}, {'id': 20147, 'synset': 'calamus.n.01', 'name': 'calamus'}, {'id': 20148, 'synset': 'rattan.n.01', 'name': 'rattan'}, {'id': 20149, 'synset': 'lawyer_cane.n.01', 'name': 'lawyer_cane'}, {'id': 20150, 'synset': 'fishtail_palm.n.01', 'name': 'fishtail_palm'}, {'id': 20151, 'synset': 'wine_palm.n.01', 'name': 'wine_palm'}, {'id': 20152, 'synset': 'wax_palm.n.03', 'name': 'wax_palm'}, {'id': 20153, 'synset': 'coconut.n.03', 'name': 'coconut'}, {'id': 20154, 'synset': 'carnauba.n.02', 'name': 'carnauba'}, {'id': 20155, 'synset': 'caranday.n.01', 'name': 'caranday'}, {'id': 20156, 'synset': 'corozo.n.01', 'name': 'corozo'}, {'id': 20157, 'synset': 'gebang_palm.n.01', 'name': 'gebang_palm'}, {'id': 20158, 'synset': 'latanier.n.01', 'name': 'latanier'}, {'id': 20159, 'synset': 'talipot.n.01', 'name': 'talipot'}, {'id': 20160, 'synset': 'oil_palm.n.01', 'name': 'oil_palm'}, {'id': 20161, 'synset': 'african_oil_palm.n.01', 'name': 'African_oil_palm'}, {'id': 20162, 'synset': 'american_oil_palm.n.01', 'name': 'American_oil_palm'}, {'id': 20163, 'synset': 'palm_nut.n.01', 'name': 'palm_nut'}, {'id': 20164, 'synset': 'cabbage_palm.n.04', 'name': 'cabbage_palm'}, {'id': 20165, 'synset': 'cabbage_palm.n.03', 'name': 'cabbage_palm'}, {'id': 20166, 'synset': 'true_sago_palm.n.01', 'name': 'true_sago_palm'}, {'id': 20167, 'synset': 'nipa_palm.n.01', 'name': 'nipa_palm'}, {'id': 20168, 'synset': 'babassu.n.01', 'name': 'babassu'}, {'id': 20169, 'synset': 'babassu_nut.n.01', 'name': 'babassu_nut'}, {'id': 20170, 'synset': 'cohune_palm.n.01', 'name': 'cohune_palm'}, {'id': 20171, 'synset': 'cohune_nut.n.01', 'name': 'cohune_nut'}, {'id': 20172, 'synset': 'date_palm.n.01', 'name': 'date_palm'}, {'id': 20173, 'synset': 'ivory_palm.n.01', 'name': 'ivory_palm'}, {'id': 20174, 'synset': 'raffia_palm.n.01', 'name': 'raffia_palm'}, {'id': 20175, 'synset': 'bamboo_palm.n.02', 'name': 'bamboo_palm'}, {'id': 20176, 'synset': 'lady_palm.n.01', 'name': 'lady_palm'}, {'id': 20177, 'synset': 'miniature_fan_palm.n.01', 'name': 'miniature_fan_palm'}, {'id': 20178, 'synset': 'reed_rhapis.n.01', 'name': 'reed_rhapis'}, {'id': 20179, 'synset': 'royal_palm.n.01', 'name': 'royal_palm'}, {'id': 20180, 'synset': 'cabbage_palm.n.02', 'name': 'cabbage_palm'}, {'id': 20181, 'synset': 'cabbage_palmetto.n.01', 'name': 'cabbage_palmetto'}, {'id': 20182, 'synset': 'saw_palmetto.n.01', 'name': 'saw_palmetto'}, {'id': 20183, 'synset': 'thatch_palm.n.01', 'name': 'thatch_palm'}, {'id': 20184, 'synset': 'key_palm.n.01', 'name': 'key_palm'}, {'id': 20185, 'synset': 'english_plantain.n.01', 'name': 'English_plantain'}, {'id': 20186, 'synset': 'broad-leaved_plantain.n.02', 'name': 'broad-leaved_plantain'}, {'id': 20187, 'synset': 'hoary_plantain.n.02', 'name': 'hoary_plantain'}, {'id': 20188, 'synset': 'fleawort.n.01', 'name': 'fleawort'}, {'id': 20189, 'synset': "rugel's_plantain.n.01", 'name': "rugel's_plantain"}, {'id': 20190, 'synset': 'hoary_plantain.n.01', 'name': 'hoary_plantain'}, {'id': 20191, 'synset': 'buckwheat.n.01', 'name': 'buckwheat'}, {'id': 20192, 'synset': "prince's-feather.n.01", 'name': "prince's-feather"}, {'id': 20193, 'synset': 'eriogonum.n.01', 'name': 'eriogonum'}, {'id': 20194, 'synset': 'umbrella_plant.n.02', 'name': 'umbrella_plant'}, {'id': 20195, 'synset': 'wild_buckwheat.n.01', 'name': 'wild_buckwheat'}, {'id': 20196, 'synset': 'rhubarb.n.02', 'name': 'rhubarb'}, {'id': 20197, 'synset': 'himalayan_rhubarb.n.01', 'name': 'Himalayan_rhubarb'}, {'id': 20198, 'synset': 'pie_plant.n.01', 'name': 'pie_plant'}, {'id': 20199, 'synset': 'chinese_rhubarb.n.01', 'name': 'Chinese_rhubarb'}, {'id': 20200, 'synset': 'sour_dock.n.01', 'name': 'sour_dock'}, {'id': 20201, 'synset': 'sheep_sorrel.n.01', 'name': 'sheep_sorrel'}, {'id': 20202, 'synset': 'bitter_dock.n.01', 'name': 'bitter_dock'}, {'id': 20203, 'synset': 'french_sorrel.n.01', 'name': 'French_sorrel'}, {'id': 20204, 'synset': 'yellow-eyed_grass.n.01', 'name': 'yellow-eyed_grass'}, {'id': 20205, 'synset': 'commelina.n.01', 'name': 'commelina'}, {'id': 20206, 'synset': 'spiderwort.n.01', 'name': 'spiderwort'}, {'id': 20207, 'synset': 'pineapple.n.01', 'name': 'pineapple'}, {'id': 20208, 'synset': 'pipewort.n.01', 'name': 'pipewort'}, {'id': 20209, 'synset': 'water_hyacinth.n.01', 'name': 'water_hyacinth'}, {'id': 20210, 'synset': 'water_star_grass.n.01', 'name': 'water_star_grass'}, {'id': 20211, 'synset': 'naiad.n.01', 'name': 'naiad'}, {'id': 20212, 'synset': 'water_plantain.n.01', 'name': 'water_plantain'}, {'id': 20213, 'synset': 'narrow-leaved_water_plantain.n.01', 'name': 'narrow-leaved_water_plantain'}, {'id': 20214, 'synset': 'hydrilla.n.01', 'name': 'hydrilla'}, {'id': 20215, 'synset': 'american_frogbit.n.01', 'name': 'American_frogbit'}, {'id': 20216, 'synset': 'waterweed.n.01', 'name': 'waterweed'}, {'id': 20217, 'synset': 'canadian_pondweed.n.01', 'name': 'Canadian_pondweed'}, {'id': 20218, 'synset': 'tape_grass.n.01', 'name': 'tape_grass'}, {'id': 20219, 'synset': 'pondweed.n.01', 'name': 'pondweed'}, {'id': 20220, 'synset': 'curled_leaf_pondweed.n.01', 'name': 'curled_leaf_pondweed'}, {'id': 20221, 'synset': 'loddon_pondweed.n.01', 'name': 'loddon_pondweed'}, {'id': 20222, 'synset': "frog's_lettuce.n.01", 'name': "frog's_lettuce"}, {'id': 20223, 'synset': 'arrow_grass.n.01', 'name': 'arrow_grass'}, {'id': 20224, 'synset': 'horned_pondweed.n.01', 'name': 'horned_pondweed'}, {'id': 20225, 'synset': 'eelgrass.n.01', 'name': 'eelgrass'}, {'id': 20226, 'synset': 'rose.n.01', 'name': 'rose'}, {'id': 20227, 'synset': 'hip.n.05', 'name': 'hip'}, {'id': 20228, 'synset': 'banksia_rose.n.01', 'name': 'banksia_rose'}, {'id': 20229, 'synset': 'damask_rose.n.01', 'name': 'damask_rose'}, {'id': 20230, 'synset': 'sweetbrier.n.01', 'name': 'sweetbrier'}, {'id': 20231, 'synset': 'cherokee_rose.n.01', 'name': 'Cherokee_rose'}, {'id': 20232, 'synset': 'musk_rose.n.01', 'name': 'musk_rose'}, {'id': 20233, 'synset': 'agrimonia.n.01', 'name': 'agrimonia'}, {'id': 20234, 'synset': 'harvest-lice.n.01', 'name': 'harvest-lice'}, {'id': 20235, 'synset': 'fragrant_agrimony.n.01', 'name': 'fragrant_agrimony'}, {'id': 20236, 'synset': 'alderleaf_juneberry.n.01', 'name': 'alderleaf_Juneberry'}, {'id': 20237, 'synset': 'flowering_quince.n.01', 'name': 'flowering_quince'}, {'id': 20238, 'synset': 'japonica.n.02', 'name': 'japonica'}, {'id': 20239, 'synset': 'coco_plum.n.01', 'name': 'coco_plum'}, {'id': 20240, 'synset': 'cotoneaster.n.01', 'name': 'cotoneaster'}, {'id': 20241, 'synset': 'cotoneaster_dammeri.n.01', 'name': 'Cotoneaster_dammeri'}, {'id': 20242, 'synset': 'cotoneaster_horizontalis.n.01', 'name': 'Cotoneaster_horizontalis'}, {'id': 20243, 'synset': 'parsley_haw.n.01', 'name': 'parsley_haw'}, {'id': 20244, 'synset': 'scarlet_haw.n.01', 'name': 'scarlet_haw'}, {'id': 20245, 'synset': 'blackthorn.n.02', 'name': 'blackthorn'}, {'id': 20246, 'synset': 'cockspur_thorn.n.01', 'name': 'cockspur_thorn'}, {'id': 20247, 'synset': 'mayhaw.n.01', 'name': 'mayhaw'}, {'id': 20248, 'synset': 'red_haw.n.02', 'name': 'red_haw'}, {'id': 20249, 'synset': 'red_haw.n.01', 'name': 'red_haw'}, {'id': 20250, 'synset': 'quince.n.01', 'name': 'quince'}, {'id': 20251, 'synset': 'mountain_avens.n.01', 'name': 'mountain_avens'}, {'id': 20252, 'synset': 'loquat.n.01', 'name': 'loquat'}, {'id': 20253, 'synset': 'beach_strawberry.n.01', 'name': 'beach_strawberry'}, {'id': 20254, 'synset': 'virginia_strawberry.n.01', 'name': 'Virginia_strawberry'}, {'id': 20255, 'synset': 'avens.n.01', 'name': 'avens'}, {'id': 20256, 'synset': 'yellow_avens.n.02', 'name': 'yellow_avens'}, {'id': 20257, 'synset': 'yellow_avens.n.01', 'name': 'yellow_avens'}, {'id': 20258, 'synset': 'prairie_smoke.n.01', 'name': 'prairie_smoke'}, {'id': 20259, 'synset': 'bennet.n.01', 'name': 'bennet'}, {'id': 20260, 'synset': 'toyon.n.01', 'name': 'toyon'}, {'id': 20261, 'synset': 'apple_tree.n.01', 'name': 'apple_tree'}, {'id': 20262, 'synset': 'apple.n.02', 'name': 'apple'}, {'id': 20263, 'synset': 'wild_apple.n.01', 'name': 'wild_apple'}, {'id': 20264, 'synset': 'crab_apple.n.01', 'name': 'crab_apple'}, {'id': 20265, 'synset': 'siberian_crab.n.01', 'name': 'Siberian_crab'}, {'id': 20266, 'synset': 'wild_crab.n.01', 'name': 'wild_crab'}, {'id': 20267, 'synset': 'american_crab_apple.n.01', 'name': 'American_crab_apple'}, {'id': 20268, 'synset': 'oregon_crab_apple.n.01', 'name': 'Oregon_crab_apple'}, {'id': 20269, 'synset': 'southern_crab_apple.n.01', 'name': 'Southern_crab_apple'}, {'id': 20270, 'synset': 'iowa_crab.n.01', 'name': 'Iowa_crab'}, {'id': 20271, 'synset': 'bechtel_crab.n.01', 'name': 'Bechtel_crab'}, {'id': 20272, 'synset': 'medlar.n.02', 'name': 'medlar'}, {'id': 20273, 'synset': 'cinquefoil.n.01', 'name': 'cinquefoil'}, {'id': 20274, 'synset': 'silverweed.n.02', 'name': 'silverweed'}, {'id': 20275, 'synset': 'salad_burnet.n.01', 'name': 'salad_burnet'}, {'id': 20276, 'synset': 'plum.n.01', 'name': 'plum'}, {'id': 20277, 'synset': 'wild_plum.n.01', 'name': 'wild_plum'}, {'id': 20278, 'synset': 'allegheny_plum.n.01', 'name': 'Allegheny_plum'}, {'id': 20279, 'synset': 'american_red_plum.n.01', 'name': 'American_red_plum'}, {'id': 20280, 'synset': 'chickasaw_plum.n.01', 'name': 'chickasaw_plum'}, {'id': 20281, 'synset': 'beach_plum.n.01', 'name': 'beach_plum'}, {'id': 20282, 'synset': 'common_plum.n.01', 'name': 'common_plum'}, {'id': 20283, 'synset': 'bullace.n.01', 'name': 'bullace'}, {'id': 20284, 'synset': 'damson_plum.n.02', 'name': 'damson_plum'}, {'id': 20285, 'synset': 'big-tree_plum.n.01', 'name': 'big-tree_plum'}, {'id': 20286, 'synset': 'canada_plum.n.01', 'name': 'Canada_plum'}, {'id': 20287, 'synset': 'plumcot.n.01', 'name': 'plumcot'}, {'id': 20288, 'synset': 'apricot.n.01', 'name': 'apricot'}, {'id': 20289, 'synset': 'japanese_apricot.n.01', 'name': 'Japanese_apricot'}, {'id': 20290, 'synset': 'common_apricot.n.01', 'name': 'common_apricot'}, {'id': 20291, 'synset': 'purple_apricot.n.01', 'name': 'purple_apricot'}, {'id': 20292, 'synset': 'cherry.n.02', 'name': 'cherry'}, {'id': 20293, 'synset': 'wild_cherry.n.02', 'name': 'wild_cherry'}, {'id': 20294, 'synset': 'wild_cherry.n.01', 'name': 'wild_cherry'}, {'id': 20295, 'synset': 'sweet_cherry.n.01', 'name': 'sweet_cherry'}, {'id': 20296, 'synset': 'heart_cherry.n.01', 'name': 'heart_cherry'}, {'id': 20297, 'synset': 'gean.n.01', 'name': 'gean'}, {'id': 20298, 'synset': 'capulin.n.01', 'name': 'capulin'}, {'id': 20299, 'synset': 'cherry_laurel.n.02', 'name': 'cherry_laurel'}, {'id': 20300, 'synset': 'cherry_plum.n.01', 'name': 'cherry_plum'}, {'id': 20301, 'synset': 'sour_cherry.n.01', 'name': 'sour_cherry'}, {'id': 20302, 'synset': 'amarelle.n.01', 'name': 'amarelle'}, {'id': 20303, 'synset': 'morello.n.01', 'name': 'morello'}, {'id': 20304, 'synset': 'marasca.n.01', 'name': 'marasca'}, {'id': 20305, 'synset': 'almond_tree.n.01', 'name': 'almond_tree'}, {'id': 20306, 'synset': 'almond.n.01', 'name': 'almond'}, {'id': 20307, 'synset': 'bitter_almond.n.01', 'name': 'bitter_almond'}, {'id': 20308, 'synset': 'jordan_almond.n.01', 'name': 'jordan_almond'}, {'id': 20309, 'synset': 'dwarf_flowering_almond.n.01', 'name': 'dwarf_flowering_almond'}, {'id': 20310, 'synset': 'holly-leaved_cherry.n.01', 'name': 'holly-leaved_cherry'}, {'id': 20311, 'synset': 'fuji.n.01', 'name': 'fuji'}, {'id': 20312, 'synset': 'flowering_almond.n.02', 'name': 'flowering_almond'}, {'id': 20313, 'synset': 'cherry_laurel.n.01', 'name': 'cherry_laurel'}, {'id': 20314, 'synset': 'catalina_cherry.n.01', 'name': 'Catalina_cherry'}, {'id': 20315, 'synset': 'bird_cherry.n.01', 'name': 'bird_cherry'}, {'id': 20316, 'synset': 'hagberry_tree.n.01', 'name': 'hagberry_tree'}, {'id': 20317, 'synset': 'hagberry.n.01', 'name': 'hagberry'}, {'id': 20318, 'synset': 'pin_cherry.n.01', 'name': 'pin_cherry'}, {'id': 20319, 'synset': 'peach.n.01', 'name': 'peach'}, {'id': 20320, 'synset': 'nectarine.n.01', 'name': 'nectarine'}, {'id': 20321, 'synset': 'sand_cherry.n.01', 'name': 'sand_cherry'}, {'id': 20322, 'synset': 'japanese_plum.n.01', 'name': 'Japanese_plum'}, {'id': 20323, 'synset': 'black_cherry.n.01', 'name': 'black_cherry'}, {'id': 20324, 'synset': 'flowering_cherry.n.01', 'name': 'flowering_cherry'}, {'id': 20325, 'synset': 'oriental_cherry.n.01', 'name': 'oriental_cherry'}, {'id': 20326, 'synset': 'japanese_flowering_cherry.n.01', 'name': 'Japanese_flowering_cherry'}, {'id': 20327, 'synset': 'sierra_plum.n.01', 'name': 'Sierra_plum'}, {'id': 20328, 'synset': 'rosebud_cherry.n.01', 'name': 'rosebud_cherry'}, {'id': 20329, 'synset': 'russian_almond.n.01', 'name': 'Russian_almond'}, {'id': 20330, 'synset': 'flowering_almond.n.01', 'name': 'flowering_almond'}, {'id': 20331, 'synset': 'chokecherry.n.02', 'name': 'chokecherry'}, {'id': 20332, 'synset': 'chokecherry.n.01', 'name': 'chokecherry'}, {'id': 20333, 'synset': 'western_chokecherry.n.01', 'name': 'western_chokecherry'}, {'id': 20334, 'synset': 'pyracantha.n.01', 'name': 'Pyracantha'}, {'id': 20335, 'synset': 'pear.n.02', 'name': 'pear'}, {'id': 20336, 'synset': 'fruit_tree.n.01', 'name': 'fruit_tree'}, {'id': 20337, 'synset': 'bramble_bush.n.01', 'name': 'bramble_bush'}, {'id': 20338, 'synset': 'lawyerbush.n.01', 'name': 'lawyerbush'}, {'id': 20339, 'synset': 'stone_bramble.n.01', 'name': 'stone_bramble'}, {'id': 20340, 'synset': 'sand_blackberry.n.01', 'name': 'sand_blackberry'}, {'id': 20341, 'synset': 'boysenberry.n.01', 'name': 'boysenberry'}, {'id': 20342, 'synset': 'loganberry.n.01', 'name': 'loganberry'}, {'id': 20343, 'synset': 'american_dewberry.n.02', 'name': 'American_dewberry'}, {'id': 20344, 'synset': 'northern_dewberry.n.01', 'name': 'Northern_dewberry'}, {'id': 20345, 'synset': 'southern_dewberry.n.01', 'name': 'Southern_dewberry'}, {'id': 20346, 'synset': 'swamp_dewberry.n.01', 'name': 'swamp_dewberry'}, {'id': 20347, 'synset': 'european_dewberry.n.01', 'name': 'European_dewberry'}, {'id': 20348, 'synset': 'raspberry.n.01', 'name': 'raspberry'}, {'id': 20349, 'synset': 'wild_raspberry.n.01', 'name': 'wild_raspberry'}, {'id': 20350, 'synset': 'american_raspberry.n.01', 'name': 'American_raspberry'}, {'id': 20351, 'synset': 'black_raspberry.n.01', 'name': 'black_raspberry'}, {'id': 20352, 'synset': 'salmonberry.n.03', 'name': 'salmonberry'}, {'id': 20353, 'synset': 'salmonberry.n.02', 'name': 'salmonberry'}, {'id': 20354, 'synset': 'wineberry.n.01', 'name': 'wineberry'}, {'id': 20355, 'synset': 'mountain_ash.n.01', 'name': 'mountain_ash'}, {'id': 20356, 'synset': 'rowan.n.01', 'name': 'rowan'}, {'id': 20357, 'synset': 'rowanberry.n.01', 'name': 'rowanberry'}, {'id': 20358, 'synset': 'american_mountain_ash.n.01', 'name': 'American_mountain_ash'}, {'id': 20359, 'synset': 'western_mountain_ash.n.01', 'name': 'Western_mountain_ash'}, {'id': 20360, 'synset': 'service_tree.n.01', 'name': 'service_tree'}, {'id': 20361, 'synset': 'wild_service_tree.n.01', 'name': 'wild_service_tree'}, {'id': 20362, 'synset': 'spirea.n.02', 'name': 'spirea'}, {'id': 20363, 'synset': 'bridal_wreath.n.02', 'name': 'bridal_wreath'}, {'id': 20364, 'synset': 'madderwort.n.01', 'name': 'madderwort'}, {'id': 20365, 'synset': 'indian_madder.n.01', 'name': 'Indian_madder'}, {'id': 20366, 'synset': 'madder.n.01', 'name': 'madder'}, {'id': 20367, 'synset': 'woodruff.n.02', 'name': 'woodruff'}, {'id': 20368, 'synset': 'dagame.n.01', 'name': 'dagame'}, {'id': 20369, 'synset': 'blolly.n.01', 'name': 'blolly'}, {'id': 20370, 'synset': 'coffee.n.02', 'name': 'coffee'}, {'id': 20371, 'synset': 'arabian_coffee.n.01', 'name': 'Arabian_coffee'}, {'id': 20372, 'synset': 'liberian_coffee.n.01', 'name': 'Liberian_coffee'}, {'id': 20373, 'synset': 'robusta_coffee.n.01', 'name': 'robusta_coffee'}, {'id': 20374, 'synset': 'cinchona.n.02', 'name': 'cinchona'}, {'id': 20375, 'synset': 'cartagena_bark.n.01', 'name': 'Cartagena_bark'}, {'id': 20376, 'synset': 'calisaya.n.01', 'name': 'calisaya'}, {'id': 20377, 'synset': 'cinchona_tree.n.01', 'name': 'cinchona_tree'}, {'id': 20378, 'synset': 'cinchona.n.01', 'name': 'cinchona'}, {'id': 20379, 'synset': 'bedstraw.n.01', 'name': 'bedstraw'}, {'id': 20380, 'synset': 'sweet_woodruff.n.01', 'name': 'sweet_woodruff'}, {'id': 20381, 'synset': 'northern_bedstraw.n.01', 'name': 'Northern_bedstraw'}, {'id': 20382, 'synset': 'yellow_bedstraw.n.01', 'name': 'yellow_bedstraw'}, {'id': 20383, 'synset': 'wild_licorice.n.01', 'name': 'wild_licorice'}, {'id': 20384, 'synset': 'cleavers.n.01', 'name': 'cleavers'}, {'id': 20385, 'synset': 'wild_madder.n.01', 'name': 'wild_madder'}, {'id': 20386, 'synset': 'cape_jasmine.n.01', 'name': 'cape_jasmine'}, {'id': 20387, 'synset': 'genipa.n.01', 'name': 'genipa'}, {'id': 20388, 'synset': 'genipap_fruit.n.01', 'name': 'genipap_fruit'}, {'id': 20389, 'synset': 'hamelia.n.01', 'name': 'hamelia'}, {'id': 20390, 'synset': 'scarlet_bush.n.01', 'name': 'scarlet_bush'}, {'id': 20391, 'synset': 'lemonwood.n.02', 'name': 'lemonwood'}, {'id': 20392, 'synset': 'negro_peach.n.01', 'name': 'negro_peach'}, {'id': 20393, 'synset': 'wild_medlar.n.01', 'name': 'wild_medlar'}, {'id': 20394, 'synset': 'spanish_tamarind.n.01', 'name': 'Spanish_tamarind'}, {'id': 20395, 'synset': 'abelia.n.01', 'name': 'abelia'}, {'id': 20396, 'synset': 'bush_honeysuckle.n.02', 'name': 'bush_honeysuckle'}, {'id': 20397, 'synset': 'american_twinflower.n.01', 'name': 'American_twinflower'}, {'id': 20398, 'synset': 'honeysuckle.n.01', 'name': 'honeysuckle'}, {'id': 20399, 'synset': 'american_fly_honeysuckle.n.01', 'name': 'American_fly_honeysuckle'}, {'id': 20400, 'synset': 'italian_honeysuckle.n.01', 'name': 'Italian_honeysuckle'}, {'id': 20401, 'synset': 'yellow_honeysuckle.n.01', 'name': 'yellow_honeysuckle'}, {'id': 20402, 'synset': 'hairy_honeysuckle.n.01', 'name': 'hairy_honeysuckle'}, {'id': 20403, 'synset': 'japanese_honeysuckle.n.01', 'name': 'Japanese_honeysuckle'}, {'id': 20404, 'synset': "hall's_honeysuckle.n.01", 'name': "Hall's_honeysuckle"}, {'id': 20405, 'synset': "morrow's_honeysuckle.n.01", 'name': "Morrow's_honeysuckle"}, {'id': 20406, 'synset': 'woodbine.n.02', 'name': 'woodbine'}, {'id': 20407, 'synset': 'trumpet_honeysuckle.n.01', 'name': 'trumpet_honeysuckle'}, {'id': 20408, 'synset': 'european_fly_honeysuckle.n.01', 'name': 'European_fly_honeysuckle'}, {'id': 20409, 'synset': 'swamp_fly_honeysuckle.n.01', 'name': 'swamp_fly_honeysuckle'}, {'id': 20410, 'synset': 'snowberry.n.01', 'name': 'snowberry'}, {'id': 20411, 'synset': 'coralberry.n.01', 'name': 'coralberry'}, {'id': 20412, 'synset': 'blue_elder.n.01', 'name': 'blue_elder'}, {'id': 20413, 'synset': 'dwarf_elder.n.01', 'name': 'dwarf_elder'}, {'id': 20414, 'synset': 'american_red_elder.n.01', 'name': 'American_red_elder'}, {'id': 20415, 'synset': 'european_red_elder.n.01', 'name': 'European_red_elder'}, {'id': 20416, 'synset': 'feverroot.n.01', 'name': 'feverroot'}, {'id': 20417, 'synset': 'cranberry_bush.n.01', 'name': 'cranberry_bush'}, {'id': 20418, 'synset': 'wayfaring_tree.n.01', 'name': 'wayfaring_tree'}, {'id': 20419, 'synset': 'guelder_rose.n.01', 'name': 'guelder_rose'}, {'id': 20420, 'synset': 'arrow_wood.n.01', 'name': 'arrow_wood'}, {'id': 20421, 'synset': 'black_haw.n.02', 'name': 'black_haw'}, {'id': 20422, 'synset': 'weigela.n.01', 'name': 'weigela'}, {'id': 20423, 'synset': 'teasel.n.01', 'name': 'teasel'}, {'id': 20424, 'synset': 'common_teasel.n.01', 'name': 'common_teasel'}, {'id': 20425, 'synset': "fuller's_teasel.n.01", 'name': "fuller's_teasel"}, {'id': 20426, 'synset': 'wild_teasel.n.01', 'name': 'wild_teasel'}, {'id': 20427, 'synset': 'scabious.n.01', 'name': 'scabious'}, {'id': 20428, 'synset': 'sweet_scabious.n.01', 'name': 'sweet_scabious'}, {'id': 20429, 'synset': 'field_scabious.n.01', 'name': 'field_scabious'}, {'id': 20430, 'synset': 'jewelweed.n.01', 'name': 'jewelweed'}, {'id': 20431, 'synset': 'geranium.n.01', 'name': 'geranium'}, {'id': 20432, 'synset': 'cranesbill.n.01', 'name': 'cranesbill'}, {'id': 20433, 'synset': 'wild_geranium.n.01', 'name': 'wild_geranium'}, {'id': 20434, 'synset': 'meadow_cranesbill.n.01', 'name': 'meadow_cranesbill'}, {'id': 20435, 'synset': "richardson's_geranium.n.01", 'name': "Richardson's_geranium"}, {'id': 20436, 'synset': 'herb_robert.n.01', 'name': 'herb_robert'}, {'id': 20437, 'synset': 'sticky_geranium.n.01', 'name': 'sticky_geranium'}, {'id': 20438, 'synset': "dove's_foot_geranium.n.01", 'name': "dove's_foot_geranium"}, {'id': 20439, 'synset': 'rose_geranium.n.01', 'name': 'rose_geranium'}, {'id': 20440, 'synset': 'fish_geranium.n.01', 'name': 'fish_geranium'}, {'id': 20441, 'synset': 'ivy_geranium.n.01', 'name': 'ivy_geranium'}, {'id': 20442, 'synset': 'apple_geranium.n.01', 'name': 'apple_geranium'}, {'id': 20443, 'synset': 'lemon_geranium.n.01', 'name': 'lemon_geranium'}, {'id': 20444, 'synset': 'storksbill.n.01', 'name': 'storksbill'}, {'id': 20445, 'synset': 'musk_clover.n.01', 'name': 'musk_clover'}, {'id': 20446, 'synset': 'incense_tree.n.01', 'name': 'incense_tree'}, {'id': 20447, 'synset': 'elephant_tree.n.01', 'name': 'elephant_tree'}, {'id': 20448, 'synset': 'gumbo-limbo.n.01', 'name': 'gumbo-limbo'}, {'id': 20449, 'synset': 'boswellia_carteri.n.01', 'name': 'Boswellia_carteri'}, {'id': 20450, 'synset': 'salai.n.01', 'name': 'salai'}, {'id': 20451, 'synset': 'balm_of_gilead.n.03', 'name': 'balm_of_gilead'}, {'id': 20452, 'synset': 'myrrh_tree.n.01', 'name': 'myrrh_tree'}, {'id': 20453, 'synset': 'protium_heptaphyllum.n.01', 'name': 'Protium_heptaphyllum'}, {'id': 20454, 'synset': 'protium_guianense.n.01', 'name': 'Protium_guianense'}, {'id': 20455, 'synset': 'water_starwort.n.01', 'name': 'water_starwort'}, {'id': 20456, 'synset': 'barbados_cherry.n.01', 'name': 'barbados_cherry'}, {'id': 20457, 'synset': 'mahogany.n.02', 'name': 'mahogany'}, {'id': 20458, 'synset': 'chinaberry.n.02', 'name': 'chinaberry'}, {'id': 20459, 'synset': 'neem.n.01', 'name': 'neem'}, {'id': 20460, 'synset': 'neem_seed.n.01', 'name': 'neem_seed'}, {'id': 20461, 'synset': 'spanish_cedar.n.01', 'name': 'Spanish_cedar'}, {'id': 20462, 'synset': 'satinwood.n.03', 'name': 'satinwood'}, {'id': 20463, 'synset': 'african_scented_mahogany.n.01', 'name': 'African_scented_mahogany'}, {'id': 20464, 'synset': 'silver_ash.n.01', 'name': 'silver_ash'}, {'id': 20465, 'synset': 'native_beech.n.01', 'name': 'native_beech'}, {'id': 20466, 'synset': 'bunji-bunji.n.01', 'name': 'bunji-bunji'}, {'id': 20467, 'synset': 'african_mahogany.n.01', 'name': 'African_mahogany'}, {'id': 20468, 'synset': 'lanseh_tree.n.01', 'name': 'lanseh_tree'}, {'id': 20469, 'synset': 'true_mahogany.n.01', 'name': 'true_mahogany'}, {'id': 20470, 'synset': 'honduras_mahogany.n.01', 'name': 'Honduras_mahogany'}, {'id': 20471, 'synset': 'philippine_mahogany.n.02', 'name': 'Philippine_mahogany'}, {'id': 20472, 'synset': 'caracolito.n.01', 'name': 'caracolito'}, {'id': 20473, 'synset': 'common_wood_sorrel.n.01', 'name': 'common_wood_sorrel'}, {'id': 20474, 'synset': 'bermuda_buttercup.n.01', 'name': 'Bermuda_buttercup'}, {'id': 20475, 'synset': 'creeping_oxalis.n.01', 'name': 'creeping_oxalis'}, {'id': 20476, 'synset': 'goatsfoot.n.01', 'name': 'goatsfoot'}, {'id': 20477, 'synset': 'violet_wood_sorrel.n.01', 'name': 'violet_wood_sorrel'}, {'id': 20478, 'synset': 'oca.n.01', 'name': 'oca'}, {'id': 20479, 'synset': 'carambola.n.01', 'name': 'carambola'}, {'id': 20480, 'synset': 'bilimbi.n.01', 'name': 'bilimbi'}, {'id': 20481, 'synset': 'milkwort.n.01', 'name': 'milkwort'}, {'id': 20482, 'synset': 'senega.n.02', 'name': 'senega'}, {'id': 20483, 'synset': 'orange_milkwort.n.01', 'name': 'orange_milkwort'}, {'id': 20484, 'synset': 'flowering_wintergreen.n.01', 'name': 'flowering_wintergreen'}, {'id': 20485, 'synset': 'seneca_snakeroot.n.01', 'name': 'Seneca_snakeroot'}, {'id': 20486, 'synset': 'common_milkwort.n.01', 'name': 'common_milkwort'}, {'id': 20487, 'synset': 'rue.n.01', 'name': 'rue'}, {'id': 20488, 'synset': 'citrus.n.02', 'name': 'citrus'}, {'id': 20489, 'synset': 'orange.n.03', 'name': 'orange'}, {'id': 20490, 'synset': 'sour_orange.n.01', 'name': 'sour_orange'}, {'id': 20491, 'synset': 'bergamot.n.01', 'name': 'bergamot'}, {'id': 20492, 'synset': 'pomelo.n.01', 'name': 'pomelo'}, {'id': 20493, 'synset': 'citron.n.02', 'name': 'citron'}, {'id': 20494, 'synset': 'grapefruit.n.01', 'name': 'grapefruit'}, {'id': 20495, 'synset': 'mandarin.n.01', 'name': 'mandarin'}, {'id': 20496, 'synset': 'tangerine.n.01', 'name': 'tangerine'}, {'id': 20497, 'synset': 'satsuma.n.01', 'name': 'satsuma'}, {'id': 20498, 'synset': 'sweet_orange.n.02', 'name': 'sweet_orange'}, {'id': 20499, 'synset': 'temple_orange.n.01', 'name': 'temple_orange'}, {'id': 20500, 'synset': 'tangelo.n.01', 'name': 'tangelo'}, {'id': 20501, 'synset': 'rangpur.n.01', 'name': 'rangpur'}, {'id': 20502, 'synset': 'lemon.n.03', 'name': 'lemon'}, {'id': 20503, 'synset': 'sweet_lemon.n.01', 'name': 'sweet_lemon'}, {'id': 20504, 'synset': 'lime.n.04', 'name': 'lime'}, {'id': 20505, 'synset': 'citrange.n.01', 'name': 'citrange'}, {'id': 20506, 'synset': 'fraxinella.n.01', 'name': 'fraxinella'}, {'id': 20507, 'synset': 'kumquat.n.01', 'name': 'kumquat'}, {'id': 20508, 'synset': 'marumi.n.01', 'name': 'marumi'}, {'id': 20509, 'synset': 'nagami.n.01', 'name': 'nagami'}, {'id': 20510, 'synset': 'cork_tree.n.01', 'name': 'cork_tree'}, {'id': 20511, 'synset': 'trifoliate_orange.n.01', 'name': 'trifoliate_orange'}, {'id': 20512, 'synset': 'prickly_ash.n.01', 'name': 'prickly_ash'}, {'id': 20513, 'synset': 'toothache_tree.n.01', 'name': 'toothache_tree'}, {'id': 20514, 'synset': "hercules'-club.n.01", 'name': "Hercules'-club"}, {'id': 20515, 'synset': 'bitterwood_tree.n.01', 'name': 'bitterwood_tree'}, {'id': 20516, 'synset': 'marupa.n.01', 'name': 'marupa'}, {'id': 20517, 'synset': 'paradise_tree.n.01', 'name': 'paradise_tree'}, {'id': 20518, 'synset': 'ailanthus.n.01', 'name': 'ailanthus'}, {'id': 20519, 'synset': 'tree_of_heaven.n.01', 'name': 'tree_of_heaven'}, {'id': 20520, 'synset': 'wild_mango.n.01', 'name': 'wild_mango'}, {'id': 20521, 'synset': 'pepper_tree.n.02', 'name': 'pepper_tree'}, {'id': 20522, 'synset': 'jamaica_quassia.n.02', 'name': 'Jamaica_quassia'}, {'id': 20523, 'synset': 'quassia.n.02', 'name': 'quassia'}, {'id': 20524, 'synset': 'nasturtium.n.01', 'name': 'nasturtium'}, {'id': 20525, 'synset': 'garden_nasturtium.n.01', 'name': 'garden_nasturtium'}, {'id': 20526, 'synset': 'bush_nasturtium.n.01', 'name': 'bush_nasturtium'}, {'id': 20527, 'synset': 'canarybird_flower.n.01', 'name': 'canarybird_flower'}, {'id': 20528, 'synset': 'bean_caper.n.01', 'name': 'bean_caper'}, {'id': 20529, 'synset': 'palo_santo.n.01', 'name': 'palo_santo'}, {'id': 20530, 'synset': 'lignum_vitae.n.02', 'name': 'lignum_vitae'}, {'id': 20531, 'synset': 'creosote_bush.n.01', 'name': 'creosote_bush'}, {'id': 20532, 'synset': 'caltrop.n.01', 'name': 'caltrop'}, {'id': 20533, 'synset': 'willow.n.01', 'name': 'willow'}, {'id': 20534, 'synset': 'osier.n.02', 'name': 'osier'}, {'id': 20535, 'synset': 'white_willow.n.01', 'name': 'white_willow'}, {'id': 20536, 'synset': 'silver_willow.n.01', 'name': 'silver_willow'}, {'id': 20537, 'synset': 'golden_willow.n.01', 'name': 'golden_willow'}, {'id': 20538, 'synset': 'cricket-bat_willow.n.01', 'name': 'cricket-bat_willow'}, {'id': 20539, 'synset': 'arctic_willow.n.01', 'name': 'arctic_willow'}, {'id': 20540, 'synset': 'weeping_willow.n.01', 'name': 'weeping_willow'}, {'id': 20541, 'synset': 'wisconsin_weeping_willow.n.01', 'name': 'Wisconsin_weeping_willow'}, {'id': 20542, 'synset': 'pussy_willow.n.01', 'name': 'pussy_willow'}, {'id': 20543, 'synset': 'sallow.n.01', 'name': 'sallow'}, {'id': 20544, 'synset': 'goat_willow.n.01', 'name': 'goat_willow'}, {'id': 20545, 'synset': 'peachleaf_willow.n.01', 'name': 'peachleaf_willow'}, {'id': 20546, 'synset': 'almond_willow.n.01', 'name': 'almond_willow'}, {'id': 20547, 'synset': 'hoary_willow.n.01', 'name': 'hoary_willow'}, {'id': 20548, 'synset': 'crack_willow.n.01', 'name': 'crack_willow'}, {'id': 20549, 'synset': 'prairie_willow.n.01', 'name': 'prairie_willow'}, {'id': 20550, 'synset': 'dwarf_willow.n.01', 'name': 'dwarf_willow'}, {'id': 20551, 'synset': 'grey_willow.n.01', 'name': 'grey_willow'}, {'id': 20552, 'synset': 'arroyo_willow.n.01', 'name': 'arroyo_willow'}, {'id': 20553, 'synset': 'shining_willow.n.01', 'name': 'shining_willow'}, {'id': 20554, 'synset': 'swamp_willow.n.01', 'name': 'swamp_willow'}, {'id': 20555, 'synset': 'bay_willow.n.01', 'name': 'bay_willow'}, {'id': 20556, 'synset': 'purple_willow.n.01', 'name': 'purple_willow'}, {'id': 20557, 'synset': 'balsam_willow.n.01', 'name': 'balsam_willow'}, {'id': 20558, 'synset': 'creeping_willow.n.01', 'name': 'creeping_willow'}, {'id': 20559, 'synset': 'sitka_willow.n.01', 'name': 'Sitka_willow'}, {'id': 20560, 'synset': 'dwarf_grey_willow.n.01', 'name': 'dwarf_grey_willow'}, {'id': 20561, 'synset': 'bearberry_willow.n.01', 'name': 'bearberry_willow'}, {'id': 20562, 'synset': 'common_osier.n.01', 'name': 'common_osier'}, {'id': 20563, 'synset': 'poplar.n.02', 'name': 'poplar'}, {'id': 20564, 'synset': 'balsam_poplar.n.01', 'name': 'balsam_poplar'}, {'id': 20565, 'synset': 'white_poplar.n.01', 'name': 'white_poplar'}, {'id': 20566, 'synset': 'grey_poplar.n.01', 'name': 'grey_poplar'}, {'id': 20567, 'synset': 'black_poplar.n.01', 'name': 'black_poplar'}, {'id': 20568, 'synset': 'lombardy_poplar.n.01', 'name': 'Lombardy_poplar'}, {'id': 20569, 'synset': 'cottonwood.n.01', 'name': 'cottonwood'}, {'id': 20570, 'synset': 'eastern_cottonwood.n.01', 'name': 'Eastern_cottonwood'}, {'id': 20571, 'synset': 'black_cottonwood.n.02', 'name': 'black_cottonwood'}, {'id': 20572, 'synset': 'swamp_cottonwood.n.01', 'name': 'swamp_cottonwood'}, {'id': 20573, 'synset': 'aspen.n.01', 'name': 'aspen'}, {'id': 20574, 'synset': 'quaking_aspen.n.01', 'name': 'quaking_aspen'}, {'id': 20575, 'synset': 'american_quaking_aspen.n.01', 'name': 'American_quaking_aspen'}, {'id': 20576, 'synset': 'canadian_aspen.n.01', 'name': 'Canadian_aspen'}, {'id': 20577, 'synset': 'sandalwood_tree.n.01', 'name': 'sandalwood_tree'}, {'id': 20578, 'synset': 'quandong.n.01', 'name': 'quandong'}, {'id': 20579, 'synset': 'rabbitwood.n.01', 'name': 'rabbitwood'}, {'id': 20580, 'synset': 'loranthaceae.n.01', 'name': 'Loranthaceae'}, {'id': 20581, 'synset': 'mistletoe.n.03', 'name': 'mistletoe'}, {'id': 20582, 'synset': 'american_mistletoe.n.02', 'name': 'American_mistletoe'}, {'id': 20583, 'synset': 'mistletoe.n.02', 'name': 'mistletoe'}, {'id': 20584, 'synset': 'american_mistletoe.n.01', 'name': 'American_mistletoe'}, {'id': 20585, 'synset': 'aalii.n.01', 'name': 'aalii'}, {'id': 20586, 'synset': 'soapberry.n.01', 'name': 'soapberry'}, {'id': 20587, 'synset': 'wild_china_tree.n.01', 'name': 'wild_China_tree'}, {'id': 20588, 'synset': 'china_tree.n.01', 'name': 'China_tree'}, {'id': 20589, 'synset': 'akee.n.01', 'name': 'akee'}, {'id': 20590, 'synset': 'soapberry_vine.n.01', 'name': 'soapberry_vine'}, {'id': 20591, 'synset': 'heartseed.n.01', 'name': 'heartseed'}, {'id': 20592, 'synset': 'balloon_vine.n.01', 'name': 'balloon_vine'}, {'id': 20593, 'synset': 'longan.n.01', 'name': 'longan'}, {'id': 20594, 'synset': 'harpullia.n.01', 'name': 'harpullia'}, {'id': 20595, 'synset': 'harpulla.n.01', 'name': 'harpulla'}, {'id': 20596, 'synset': 'moreton_bay_tulipwood.n.01', 'name': 'Moreton_Bay_tulipwood'}, {'id': 20597, 'synset': 'litchi.n.01', 'name': 'litchi'}, {'id': 20598, 'synset': 'spanish_lime.n.01', 'name': 'Spanish_lime'}, {'id': 20599, 'synset': 'rambutan.n.01', 'name': 'rambutan'}, {'id': 20600, 'synset': 'pulasan.n.01', 'name': 'pulasan'}, {'id': 20601, 'synset': 'pachysandra.n.01', 'name': 'pachysandra'}, {'id': 20602, 'synset': 'allegheny_spurge.n.01', 'name': 'Allegheny_spurge'}, {'id': 20603, 'synset': 'bittersweet.n.02', 'name': 'bittersweet'}, {'id': 20604, 'synset': 'spindle_tree.n.01', 'name': 'spindle_tree'}, {'id': 20605, 'synset': 'winged_spindle_tree.n.01', 'name': 'winged_spindle_tree'}, {'id': 20606, 'synset': 'wahoo.n.02', 'name': 'wahoo'}, {'id': 20607, 'synset': 'strawberry_bush.n.01', 'name': 'strawberry_bush'}, {'id': 20608, 'synset': 'evergreen_bittersweet.n.01', 'name': 'evergreen_bittersweet'}, {'id': 20609, 'synset': 'cyrilla.n.01', 'name': 'cyrilla'}, {'id': 20610, 'synset': 'titi.n.01', 'name': 'titi'}, {'id': 20611, 'synset': 'crowberry.n.01', 'name': 'crowberry'}, {'id': 20612, 'synset': 'maple.n.02', 'name': 'maple'}, {'id': 20613, 'synset': 'silver_maple.n.01', 'name': 'silver_maple'}, {'id': 20614, 'synset': 'sugar_maple.n.01', 'name': 'sugar_maple'}, {'id': 20615, 'synset': 'red_maple.n.01', 'name': 'red_maple'}, {'id': 20616, 'synset': 'moosewood.n.01', 'name': 'moosewood'}, {'id': 20617, 'synset': 'oregon_maple.n.01', 'name': 'Oregon_maple'}, {'id': 20618, 'synset': 'dwarf_maple.n.01', 'name': 'dwarf_maple'}, {'id': 20619, 'synset': 'mountain_maple.n.01', 'name': 'mountain_maple'}, {'id': 20620, 'synset': 'vine_maple.n.01', 'name': 'vine_maple'}, {'id': 20621, 'synset': 'hedge_maple.n.01', 'name': 'hedge_maple'}, {'id': 20622, 'synset': 'norway_maple.n.01', 'name': 'Norway_maple'}, {'id': 20623, 'synset': 'sycamore.n.03', 'name': 'sycamore'}, {'id': 20624, 'synset': 'box_elder.n.01', 'name': 'box_elder'}, {'id': 20625, 'synset': 'california_box_elder.n.01', 'name': 'California_box_elder'}, {'id': 20626, 'synset': 'pointed-leaf_maple.n.01', 'name': 'pointed-leaf_maple'}, {'id': 20627, 'synset': 'japanese_maple.n.02', 'name': 'Japanese_maple'}, {'id': 20628, 'synset': 'japanese_maple.n.01', 'name': 'Japanese_maple'}, {'id': 20629, 'synset': 'holly.n.01', 'name': 'holly'}, {'id': 20630, 'synset': 'chinese_holly.n.01', 'name': 'Chinese_holly'}, {'id': 20631, 'synset': 'bearberry.n.02', 'name': 'bearberry'}, {'id': 20632, 'synset': 'inkberry.n.01', 'name': 'inkberry'}, {'id': 20633, 'synset': 'mate.n.07', 'name': 'mate'}, {'id': 20634, 'synset': 'american_holly.n.01', 'name': 'American_holly'}, {'id': 20635, 'synset': 'low_gallberry_holly.n.01', 'name': 'low_gallberry_holly'}, {'id': 20636, 'synset': 'tall_gallberry_holly.n.01', 'name': 'tall_gallberry_holly'}, {'id': 20637, 'synset': 'yaupon_holly.n.01', 'name': 'yaupon_holly'}, {'id': 20638, 'synset': 'deciduous_holly.n.01', 'name': 'deciduous_holly'}, {'id': 20639, 'synset': 'juneberry_holly.n.01', 'name': 'juneberry_holly'}, {'id': 20640, 'synset': 'largeleaf_holly.n.01', 'name': 'largeleaf_holly'}, {'id': 20641, 'synset': 'geogia_holly.n.01', 'name': 'Geogia_holly'}, {'id': 20642, 'synset': 'common_winterberry_holly.n.01', 'name': 'common_winterberry_holly'}, {'id': 20643, 'synset': 'smooth_winterberry_holly.n.01', 'name': 'smooth_winterberry_holly'}, {'id': 20644, 'synset': 'cashew.n.01', 'name': 'cashew'}, {'id': 20645, 'synset': 'goncalo_alves.n.01', 'name': 'goncalo_alves'}, {'id': 20646, 'synset': 'venetian_sumac.n.01', 'name': 'Venetian_sumac'}, {'id': 20647, 'synset': 'laurel_sumac.n.01', 'name': 'laurel_sumac'}, {'id': 20648, 'synset': 'mango.n.01', 'name': 'mango'}, {'id': 20649, 'synset': 'pistachio.n.01', 'name': 'pistachio'}, {'id': 20650, 'synset': 'terebinth.n.01', 'name': 'terebinth'}, {'id': 20651, 'synset': 'mastic.n.03', 'name': 'mastic'}, {'id': 20652, 'synset': 'australian_sumac.n.01', 'name': 'Australian_sumac'}, {'id': 20653, 'synset': 'sumac.n.02', 'name': 'sumac'}, {'id': 20654, 'synset': 'smooth_sumac.n.01', 'name': 'smooth_sumac'}, {'id': 20655, 'synset': 'sugar-bush.n.01', 'name': 'sugar-bush'}, {'id': 20656, 'synset': 'staghorn_sumac.n.01', 'name': 'staghorn_sumac'}, {'id': 20657, 'synset': 'squawbush.n.01', 'name': 'squawbush'}, {'id': 20658, 'synset': 'aroeira_blanca.n.01', 'name': 'aroeira_blanca'}, {'id': 20659, 'synset': 'pepper_tree.n.01', 'name': 'pepper_tree'}, {'id': 20660, 'synset': 'brazilian_pepper_tree.n.01', 'name': 'Brazilian_pepper_tree'}, {'id': 20661, 'synset': 'hog_plum.n.01', 'name': 'hog_plum'}, {'id': 20662, 'synset': 'mombin.n.01', 'name': 'mombin'}, {'id': 20663, 'synset': 'poison_ash.n.01', 'name': 'poison_ash'}, {'id': 20664, 'synset': 'poison_ivy.n.02', 'name': 'poison_ivy'}, {'id': 20665, 'synset': 'western_poison_oak.n.01', 'name': 'western_poison_oak'}, {'id': 20666, 'synset': 'eastern_poison_oak.n.01', 'name': 'eastern_poison_oak'}, {'id': 20667, 'synset': 'varnish_tree.n.02', 'name': 'varnish_tree'}, {'id': 20668, 'synset': 'horse_chestnut.n.01', 'name': 'horse_chestnut'}, {'id': 20669, 'synset': 'buckeye.n.01', 'name': 'buckeye'}, {'id': 20670, 'synset': 'sweet_buckeye.n.01', 'name': 'sweet_buckeye'}, {'id': 20671, 'synset': 'ohio_buckeye.n.01', 'name': 'Ohio_buckeye'}, {'id': 20672, 'synset': 'dwarf_buckeye.n.01', 'name': 'dwarf_buckeye'}, {'id': 20673, 'synset': 'red_buckeye.n.01', 'name': 'red_buckeye'}, {'id': 20674, 'synset': 'particolored_buckeye.n.01', 'name': 'particolored_buckeye'}, {'id': 20675, 'synset': 'ebony.n.03', 'name': 'ebony'}, {'id': 20676, 'synset': 'marblewood.n.02', 'name': 'marblewood'}, {'id': 20677, 'synset': 'marblewood.n.01', 'name': 'marblewood'}, {'id': 20678, 'synset': 'persimmon.n.01', 'name': 'persimmon'}, {'id': 20679, 'synset': 'japanese_persimmon.n.01', 'name': 'Japanese_persimmon'}, {'id': 20680, 'synset': 'american_persimmon.n.01', 'name': 'American_persimmon'}, {'id': 20681, 'synset': 'date_plum.n.01', 'name': 'date_plum'}, {'id': 20682, 'synset': 'buckthorn.n.02', 'name': 'buckthorn'}, {'id': 20683, 'synset': 'southern_buckthorn.n.01', 'name': 'southern_buckthorn'}, {'id': 20684, 'synset': 'false_buckthorn.n.01', 'name': 'false_buckthorn'}, {'id': 20685, 'synset': 'star_apple.n.01', 'name': 'star_apple'}, {'id': 20686, 'synset': 'satinleaf.n.01', 'name': 'satinleaf'}, {'id': 20687, 'synset': 'balata.n.02', 'name': 'balata'}, {'id': 20688, 'synset': 'sapodilla.n.01', 'name': 'sapodilla'}, {'id': 20689, 'synset': 'gutta-percha_tree.n.02', 'name': 'gutta-percha_tree'}, {'id': 20690, 'synset': 'gutta-percha_tree.n.01', 'name': 'gutta-percha_tree'}, {'id': 20691, 'synset': 'canistel.n.01', 'name': 'canistel'}, {'id': 20692, 'synset': 'marmalade_tree.n.01', 'name': 'marmalade_tree'}, {'id': 20693, 'synset': 'sweetleaf.n.01', 'name': 'sweetleaf'}, {'id': 20694, 'synset': 'asiatic_sweetleaf.n.01', 'name': 'Asiatic_sweetleaf'}, {'id': 20695, 'synset': 'styrax.n.01', 'name': 'styrax'}, {'id': 20696, 'synset': 'snowbell.n.01', 'name': 'snowbell'}, {'id': 20697, 'synset': 'japanese_snowbell.n.01', 'name': 'Japanese_snowbell'}, {'id': 20698, 'synset': 'texas_snowbell.n.01', 'name': 'Texas_snowbell'}, {'id': 20699, 'synset': 'silver-bell_tree.n.01', 'name': 'silver-bell_tree'}, {'id': 20700, 'synset': 'carnivorous_plant.n.01', 'name': 'carnivorous_plant'}, {'id': 20701, 'synset': 'pitcher_plant.n.01', 'name': 'pitcher_plant'}, {'id': 20702, 'synset': 'common_pitcher_plant.n.01', 'name': 'common_pitcher_plant'}, {'id': 20703, 'synset': 'hooded_pitcher_plant.n.01', 'name': 'hooded_pitcher_plant'}, {'id': 20704, 'synset': "huntsman's_horn.n.01", 'name': "huntsman's_horn"}, {'id': 20705, 'synset': 'tropical_pitcher_plant.n.01', 'name': 'tropical_pitcher_plant'}, {'id': 20706, 'synset': 'sundew.n.01', 'name': 'sundew'}, {'id': 20707, 'synset': "venus's_flytrap.n.01", 'name': "Venus's_flytrap"}, {'id': 20708, 'synset': 'waterwheel_plant.n.01', 'name': 'waterwheel_plant'}, {'id': 20709, 'synset': 'drosophyllum_lusitanicum.n.01', 'name': 'Drosophyllum_lusitanicum'}, {'id': 20710, 'synset': 'roridula.n.01', 'name': 'roridula'}, {'id': 20711, 'synset': 'australian_pitcher_plant.n.01', 'name': 'Australian_pitcher_plant'}, {'id': 20712, 'synset': 'sedum.n.01', 'name': 'sedum'}, {'id': 20713, 'synset': 'stonecrop.n.01', 'name': 'stonecrop'}, {'id': 20714, 'synset': 'rose-root.n.01', 'name': 'rose-root'}, {'id': 20715, 'synset': 'orpine.n.01', 'name': 'orpine'}, {'id': 20716, 'synset': 'pinwheel.n.01', 'name': 'pinwheel'}, {'id': 20717, 'synset': 'christmas_bush.n.01', 'name': 'Christmas_bush'}, {'id': 20718, 'synset': 'hortensia.n.01', 'name': 'hortensia'}, {'id': 20719, 'synset': 'fall-blooming_hydrangea.n.01', 'name': 'fall-blooming_hydrangea'}, {'id': 20720, 'synset': 'carpenteria.n.01', 'name': 'carpenteria'}, {'id': 20721, 'synset': 'decumary.n.01', 'name': 'decumary'}, {'id': 20722, 'synset': 'deutzia.n.01', 'name': 'deutzia'}, {'id': 20723, 'synset': 'philadelphus.n.01', 'name': 'philadelphus'}, {'id': 20724, 'synset': 'mock_orange.n.01', 'name': 'mock_orange'}, {'id': 20725, 'synset': 'saxifrage.n.01', 'name': 'saxifrage'}, {'id': 20726, 'synset': 'yellow_mountain_saxifrage.n.01', 'name': 'yellow_mountain_saxifrage'}, {'id': 20727, 'synset': 'meadow_saxifrage.n.01', 'name': 'meadow_saxifrage'}, {'id': 20728, 'synset': 'mossy_saxifrage.n.01', 'name': 'mossy_saxifrage'}, {'id': 20729, 'synset': 'western_saxifrage.n.01', 'name': 'western_saxifrage'}, {'id': 20730, 'synset': 'purple_saxifrage.n.01', 'name': 'purple_saxifrage'}, {'id': 20731, 'synset': 'star_saxifrage.n.01', 'name': 'star_saxifrage'}, {'id': 20732, 'synset': 'strawberry_geranium.n.01', 'name': 'strawberry_geranium'}, {'id': 20733, 'synset': 'astilbe.n.01', 'name': 'astilbe'}, {'id': 20734, 'synset': 'false_goatsbeard.n.01', 'name': 'false_goatsbeard'}, {'id': 20735, 'synset': 'dwarf_astilbe.n.01', 'name': 'dwarf_astilbe'}, {'id': 20736, 'synset': 'spirea.n.01', 'name': 'spirea'}, {'id': 20737, 'synset': 'bergenia.n.01', 'name': 'bergenia'}, {'id': 20738, 'synset': 'coast_boykinia.n.01', 'name': 'coast_boykinia'}, {'id': 20739, 'synset': 'golden_saxifrage.n.01', 'name': 'golden_saxifrage'}, {'id': 20740, 'synset': 'umbrella_plant.n.01', 'name': 'umbrella_plant'}, {'id': 20741, 'synset': 'bridal_wreath.n.01', 'name': 'bridal_wreath'}, {'id': 20742, 'synset': 'alumroot.n.01', 'name': 'alumroot'}, {'id': 20743, 'synset': 'coralbells.n.01', 'name': 'coralbells'}, {'id': 20744, 'synset': 'leatherleaf_saxifrage.n.01', 'name': 'leatherleaf_saxifrage'}, {'id': 20745, 'synset': 'woodland_star.n.01', 'name': 'woodland_star'}, {'id': 20746, 'synset': 'prairie_star.n.01', 'name': 'prairie_star'}, {'id': 20747, 'synset': 'miterwort.n.01', 'name': 'miterwort'}, {'id': 20748, 'synset': "five-point_bishop's_cap.n.01", 'name': "five-point_bishop's_cap"}, {'id': 20749, 'synset': 'parnassia.n.01', 'name': 'parnassia'}, {'id': 20750, 'synset': 'bog_star.n.01', 'name': 'bog_star'}, {'id': 20751, 'synset': 'fringed_grass_of_parnassus.n.01', 'name': 'fringed_grass_of_Parnassus'}, {'id': 20752, 'synset': 'false_alumroot.n.01', 'name': 'false_alumroot'}, {'id': 20753, 'synset': 'foamflower.n.01', 'name': 'foamflower'}, {'id': 20754, 'synset': 'false_miterwort.n.01', 'name': 'false_miterwort'}, {'id': 20755, 'synset': 'pickaback_plant.n.01', 'name': 'pickaback_plant'}, {'id': 20756, 'synset': 'currant.n.02', 'name': 'currant'}, {'id': 20757, 'synset': 'black_currant.n.01', 'name': 'black_currant'}, {'id': 20758, 'synset': 'white_currant.n.01', 'name': 'white_currant'}, {'id': 20759, 'synset': 'gooseberry.n.01', 'name': 'gooseberry'}, {'id': 20760, 'synset': 'plane_tree.n.01', 'name': 'plane_tree'}, {'id': 20761, 'synset': 'london_plane.n.01', 'name': 'London_plane'}, {'id': 20762, 'synset': 'american_sycamore.n.01', 'name': 'American_sycamore'}, {'id': 20763, 'synset': 'oriental_plane.n.01', 'name': 'oriental_plane'}, {'id': 20764, 'synset': 'california_sycamore.n.01', 'name': 'California_sycamore'}, {'id': 20765, 'synset': 'arizona_sycamore.n.01', 'name': 'Arizona_sycamore'}, {'id': 20766, 'synset': 'greek_valerian.n.01', 'name': 'Greek_valerian'}, {'id': 20767, 'synset': "northern_jacob's_ladder.n.01", 'name': "northern_Jacob's_ladder"}, {'id': 20768, 'synset': 'skunkweed.n.01', 'name': 'skunkweed'}, {'id': 20769, 'synset': 'phlox.n.01', 'name': 'phlox'}, {'id': 20770, 'synset': 'moss_pink.n.02', 'name': 'moss_pink'}, {'id': 20771, 'synset': 'evening-snow.n.01', 'name': 'evening-snow'}, {'id': 20772, 'synset': 'acanthus.n.01', 'name': 'acanthus'}, {'id': 20773, 'synset': "bear's_breech.n.01", 'name': "bear's_breech"}, {'id': 20774, 'synset': 'caricature_plant.n.01', 'name': 'caricature_plant'}, {'id': 20775, 'synset': 'black-eyed_susan.n.01', 'name': 'black-eyed_Susan'}, {'id': 20776, 'synset': 'catalpa.n.01', 'name': 'catalpa'}, {'id': 20777, 'synset': 'catalpa_bignioides.n.01', 'name': 'Catalpa_bignioides'}, {'id': 20778, 'synset': 'catalpa_speciosa.n.01', 'name': 'Catalpa_speciosa'}, {'id': 20779, 'synset': 'desert_willow.n.01', 'name': 'desert_willow'}, {'id': 20780, 'synset': 'calabash.n.02', 'name': 'calabash'}, {'id': 20781, 'synset': 'calabash.n.01', 'name': 'calabash'}, {'id': 20782, 'synset': 'borage.n.01', 'name': 'borage'}, {'id': 20783, 'synset': 'common_amsinckia.n.01', 'name': 'common_amsinckia'}, {'id': 20784, 'synset': 'anchusa.n.01', 'name': 'anchusa'}, {'id': 20785, 'synset': 'bugloss.n.01', 'name': 'bugloss'}, {'id': 20786, 'synset': 'cape_forget-me-not.n.02', 'name': 'cape_forget-me-not'}, {'id': 20787, 'synset': 'cape_forget-me-not.n.01', 'name': 'cape_forget-me-not'}, {'id': 20788, 'synset': 'spanish_elm.n.02', 'name': 'Spanish_elm'}, {'id': 20789, 'synset': 'princewood.n.01', 'name': 'princewood'}, {'id': 20790, 'synset': 'chinese_forget-me-not.n.01', 'name': 'Chinese_forget-me-not'}, {'id': 20791, 'synset': "hound's-tongue.n.02", 'name': "hound's-tongue"}, {'id': 20792, 'synset': "hound's-tongue.n.01", 'name': "hound's-tongue"}, {'id': 20793, 'synset': 'blueweed.n.01', 'name': 'blueweed'}, {'id': 20794, 'synset': "beggar's_lice.n.01", 'name': "beggar's_lice"}, {'id': 20795, 'synset': 'gromwell.n.01', 'name': 'gromwell'}, {'id': 20796, 'synset': 'puccoon.n.01', 'name': 'puccoon'}, {'id': 20797, 'synset': 'virginia_bluebell.n.01', 'name': 'Virginia_bluebell'}, {'id': 20798, 'synset': 'garden_forget-me-not.n.01', 'name': 'garden_forget-me-not'}, {'id': 20799, 'synset': 'forget-me-not.n.01', 'name': 'forget-me-not'}, {'id': 20800, 'synset': 'false_gromwell.n.01', 'name': 'false_gromwell'}, {'id': 20801, 'synset': 'comfrey.n.01', 'name': 'comfrey'}, {'id': 20802, 'synset': 'common_comfrey.n.01', 'name': 'common_comfrey'}, {'id': 20803, 'synset': 'convolvulus.n.01', 'name': 'convolvulus'}, {'id': 20804, 'synset': 'bindweed.n.01', 'name': 'bindweed'}, {'id': 20805, 'synset': 'field_bindweed.n.01', 'name': 'field_bindweed'}, {'id': 20806, 'synset': 'scammony.n.03', 'name': 'scammony'}, {'id': 20807, 'synset': 'silverweed.n.01', 'name': 'silverweed'}, {'id': 20808, 'synset': 'dodder.n.01', 'name': 'dodder'}, {'id': 20809, 'synset': 'dichondra.n.01', 'name': 'dichondra'}, {'id': 20810, 'synset': 'cypress_vine.n.01', 'name': 'cypress_vine'}, {'id': 20811, 'synset': 'moonflower.n.01', 'name': 'moonflower'}, {'id': 20812, 'synset': 'wild_potato_vine.n.01', 'name': 'wild_potato_vine'}, {'id': 20813, 'synset': 'red_morning-glory.n.01', 'name': 'red_morning-glory'}, {'id': 20814, 'synset': 'man-of-the-earth.n.01', 'name': 'man-of-the-earth'}, {'id': 20815, 'synset': 'scammony.n.01', 'name': 'scammony'}, {'id': 20816, 'synset': 'japanese_morning_glory.n.01', 'name': 'Japanese_morning_glory'}, {'id': 20817, 'synset': 'imperial_japanese_morning_glory.n.01', 'name': 'imperial_Japanese_morning_glory'}, {'id': 20818, 'synset': 'gesneriad.n.01', 'name': 'gesneriad'}, {'id': 20819, 'synset': 'gesneria.n.01', 'name': 'gesneria'}, {'id': 20820, 'synset': 'achimenes.n.01', 'name': 'achimenes'}, {'id': 20821, 'synset': 'aeschynanthus.n.01', 'name': 'aeschynanthus'}, {'id': 20822, 'synset': 'lace-flower_vine.n.01', 'name': 'lace-flower_vine'}, {'id': 20823, 'synset': 'columnea.n.01', 'name': 'columnea'}, {'id': 20824, 'synset': 'episcia.n.01', 'name': 'episcia'}, {'id': 20825, 'synset': 'gloxinia.n.01', 'name': 'gloxinia'}, {'id': 20826, 'synset': 'canterbury_bell.n.01', 'name': 'Canterbury_bell'}, {'id': 20827, 'synset': 'kohleria.n.01', 'name': 'kohleria'}, {'id': 20828, 'synset': 'african_violet.n.01', 'name': 'African_violet'}, {'id': 20829, 'synset': 'streptocarpus.n.01', 'name': 'streptocarpus'}, {'id': 20830, 'synset': 'cape_primrose.n.01', 'name': 'Cape_primrose'}, {'id': 20831, 'synset': 'waterleaf.n.01', 'name': 'waterleaf'}, {'id': 20832, 'synset': 'virginia_waterleaf.n.01', 'name': 'Virginia_waterleaf'}, {'id': 20833, 'synset': 'yellow_bells.n.01', 'name': 'yellow_bells'}, {'id': 20834, 'synset': 'yerba_santa.n.01', 'name': 'yerba_santa'}, {'id': 20835, 'synset': 'nemophila.n.01', 'name': 'nemophila'}, {'id': 20836, 'synset': 'baby_blue-eyes.n.01', 'name': 'baby_blue-eyes'}, {'id': 20837, 'synset': 'five-spot.n.02', 'name': 'five-spot'}, {'id': 20838, 'synset': 'scorpionweed.n.01', 'name': 'scorpionweed'}, {'id': 20839, 'synset': 'california_bluebell.n.02', 'name': 'California_bluebell'}, {'id': 20840, 'synset': 'california_bluebell.n.01', 'name': 'California_bluebell'}, {'id': 20841, 'synset': 'fiddleneck.n.01', 'name': 'fiddleneck'}, {'id': 20842, 'synset': 'fiesta_flower.n.01', 'name': 'fiesta_flower'}, {'id': 20843, 'synset': 'basil_thyme.n.01', 'name': 'basil_thyme'}, {'id': 20844, 'synset': 'giant_hyssop.n.01', 'name': 'giant_hyssop'}, {'id': 20845, 'synset': 'yellow_giant_hyssop.n.01', 'name': 'yellow_giant_hyssop'}, {'id': 20846, 'synset': 'anise_hyssop.n.01', 'name': 'anise_hyssop'}, {'id': 20847, 'synset': 'mexican_hyssop.n.01', 'name': 'Mexican_hyssop'}, {'id': 20848, 'synset': 'bugle.n.02', 'name': 'bugle'}, {'id': 20849, 'synset': 'creeping_bugle.n.01', 'name': 'creeping_bugle'}, {'id': 20850, 'synset': 'erect_bugle.n.01', 'name': 'erect_bugle'}, {'id': 20851, 'synset': 'pyramid_bugle.n.01', 'name': 'pyramid_bugle'}, {'id': 20852, 'synset': 'wood_mint.n.01', 'name': 'wood_mint'}, {'id': 20853, 'synset': 'hairy_wood_mint.n.01', 'name': 'hairy_wood_mint'}, {'id': 20854, 'synset': 'downy_wood_mint.n.01', 'name': 'downy_wood_mint'}, {'id': 20855, 'synset': 'calamint.n.01', 'name': 'calamint'}, {'id': 20856, 'synset': 'common_calamint.n.01', 'name': 'common_calamint'}, {'id': 20857, 'synset': 'large-flowered_calamint.n.01', 'name': 'large-flowered_calamint'}, {'id': 20858, 'synset': 'lesser_calamint.n.01', 'name': 'lesser_calamint'}, {'id': 20859, 'synset': 'wild_basil.n.01', 'name': 'wild_basil'}, {'id': 20860, 'synset': 'horse_balm.n.01', 'name': 'horse_balm'}, {'id': 20861, 'synset': 'coleus.n.01', 'name': 'coleus'}, {'id': 20862, 'synset': 'country_borage.n.01', 'name': 'country_borage'}, {'id': 20863, 'synset': 'painted_nettle.n.01', 'name': 'painted_nettle'}, {'id': 20864, 'synset': 'apalachicola_rosemary.n.01', 'name': 'Apalachicola_rosemary'}, {'id': 20865, 'synset': 'dragonhead.n.01', 'name': 'dragonhead'}, {'id': 20866, 'synset': 'elsholtzia.n.01', 'name': 'elsholtzia'}, {'id': 20867, 'synset': 'hemp_nettle.n.01', 'name': 'hemp_nettle'}, {'id': 20868, 'synset': 'ground_ivy.n.01', 'name': 'ground_ivy'}, {'id': 20869, 'synset': 'pennyroyal.n.02', 'name': 'pennyroyal'}, {'id': 20870, 'synset': 'hyssop.n.01', 'name': 'hyssop'}, {'id': 20871, 'synset': 'dead_nettle.n.02', 'name': 'dead_nettle'}, {'id': 20872, 'synset': 'white_dead_nettle.n.01', 'name': 'white_dead_nettle'}, {'id': 20873, 'synset': 'henbit.n.01', 'name': 'henbit'}, {'id': 20874, 'synset': 'english_lavender.n.01', 'name': 'English_lavender'}, {'id': 20875, 'synset': 'french_lavender.n.02', 'name': 'French_lavender'}, {'id': 20876, 'synset': 'spike_lavender.n.01', 'name': 'spike_lavender'}, {'id': 20877, 'synset': 'dagga.n.01', 'name': 'dagga'}, {'id': 20878, 'synset': "lion's-ear.n.01", 'name': "lion's-ear"}, {'id': 20879, 'synset': 'motherwort.n.01', 'name': 'motherwort'}, {'id': 20880, 'synset': 'pitcher_sage.n.02', 'name': 'pitcher_sage'}, {'id': 20881, 'synset': 'bugleweed.n.01', 'name': 'bugleweed'}, {'id': 20882, 'synset': 'water_horehound.n.01', 'name': 'water_horehound'}, {'id': 20883, 'synset': 'gipsywort.n.01', 'name': 'gipsywort'}, {'id': 20884, 'synset': 'origanum.n.01', 'name': 'origanum'}, {'id': 20885, 'synset': 'oregano.n.01', 'name': 'oregano'}, {'id': 20886, 'synset': 'sweet_marjoram.n.01', 'name': 'sweet_marjoram'}, {'id': 20887, 'synset': 'horehound.n.01', 'name': 'horehound'}, {'id': 20888, 'synset': 'common_horehound.n.01', 'name': 'common_horehound'}, {'id': 20889, 'synset': 'lemon_balm.n.01', 'name': 'lemon_balm'}, {'id': 20890, 'synset': 'corn_mint.n.01', 'name': 'corn_mint'}, {'id': 20891, 'synset': 'water-mint.n.01', 'name': 'water-mint'}, {'id': 20892, 'synset': 'bergamot_mint.n.02', 'name': 'bergamot_mint'}, {'id': 20893, 'synset': 'horsemint.n.03', 'name': 'horsemint'}, {'id': 20894, 'synset': 'peppermint.n.01', 'name': 'peppermint'}, {'id': 20895, 'synset': 'spearmint.n.01', 'name': 'spearmint'}, {'id': 20896, 'synset': 'apple_mint.n.01', 'name': 'apple_mint'}, {'id': 20897, 'synset': 'pennyroyal.n.01', 'name': 'pennyroyal'}, {'id': 20898, 'synset': 'yerba_buena.n.01', 'name': 'yerba_buena'}, {'id': 20899, 'synset': 'molucca_balm.n.01', 'name': 'molucca_balm'}, {'id': 20900, 'synset': 'monarda.n.01', 'name': 'monarda'}, {'id': 20901, 'synset': 'bee_balm.n.02', 'name': 'bee_balm'}, {'id': 20902, 'synset': 'horsemint.n.02', 'name': 'horsemint'}, {'id': 20903, 'synset': 'bee_balm.n.01', 'name': 'bee_balm'}, {'id': 20904, 'synset': 'lemon_mint.n.01', 'name': 'lemon_mint'}, {'id': 20905, 'synset': 'plains_lemon_monarda.n.01', 'name': 'plains_lemon_monarda'}, {'id': 20906, 'synset': 'basil_balm.n.01', 'name': 'basil_balm'}, {'id': 20907, 'synset': 'mustang_mint.n.01', 'name': 'mustang_mint'}, {'id': 20908, 'synset': 'catmint.n.01', 'name': 'catmint'}, {'id': 20909, 'synset': 'basil.n.01', 'name': 'basil'}, {'id': 20910, 'synset': 'beefsteak_plant.n.01', 'name': 'beefsteak_plant'}, {'id': 20911, 'synset': 'phlomis.n.01', 'name': 'phlomis'}, {'id': 20912, 'synset': 'jerusalem_sage.n.01', 'name': 'Jerusalem_sage'}, {'id': 20913, 'synset': 'physostegia.n.01', 'name': 'physostegia'}, {'id': 20914, 'synset': 'plectranthus.n.01', 'name': 'plectranthus'}, {'id': 20915, 'synset': 'patchouli.n.01', 'name': 'patchouli'}, {'id': 20916, 'synset': 'self-heal.n.01', 'name': 'self-heal'}, {'id': 20917, 'synset': 'mountain_mint.n.01', 'name': 'mountain_mint'}, {'id': 20918, 'synset': 'rosemary.n.01', 'name': 'rosemary'}, {'id': 20919, 'synset': 'clary_sage.n.01', 'name': 'clary_sage'}, {'id': 20920, 'synset': 'purple_sage.n.01', 'name': 'purple_sage'}, {'id': 20921, 'synset': 'cancerweed.n.01', 'name': 'cancerweed'}, {'id': 20922, 'synset': 'common_sage.n.01', 'name': 'common_sage'}, {'id': 20923, 'synset': 'meadow_clary.n.01', 'name': 'meadow_clary'}, {'id': 20924, 'synset': 'clary.n.01', 'name': 'clary'}, {'id': 20925, 'synset': 'pitcher_sage.n.01', 'name': 'pitcher_sage'}, {'id': 20926, 'synset': 'mexican_mint.n.01', 'name': 'Mexican_mint'}, {'id': 20927, 'synset': 'wild_sage.n.01', 'name': 'wild_sage'}, {'id': 20928, 'synset': 'savory.n.01', 'name': 'savory'}, {'id': 20929, 'synset': 'summer_savory.n.01', 'name': 'summer_savory'}, {'id': 20930, 'synset': 'winter_savory.n.01', 'name': 'winter_savory'}, {'id': 20931, 'synset': 'skullcap.n.02', 'name': 'skullcap'}, {'id': 20932, 'synset': 'blue_pimpernel.n.01', 'name': 'blue_pimpernel'}, {'id': 20933, 'synset': 'hedge_nettle.n.02', 'name': 'hedge_nettle'}, {'id': 20934, 'synset': 'hedge_nettle.n.01', 'name': 'hedge_nettle'}, {'id': 20935, 'synset': 'germander.n.01', 'name': 'germander'}, {'id': 20936, 'synset': 'american_germander.n.01', 'name': 'American_germander'}, {'id': 20937, 'synset': 'cat_thyme.n.01', 'name': 'cat_thyme'}, {'id': 20938, 'synset': 'wood_sage.n.01', 'name': 'wood_sage'}, {'id': 20939, 'synset': 'thyme.n.01', 'name': 'thyme'}, {'id': 20940, 'synset': 'common_thyme.n.01', 'name': 'common_thyme'}, {'id': 20941, 'synset': 'wild_thyme.n.01', 'name': 'wild_thyme'}, {'id': 20942, 'synset': 'blue_curls.n.01', 'name': 'blue_curls'}, {'id': 20943, 'synset': 'turpentine_camphor_weed.n.01', 'name': 'turpentine_camphor_weed'}, {'id': 20944, 'synset': 'bastard_pennyroyal.n.01', 'name': 'bastard_pennyroyal'}, {'id': 20945, 'synset': 'bladderwort.n.01', 'name': 'bladderwort'}, {'id': 20946, 'synset': 'butterwort.n.01', 'name': 'butterwort'}, {'id': 20947, 'synset': 'genlisea.n.01', 'name': 'genlisea'}, {'id': 20948, 'synset': 'martynia.n.01', 'name': 'martynia'}, {'id': 20949, 'synset': 'common_unicorn_plant.n.01', 'name': 'common_unicorn_plant'}, {'id': 20950, 'synset': "sand_devil's_claw.n.01", 'name': "sand_devil's_claw"}, {'id': 20951, 'synset': 'sweet_unicorn_plant.n.01', 'name': 'sweet_unicorn_plant'}, {'id': 20952, 'synset': 'figwort.n.01', 'name': 'figwort'}, {'id': 20953, 'synset': 'snapdragon.n.01', 'name': 'snapdragon'}, {'id': 20954, 'synset': 'white_snapdragon.n.01', 'name': 'white_snapdragon'}, {'id': 20955, 'synset': 'yellow_twining_snapdragon.n.01', 'name': 'yellow_twining_snapdragon'}, {'id': 20956, 'synset': 'mediterranean_snapdragon.n.01', 'name': 'Mediterranean_snapdragon'}, {'id': 20957, 'synset': 'kitten-tails.n.01', 'name': 'kitten-tails'}, {'id': 20958, 'synset': 'alpine_besseya.n.01', 'name': 'Alpine_besseya'}, {'id': 20959, 'synset': 'false_foxglove.n.02', 'name': 'false_foxglove'}, {'id': 20960, 'synset': 'false_foxglove.n.01', 'name': 'false_foxglove'}, {'id': 20961, 'synset': 'calceolaria.n.01', 'name': 'calceolaria'}, {'id': 20962, 'synset': 'indian_paintbrush.n.02', 'name': 'Indian_paintbrush'}, {'id': 20963, 'synset': 'desert_paintbrush.n.01', 'name': 'desert_paintbrush'}, {'id': 20964, 'synset': 'giant_red_paintbrush.n.01', 'name': 'giant_red_paintbrush'}, {'id': 20965, 'synset': 'great_plains_paintbrush.n.01', 'name': 'great_plains_paintbrush'}, {'id': 20966, 'synset': 'sulfur_paintbrush.n.01', 'name': 'sulfur_paintbrush'}, {'id': 20967, 'synset': 'shellflower.n.01', 'name': 'shellflower'}, {'id': 20968, 'synset': 'maiden_blue-eyed_mary.n.01', 'name': 'maiden_blue-eyed_Mary'}, {'id': 20969, 'synset': 'blue-eyed_mary.n.01', 'name': 'blue-eyed_Mary'}, {'id': 20970, 'synset': 'foxglove.n.01', 'name': 'foxglove'}, {'id': 20971, 'synset': 'common_foxglove.n.01', 'name': 'common_foxglove'}, {'id': 20972, 'synset': 'yellow_foxglove.n.01', 'name': 'yellow_foxglove'}, {'id': 20973, 'synset': 'gerardia.n.01', 'name': 'gerardia'}, {'id': 20974, 'synset': 'blue_toadflax.n.01', 'name': 'blue_toadflax'}, {'id': 20975, 'synset': 'toadflax.n.01', 'name': 'toadflax'}, {'id': 20976, 'synset': 'golden-beard_penstemon.n.01', 'name': 'golden-beard_penstemon'}, {'id': 20977, 'synset': 'scarlet_bugler.n.01', 'name': 'scarlet_bugler'}, {'id': 20978, 'synset': 'red_shrubby_penstemon.n.01', 'name': 'red_shrubby_penstemon'}, {'id': 20979, 'synset': 'platte_river_penstemon.n.01', 'name': 'Platte_River_penstemon'}, {'id': 20980, 'synset': 'hot-rock_penstemon.n.01', 'name': 'hot-rock_penstemon'}, {'id': 20981, 'synset': "jones'_penstemon.n.01", 'name': "Jones'_penstemon"}, {'id': 20982, 'synset': 'shrubby_penstemon.n.01', 'name': 'shrubby_penstemon'}, {'id': 20983, 'synset': 'narrow-leaf_penstemon.n.01', 'name': 'narrow-leaf_penstemon'}, {'id': 20984, 'synset': 'balloon_flower.n.01', 'name': 'balloon_flower'}, {'id': 20985, 'synset': "parry's_penstemon.n.01", 'name': "Parry's_penstemon"}, {'id': 20986, 'synset': 'rock_penstemon.n.01', 'name': 'rock_penstemon'}, {'id': 20987, 'synset': "rydberg's_penstemon.n.01", 'name': "Rydberg's_penstemon"}, {'id': 20988, 'synset': 'cascade_penstemon.n.01', 'name': 'cascade_penstemon'}, {'id': 20989, 'synset': "whipple's_penstemon.n.01", 'name': "Whipple's_penstemon"}, {'id': 20990, 'synset': 'moth_mullein.n.01', 'name': 'moth_mullein'}, {'id': 20991, 'synset': 'white_mullein.n.01', 'name': 'white_mullein'}, {'id': 20992, 'synset': 'purple_mullein.n.01', 'name': 'purple_mullein'}, {'id': 20993, 'synset': 'common_mullein.n.01', 'name': 'common_mullein'}, {'id': 20994, 'synset': 'veronica.n.01', 'name': 'veronica'}, {'id': 20995, 'synset': 'field_speedwell.n.01', 'name': 'field_speedwell'}, {'id': 20996, 'synset': 'brooklime.n.02', 'name': 'brooklime'}, {'id': 20997, 'synset': 'corn_speedwell.n.01', 'name': 'corn_speedwell'}, {'id': 20998, 'synset': 'brooklime.n.01', 'name': 'brooklime'}, {'id': 20999, 'synset': 'germander_speedwell.n.01', 'name': 'germander_speedwell'}, {'id': 21000, 'synset': 'water_speedwell.n.01', 'name': 'water_speedwell'}, {'id': 21001, 'synset': 'common_speedwell.n.01', 'name': 'common_speedwell'}, {'id': 21002, 'synset': 'purslane_speedwell.n.01', 'name': 'purslane_speedwell'}, {'id': 21003, 'synset': 'thyme-leaved_speedwell.n.01', 'name': 'thyme-leaved_speedwell'}, {'id': 21004, 'synset': 'nightshade.n.01', 'name': 'nightshade'}, {'id': 21005, 'synset': 'horse_nettle.n.01', 'name': 'horse_nettle'}, {'id': 21006, 'synset': 'african_holly.n.01', 'name': 'African_holly'}, {'id': 21007, 'synset': 'potato_vine.n.02', 'name': 'potato_vine'}, {'id': 21008, 'synset': 'garden_huckleberry.n.01', 'name': 'garden_huckleberry'}, {'id': 21009, 'synset': 'naranjilla.n.01', 'name': 'naranjilla'}, {'id': 21010, 'synset': 'potato_vine.n.01', 'name': 'potato_vine'}, {'id': 21011, 'synset': 'potato_tree.n.01', 'name': 'potato_tree'}, {'id': 21012, 'synset': 'belladonna.n.01', 'name': 'belladonna'}, {'id': 21013, 'synset': 'bush_violet.n.01', 'name': 'bush_violet'}, {'id': 21014, 'synset': 'lady-of-the-night.n.01', 'name': 'lady-of-the-night'}, {'id': 21015, 'synset': "angel's_trumpet.n.02", 'name': "angel's_trumpet"}, {'id': 21016, 'synset': "angel's_trumpet.n.01", 'name': "angel's_trumpet"}, {'id': 21017, 'synset': "red_angel's_trumpet.n.01", 'name': "red_angel's_trumpet"}, {'id': 21018, 'synset': 'cone_pepper.n.01', 'name': 'cone_pepper'}, {'id': 21019, 'synset': 'bird_pepper.n.01', 'name': 'bird_pepper'}, {'id': 21020, 'synset': 'day_jessamine.n.01', 'name': 'day_jessamine'}, {'id': 21021, 'synset': 'night_jasmine.n.01', 'name': 'night_jasmine'}, {'id': 21022, 'synset': 'tree_tomato.n.01', 'name': 'tree_tomato'}, {'id': 21023, 'synset': 'thorn_apple.n.01', 'name': 'thorn_apple'}, {'id': 21024, 'synset': 'jimsonweed.n.01', 'name': 'jimsonweed'}, {'id': 21025, 'synset': 'pichi.n.01', 'name': 'pichi'}, {'id': 21026, 'synset': 'henbane.n.01', 'name': 'henbane'}, {'id': 21027, 'synset': 'egyptian_henbane.n.01', 'name': 'Egyptian_henbane'}, {'id': 21028, 'synset': 'matrimony_vine.n.01', 'name': 'matrimony_vine'}, {'id': 21029, 'synset': 'common_matrimony_vine.n.01', 'name': 'common_matrimony_vine'}, {'id': 21030, 'synset': 'christmasberry.n.01', 'name': 'Christmasberry'}, {'id': 21031, 'synset': 'plum_tomato.n.01', 'name': 'plum_tomato'}, {'id': 21032, 'synset': 'mandrake.n.02', 'name': 'mandrake'}, {'id': 21033, 'synset': 'mandrake_root.n.01', 'name': 'mandrake_root'}, {'id': 21034, 'synset': 'apple_of_peru.n.01', 'name': 'apple_of_Peru'}, {'id': 21035, 'synset': 'flowering_tobacco.n.01', 'name': 'flowering_tobacco'}, {'id': 21036, 'synset': 'common_tobacco.n.01', 'name': 'common_tobacco'}, {'id': 21037, 'synset': 'wild_tobacco.n.01', 'name': 'wild_tobacco'}, {'id': 21038, 'synset': 'cupflower.n.02', 'name': 'cupflower'}, {'id': 21039, 'synset': 'whitecup.n.01', 'name': 'whitecup'}, {'id': 21040, 'synset': 'petunia.n.01', 'name': 'petunia'}, {'id': 21041, 'synset': 'large_white_petunia.n.01', 'name': 'large_white_petunia'}, {'id': 21042, 'synset': 'violet-flowered_petunia.n.01', 'name': 'violet-flowered_petunia'}, {'id': 21043, 'synset': 'hybrid_petunia.n.01', 'name': 'hybrid_petunia'}, {'id': 21044, 'synset': 'cape_gooseberry.n.01', 'name': 'cape_gooseberry'}, {'id': 21045, 'synset': 'strawberry_tomato.n.01', 'name': 'strawberry_tomato'}, {'id': 21046, 'synset': 'tomatillo.n.02', 'name': 'tomatillo'}, {'id': 21047, 'synset': 'tomatillo.n.01', 'name': 'tomatillo'}, {'id': 21048, 'synset': 'yellow_henbane.n.01', 'name': 'yellow_henbane'}, {'id': 21049, 'synset': "cock's_eggs.n.01", 'name': "cock's_eggs"}, {'id': 21050, 'synset': 'salpiglossis.n.01', 'name': 'salpiglossis'}, {'id': 21051, 'synset': 'painted_tongue.n.01', 'name': 'painted_tongue'}, {'id': 21052, 'synset': 'butterfly_flower.n.01', 'name': 'butterfly_flower'}, {'id': 21053, 'synset': 'scopolia_carniolica.n.01', 'name': 'Scopolia_carniolica'}, {'id': 21054, 'synset': 'chalice_vine.n.01', 'name': 'chalice_vine'}, {'id': 21055, 'synset': 'verbena.n.01', 'name': 'verbena'}, {'id': 21056, 'synset': 'lantana.n.01', 'name': 'lantana'}, {'id': 21057, 'synset': 'black_mangrove.n.02', 'name': 'black_mangrove'}, {'id': 21058, 'synset': 'white_mangrove.n.01', 'name': 'white_mangrove'}, {'id': 21059, 'synset': 'black_mangrove.n.01', 'name': 'black_mangrove'}, {'id': 21060, 'synset': 'teak.n.02', 'name': 'teak'}, {'id': 21061, 'synset': 'spurge.n.01', 'name': 'spurge'}, {'id': 21062, 'synset': 'sun_spurge.n.01', 'name': 'sun_spurge'}, {'id': 21063, 'synset': 'petty_spurge.n.01', 'name': 'petty_spurge'}, {'id': 21064, 'synset': "medusa's_head.n.01", 'name': "medusa's_head"}, {'id': 21065, 'synset': 'wild_spurge.n.01', 'name': 'wild_spurge'}, {'id': 21066, 'synset': 'snow-on-the-mountain.n.01', 'name': 'snow-on-the-mountain'}, {'id': 21067, 'synset': 'cypress_spurge.n.01', 'name': 'cypress_spurge'}, {'id': 21068, 'synset': 'leafy_spurge.n.01', 'name': 'leafy_spurge'}, {'id': 21069, 'synset': 'hairy_spurge.n.01', 'name': 'hairy_spurge'}, {'id': 21070, 'synset': 'poinsettia.n.01', 'name': 'poinsettia'}, {'id': 21071, 'synset': 'japanese_poinsettia.n.01', 'name': 'Japanese_poinsettia'}, {'id': 21072, 'synset': 'fire-on-the-mountain.n.01', 'name': 'fire-on-the-mountain'}, {'id': 21073, 'synset': 'wood_spurge.n.01', 'name': 'wood_spurge'}, {'id': 21074, 'synset': 'dwarf_spurge.n.01', 'name': 'dwarf_spurge'}, {'id': 21075, 'synset': 'scarlet_plume.n.01', 'name': 'scarlet_plume'}, {'id': 21076, 'synset': 'naboom.n.01', 'name': 'naboom'}, {'id': 21077, 'synset': 'crown_of_thorns.n.02', 'name': 'crown_of_thorns'}, {'id': 21078, 'synset': 'toothed_spurge.n.01', 'name': 'toothed_spurge'}, {'id': 21079, 'synset': 'three-seeded_mercury.n.01', 'name': 'three-seeded_mercury'}, {'id': 21080, 'synset': 'croton.n.02', 'name': 'croton'}, {'id': 21081, 'synset': 'cascarilla.n.01', 'name': 'cascarilla'}, {'id': 21082, 'synset': 'cascarilla_bark.n.01', 'name': 'cascarilla_bark'}, {'id': 21083, 'synset': 'castor-oil_plant.n.01', 'name': 'castor-oil_plant'}, {'id': 21084, 'synset': 'spurge_nettle.n.01', 'name': 'spurge_nettle'}, {'id': 21085, 'synset': 'physic_nut.n.01', 'name': 'physic_nut'}, {'id': 21086, 'synset': 'para_rubber_tree.n.01', 'name': 'Para_rubber_tree'}, {'id': 21087, 'synset': 'cassava.n.03', 'name': 'cassava'}, {'id': 21088, 'synset': 'bitter_cassava.n.01', 'name': 'bitter_cassava'}, {'id': 21089, 'synset': 'cassava.n.02', 'name': 'cassava'}, {'id': 21090, 'synset': 'sweet_cassava.n.01', 'name': 'sweet_cassava'}, {'id': 21091, 'synset': 'candlenut.n.01', 'name': 'candlenut'}, {'id': 21092, 'synset': 'tung_tree.n.01', 'name': 'tung_tree'}, {'id': 21093, 'synset': 'slipper_spurge.n.01', 'name': 'slipper_spurge'}, {'id': 21094, 'synset': 'candelilla.n.01', 'name': 'candelilla'}, {'id': 21095, 'synset': 'jewbush.n.01', 'name': 'Jewbush'}, {'id': 21096, 'synset': 'jumping_bean.n.01', 'name': 'jumping_bean'}, {'id': 21097, 'synset': 'camellia.n.01', 'name': 'camellia'}, {'id': 21098, 'synset': 'japonica.n.01', 'name': 'japonica'}, {'id': 21099, 'synset': 'umbellifer.n.01', 'name': 'umbellifer'}, {'id': 21100, 'synset': 'wild_parsley.n.01', 'name': 'wild_parsley'}, {'id': 21101, 'synset': "fool's_parsley.n.01", 'name': "fool's_parsley"}, {'id': 21102, 'synset': 'dill.n.01', 'name': 'dill'}, {'id': 21103, 'synset': 'angelica.n.01', 'name': 'angelica'}, {'id': 21104, 'synset': 'garden_angelica.n.01', 'name': 'garden_angelica'}, {'id': 21105, 'synset': 'wild_angelica.n.01', 'name': 'wild_angelica'}, {'id': 21106, 'synset': 'chervil.n.01', 'name': 'chervil'}, {'id': 21107, 'synset': 'cow_parsley.n.01', 'name': 'cow_parsley'}, {'id': 21108, 'synset': 'wild_celery.n.01', 'name': 'wild_celery'}, {'id': 21109, 'synset': 'astrantia.n.01', 'name': 'astrantia'}, {'id': 21110, 'synset': 'greater_masterwort.n.01', 'name': 'greater_masterwort'}, {'id': 21111, 'synset': 'caraway.n.01', 'name': 'caraway'}, {'id': 21112, 'synset': 'whorled_caraway.n.01', 'name': 'whorled_caraway'}, {'id': 21113, 'synset': 'water_hemlock.n.01', 'name': 'water_hemlock'}, {'id': 21114, 'synset': 'spotted_cowbane.n.01', 'name': 'spotted_cowbane'}, {'id': 21115, 'synset': 'hemlock.n.02', 'name': 'hemlock'}, {'id': 21116, 'synset': 'earthnut.n.02', 'name': 'earthnut'}, {'id': 21117, 'synset': 'cumin.n.01', 'name': 'cumin'}, {'id': 21118, 'synset': 'wild_carrot.n.01', 'name': 'wild_carrot'}, {'id': 21119, 'synset': 'eryngo.n.01', 'name': 'eryngo'}, {'id': 21120, 'synset': 'sea_holly.n.01', 'name': 'sea_holly'}, {'id': 21121, 'synset': 'button_snakeroot.n.02', 'name': 'button_snakeroot'}, {'id': 21122, 'synset': 'rattlesnake_master.n.01', 'name': 'rattlesnake_master'}, {'id': 21123, 'synset': 'fennel.n.01', 'name': 'fennel'}, {'id': 21124, 'synset': 'common_fennel.n.01', 'name': 'common_fennel'}, {'id': 21125, 'synset': 'florence_fennel.n.01', 'name': 'Florence_fennel'}, {'id': 21126, 'synset': 'cow_parsnip.n.01', 'name': 'cow_parsnip'}, {'id': 21127, 'synset': 'lovage.n.01', 'name': 'lovage'}, {'id': 21128, 'synset': 'sweet_cicely.n.01', 'name': 'sweet_cicely'}, {'id': 21129, 'synset': 'water_fennel.n.01', 'name': 'water_fennel'}, {'id': 21130, 'synset': 'parsnip.n.02', 'name': 'parsnip'}, {'id': 21131, 'synset': 'cultivated_parsnip.n.01', 'name': 'cultivated_parsnip'}, {'id': 21132, 'synset': 'wild_parsnip.n.01', 'name': 'wild_parsnip'}, {'id': 21133, 'synset': 'parsley.n.01', 'name': 'parsley'}, {'id': 21134, 'synset': 'italian_parsley.n.01', 'name': 'Italian_parsley'}, {'id': 21135, 'synset': 'hamburg_parsley.n.01', 'name': 'Hamburg_parsley'}, {'id': 21136, 'synset': 'anise.n.01', 'name': 'anise'}, {'id': 21137, 'synset': 'sanicle.n.01', 'name': 'sanicle'}, {'id': 21138, 'synset': 'purple_sanicle.n.01', 'name': 'purple_sanicle'}, {'id': 21139, 'synset': 'european_sanicle.n.01', 'name': 'European_sanicle'}, {'id': 21140, 'synset': 'water_parsnip.n.01', 'name': 'water_parsnip'}, {'id': 21141, 'synset': 'greater_water_parsnip.n.01', 'name': 'greater_water_parsnip'}, {'id': 21142, 'synset': 'skirret.n.01', 'name': 'skirret'}, {'id': 21143, 'synset': 'dogwood.n.01', 'name': 'dogwood'}, {'id': 21144, 'synset': 'common_white_dogwood.n.01', 'name': 'common_white_dogwood'}, {'id': 21145, 'synset': 'red_osier.n.01', 'name': 'red_osier'}, {'id': 21146, 'synset': 'silky_dogwood.n.02', 'name': 'silky_dogwood'}, {'id': 21147, 'synset': 'silky_cornel.n.01', 'name': 'silky_cornel'}, {'id': 21148, 'synset': 'common_european_dogwood.n.01', 'name': 'common_European_dogwood'}, {'id': 21149, 'synset': 'bunchberry.n.01', 'name': 'bunchberry'}, {'id': 21150, 'synset': 'cornelian_cherry.n.01', 'name': 'cornelian_cherry'}, {'id': 21151, 'synset': 'puka.n.01', 'name': 'puka'}, {'id': 21152, 'synset': 'kapuka.n.01', 'name': 'kapuka'}, {'id': 21153, 'synset': 'valerian.n.01', 'name': 'valerian'}, {'id': 21154, 'synset': 'common_valerian.n.01', 'name': 'common_valerian'}, {'id': 21155, 'synset': 'common_corn_salad.n.01', 'name': 'common_corn_salad'}, {'id': 21156, 'synset': 'red_valerian.n.01', 'name': 'red_valerian'}, {'id': 21157, 'synset': 'filmy_fern.n.02', 'name': 'filmy_fern'}, {'id': 21158, 'synset': 'bristle_fern.n.01', 'name': 'bristle_fern'}, {'id': 21159, 'synset': "hare's-foot_bristle_fern.n.01", 'name': "hare's-foot_bristle_fern"}, {'id': 21160, 'synset': 'killarney_fern.n.01', 'name': 'Killarney_fern'}, {'id': 21161, 'synset': 'kidney_fern.n.01', 'name': 'kidney_fern'}, {'id': 21162, 'synset': 'flowering_fern.n.02', 'name': 'flowering_fern'}, {'id': 21163, 'synset': 'royal_fern.n.01', 'name': 'royal_fern'}, {'id': 21164, 'synset': 'interrupted_fern.n.01', 'name': 'interrupted_fern'}, {'id': 21165, 'synset': 'crape_fern.n.01', 'name': 'crape_fern'}, {'id': 21166, 'synset': 'crepe_fern.n.01', 'name': 'crepe_fern'}, {'id': 21167, 'synset': 'curly_grass.n.01', 'name': 'curly_grass'}, {'id': 21168, 'synset': 'pine_fern.n.01', 'name': 'pine_fern'}, {'id': 21169, 'synset': 'climbing_fern.n.01', 'name': 'climbing_fern'}, {'id': 21170, 'synset': 'creeping_fern.n.01', 'name': 'creeping_fern'}, {'id': 21171, 'synset': 'climbing_maidenhair.n.01', 'name': 'climbing_maidenhair'}, {'id': 21172, 'synset': 'scented_fern.n.02', 'name': 'scented_fern'}, {'id': 21173, 'synset': 'clover_fern.n.01', 'name': 'clover_fern'}, {'id': 21174, 'synset': 'nardoo.n.01', 'name': 'nardoo'}, {'id': 21175, 'synset': 'water_clover.n.01', 'name': 'water_clover'}, {'id': 21176, 'synset': 'pillwort.n.01', 'name': 'pillwort'}, {'id': 21177, 'synset': 'regnellidium.n.01', 'name': 'regnellidium'}, {'id': 21178, 'synset': 'floating-moss.n.01', 'name': 'floating-moss'}, {'id': 21179, 'synset': 'mosquito_fern.n.01', 'name': 'mosquito_fern'}, {'id': 21180, 'synset': "adder's_tongue.n.01", 'name': "adder's_tongue"}, {'id': 21181, 'synset': 'ribbon_fern.n.03', 'name': 'ribbon_fern'}, {'id': 21182, 'synset': 'grape_fern.n.01', 'name': 'grape_fern'}, {'id': 21183, 'synset': 'daisyleaf_grape_fern.n.01', 'name': 'daisyleaf_grape_fern'}, {'id': 21184, 'synset': 'leathery_grape_fern.n.01', 'name': 'leathery_grape_fern'}, {'id': 21185, 'synset': 'rattlesnake_fern.n.01', 'name': 'rattlesnake_fern'}, {'id': 21186, 'synset': 'flowering_fern.n.01', 'name': 'flowering_fern'}, {'id': 21187, 'synset': 'powdery_mildew.n.01', 'name': 'powdery_mildew'}, {'id': 21188, 'synset': 'dutch_elm_fungus.n.01', 'name': 'Dutch_elm_fungus'}, {'id': 21189, 'synset': 'ergot.n.02', 'name': 'ergot'}, {'id': 21190, 'synset': 'rye_ergot.n.01', 'name': 'rye_ergot'}, {'id': 21191, 'synset': 'black_root_rot_fungus.n.01', 'name': 'black_root_rot_fungus'}, {'id': 21192, 'synset': "dead-man's-fingers.n.01", 'name': "dead-man's-fingers"}, {'id': 21193, 'synset': 'sclerotinia.n.01', 'name': 'sclerotinia'}, {'id': 21194, 'synset': 'brown_cup.n.01', 'name': 'brown_cup'}, {'id': 21195, 'synset': 'earthball.n.01', 'name': 'earthball'}, {'id': 21196, 'synset': 'scleroderma_citrinum.n.01', 'name': 'Scleroderma_citrinum'}, {'id': 21197, 'synset': 'scleroderma_flavidium.n.01', 'name': 'Scleroderma_flavidium'}, {'id': 21198, 'synset': 'scleroderma_bovista.n.01', 'name': 'Scleroderma_bovista'}, {'id': 21199, 'synset': 'podaxaceae.n.01', 'name': 'Podaxaceae'}, {'id': 21200, 'synset': 'stalked_puffball.n.02', 'name': 'stalked_puffball'}, {'id': 21201, 'synset': 'stalked_puffball.n.01', 'name': 'stalked_puffball'}, {'id': 21202, 'synset': 'false_truffle.n.01', 'name': 'false_truffle'}, {'id': 21203, 'synset': 'rhizopogon_idahoensis.n.01', 'name': 'Rhizopogon_idahoensis'}, {'id': 21204, 'synset': 'truncocolumella_citrina.n.01', 'name': 'Truncocolumella_citrina'}, {'id': 21205, 'synset': 'mucor.n.01', 'name': 'mucor'}, {'id': 21206, 'synset': 'rhizopus.n.01', 'name': 'rhizopus'}, {'id': 21207, 'synset': 'bread_mold.n.01', 'name': 'bread_mold'}, {'id': 21208, 'synset': 'slime_mold.n.01', 'name': 'slime_mold'}, {'id': 21209, 'synset': 'true_slime_mold.n.01', 'name': 'true_slime_mold'}, {'id': 21210, 'synset': 'cellular_slime_mold.n.01', 'name': 'cellular_slime_mold'}, {'id': 21211, 'synset': 'dictostylium.n.01', 'name': 'dictostylium'}, {'id': 21212, 'synset': 'pond-scum_parasite.n.01', 'name': 'pond-scum_parasite'}, {'id': 21213, 'synset': 'potato_wart_fungus.n.01', 'name': 'potato_wart_fungus'}, {'id': 21214, 'synset': 'white_fungus.n.01', 'name': 'white_fungus'}, {'id': 21215, 'synset': 'water_mold.n.01', 'name': 'water_mold'}, {'id': 21216, 'synset': 'downy_mildew.n.01', 'name': 'downy_mildew'}, {'id': 21217, 'synset': 'blue_mold_fungus.n.01', 'name': 'blue_mold_fungus'}, {'id': 21218, 'synset': 'onion_mildew.n.01', 'name': 'onion_mildew'}, {'id': 21219, 'synset': 'tobacco_mildew.n.01', 'name': 'tobacco_mildew'}, {'id': 21220, 'synset': 'white_rust.n.01', 'name': 'white_rust'}, {'id': 21221, 'synset': 'pythium.n.01', 'name': 'pythium'}, {'id': 21222, 'synset': 'damping_off_fungus.n.01', 'name': 'damping_off_fungus'}, {'id': 21223, 'synset': 'phytophthora_citrophthora.n.01', 'name': 'Phytophthora_citrophthora'}, {'id': 21224, 'synset': 'phytophthora_infestans.n.01', 'name': 'Phytophthora_infestans'}, {'id': 21225, 'synset': 'clubroot_fungus.n.01', 'name': 'clubroot_fungus'}, {'id': 21226, 'synset': 'geglossaceae.n.01', 'name': 'Geglossaceae'}, {'id': 21227, 'synset': 'sarcosomataceae.n.01', 'name': 'Sarcosomataceae'}, {'id': 21228, 'synset': 'rufous_rubber_cup.n.01', 'name': 'Rufous_rubber_cup'}, {'id': 21229, 'synset': "devil's_cigar.n.01", 'name': "devil's_cigar"}, {'id': 21230, 'synset': "devil's_urn.n.01", 'name': "devil's_urn"}, {'id': 21231, 'synset': 'truffle.n.01', 'name': 'truffle'}, {'id': 21232, 'synset': 'club_fungus.n.01', 'name': 'club_fungus'}, {'id': 21233, 'synset': 'coral_fungus.n.01', 'name': 'coral_fungus'}, {'id': 21234, 'synset': 'tooth_fungus.n.01', 'name': 'tooth_fungus'}, {'id': 21235, 'synset': 'lichen.n.02', 'name': 'lichen'}, {'id': 21236, 'synset': 'ascolichen.n.01', 'name': 'ascolichen'}, {'id': 21237, 'synset': 'basidiolichen.n.01', 'name': 'basidiolichen'}, {'id': 21238, 'synset': 'lecanora.n.01', 'name': 'lecanora'}, {'id': 21239, 'synset': 'manna_lichen.n.01', 'name': 'manna_lichen'}, {'id': 21240, 'synset': 'archil.n.02', 'name': 'archil'}, {'id': 21241, 'synset': 'roccella.n.01', 'name': 'roccella'}, {'id': 21242, 'synset': 'beard_lichen.n.01', 'name': 'beard_lichen'}, {'id': 21243, 'synset': 'horsehair_lichen.n.01', 'name': 'horsehair_lichen'}, {'id': 21244, 'synset': 'reindeer_moss.n.01', 'name': 'reindeer_moss'}, {'id': 21245, 'synset': 'crottle.n.01', 'name': 'crottle'}, {'id': 21246, 'synset': 'iceland_moss.n.01', 'name': 'Iceland_moss'}, {'id': 21247, 'synset': 'fungus.n.01', 'name': 'fungus'}, {'id': 21248, 'synset': 'promycelium.n.01', 'name': 'promycelium'}, {'id': 21249, 'synset': 'true_fungus.n.01', 'name': 'true_fungus'}, {'id': 21250, 'synset': 'basidiomycete.n.01', 'name': 'basidiomycete'}, {'id': 21251, 'synset': 'mushroom.n.03', 'name': 'mushroom'}, {'id': 21252, 'synset': 'agaric.n.02', 'name': 'agaric'}, {'id': 21253, 'synset': 'mushroom.n.01', 'name': 'mushroom'}, {'id': 21254, 'synset': 'toadstool.n.01', 'name': 'toadstool'}, {'id': 21255, 'synset': 'horse_mushroom.n.01', 'name': 'horse_mushroom'}, {'id': 21256, 'synset': 'meadow_mushroom.n.01', 'name': 'meadow_mushroom'}, {'id': 21257, 'synset': 'shiitake.n.01', 'name': 'shiitake'}, {'id': 21258, 'synset': 'scaly_lentinus.n.01', 'name': 'scaly_lentinus'}, {'id': 21259, 'synset': 'royal_agaric.n.01', 'name': 'royal_agaric'}, {'id': 21260, 'synset': 'false_deathcap.n.01', 'name': 'false_deathcap'}, {'id': 21261, 'synset': 'fly_agaric.n.01', 'name': 'fly_agaric'}, {'id': 21262, 'synset': 'death_cap.n.01', 'name': 'death_cap'}, {'id': 21263, 'synset': 'blushing_mushroom.n.01', 'name': 'blushing_mushroom'}, {'id': 21264, 'synset': 'destroying_angel.n.01', 'name': 'destroying_angel'}, {'id': 21265, 'synset': 'chanterelle.n.01', 'name': 'chanterelle'}, {'id': 21266, 'synset': 'floccose_chanterelle.n.01', 'name': 'floccose_chanterelle'}, {'id': 21267, 'synset': "pig's_ears.n.01", 'name': "pig's_ears"}, {'id': 21268, 'synset': 'cinnabar_chanterelle.n.01', 'name': 'cinnabar_chanterelle'}, {'id': 21269, 'synset': 'jack-o-lantern_fungus.n.01', 'name': 'jack-o-lantern_fungus'}, {'id': 21270, 'synset': 'inky_cap.n.01', 'name': 'inky_cap'}, {'id': 21271, 'synset': 'shaggymane.n.01', 'name': 'shaggymane'}, {'id': 21272, 'synset': 'milkcap.n.01', 'name': 'milkcap'}, {'id': 21273, 'synset': 'fairy-ring_mushroom.n.01', 'name': 'fairy-ring_mushroom'}, {'id': 21274, 'synset': 'fairy_ring.n.01', 'name': 'fairy_ring'}, {'id': 21275, 'synset': 'oyster_mushroom.n.01', 'name': 'oyster_mushroom'}, {'id': 21276, 'synset': 'olive-tree_agaric.n.01', 'name': 'olive-tree_agaric'}, {'id': 21277, 'synset': 'pholiota_astragalina.n.01', 'name': 'Pholiota_astragalina'}, {'id': 21278, 'synset': 'pholiota_aurea.n.01', 'name': 'Pholiota_aurea'}, {'id': 21279, 'synset': 'pholiota_destruens.n.01', 'name': 'Pholiota_destruens'}, {'id': 21280, 'synset': 'pholiota_flammans.n.01', 'name': 'Pholiota_flammans'}, {'id': 21281, 'synset': 'pholiota_flavida.n.01', 'name': 'Pholiota_flavida'}, {'id': 21282, 'synset': 'nameko.n.01', 'name': 'nameko'}, {'id': 21283, 'synset': 'pholiota_squarrosa-adiposa.n.01', 'name': 'Pholiota_squarrosa-adiposa'}, {'id': 21284, 'synset': 'pholiota_squarrosa.n.01', 'name': 'Pholiota_squarrosa'}, {'id': 21285, 'synset': 'pholiota_squarrosoides.n.01', 'name': 'Pholiota_squarrosoides'}, {'id': 21286, 'synset': 'stropharia_ambigua.n.01', 'name': 'Stropharia_ambigua'}, {'id': 21287, 'synset': 'stropharia_hornemannii.n.01', 'name': 'Stropharia_hornemannii'}, {'id': 21288, 'synset': 'stropharia_rugoso-annulata.n.01', 'name': 'Stropharia_rugoso-annulata'}, {'id': 21289, 'synset': 'gill_fungus.n.01', 'name': 'gill_fungus'}, {'id': 21290, 'synset': 'entoloma_lividum.n.01', 'name': 'Entoloma_lividum'}, {'id': 21291, 'synset': 'entoloma_aprile.n.01', 'name': 'Entoloma_aprile'}, {'id': 21292, 'synset': 'chlorophyllum_molybdites.n.01', 'name': 'Chlorophyllum_molybdites'}, {'id': 21293, 'synset': 'lepiota.n.01', 'name': 'lepiota'}, {'id': 21294, 'synset': 'parasol_mushroom.n.01', 'name': 'parasol_mushroom'}, {'id': 21295, 'synset': 'poisonous_parasol.n.01', 'name': 'poisonous_parasol'}, {'id': 21296, 'synset': 'lepiota_naucina.n.01', 'name': 'Lepiota_naucina'}, {'id': 21297, 'synset': 'lepiota_rhacodes.n.01', 'name': 'Lepiota_rhacodes'}, {'id': 21298, 'synset': 'american_parasol.n.01', 'name': 'American_parasol'}, {'id': 21299, 'synset': 'lepiota_rubrotincta.n.01', 'name': 'Lepiota_rubrotincta'}, {'id': 21300, 'synset': 'lepiota_clypeolaria.n.01', 'name': 'Lepiota_clypeolaria'}, {'id': 21301, 'synset': 'onion_stem.n.01', 'name': 'onion_stem'}, {'id': 21302, 'synset': 'pink_disease_fungus.n.01', 'name': 'pink_disease_fungus'}, {'id': 21303, 'synset': 'bottom_rot_fungus.n.01', 'name': 'bottom_rot_fungus'}, {'id': 21304, 'synset': 'potato_fungus.n.01', 'name': 'potato_fungus'}, {'id': 21305, 'synset': 'coffee_fungus.n.01', 'name': 'coffee_fungus'}, {'id': 21306, 'synset': 'blewits.n.01', 'name': 'blewits'}, {'id': 21307, 'synset': 'sandy_mushroom.n.01', 'name': 'sandy_mushroom'}, {'id': 21308, 'synset': 'tricholoma_pessundatum.n.01', 'name': 'Tricholoma_pessundatum'}, {'id': 21309, 'synset': 'tricholoma_sejunctum.n.01', 'name': 'Tricholoma_sejunctum'}, {'id': 21310, 'synset': 'man-on-a-horse.n.01', 'name': 'man-on-a-horse'}, {'id': 21311, 'synset': 'tricholoma_venenata.n.01', 'name': 'Tricholoma_venenata'}, {'id': 21312, 'synset': 'tricholoma_pardinum.n.01', 'name': 'Tricholoma_pardinum'}, {'id': 21313, 'synset': 'tricholoma_vaccinum.n.01', 'name': 'Tricholoma_vaccinum'}, {'id': 21314, 'synset': 'tricholoma_aurantium.n.01', 'name': 'Tricholoma_aurantium'}, {'id': 21315, 'synset': 'volvaria_bombycina.n.01', 'name': 'Volvaria_bombycina'}, {'id': 21316, 'synset': 'pluteus_aurantiorugosus.n.01', 'name': 'Pluteus_aurantiorugosus'}, {'id': 21317, 'synset': 'pluteus_magnus.n.01', 'name': 'Pluteus_magnus'}, {'id': 21318, 'synset': 'deer_mushroom.n.01', 'name': 'deer_mushroom'}, {'id': 21319, 'synset': 'straw_mushroom.n.01', 'name': 'straw_mushroom'}, {'id': 21320, 'synset': 'volvariella_bombycina.n.01', 'name': 'Volvariella_bombycina'}, {'id': 21321, 'synset': 'clitocybe_clavipes.n.01', 'name': 'Clitocybe_clavipes'}, {'id': 21322, 'synset': 'clitocybe_dealbata.n.01', 'name': 'Clitocybe_dealbata'}, {'id': 21323, 'synset': 'clitocybe_inornata.n.01', 'name': 'Clitocybe_inornata'}, {'id': 21324, 'synset': 'clitocybe_robusta.n.01', 'name': 'Clitocybe_robusta'}, {'id': 21325, 'synset': 'clitocybe_irina.n.01', 'name': 'Clitocybe_irina'}, {'id': 21326, 'synset': 'clitocybe_subconnexa.n.01', 'name': 'Clitocybe_subconnexa'}, {'id': 21327, 'synset': 'winter_mushroom.n.01', 'name': 'winter_mushroom'}, {'id': 21328, 'synset': 'mycelium.n.01', 'name': 'mycelium'}, {'id': 21329, 'synset': 'sclerotium.n.02', 'name': 'sclerotium'}, {'id': 21330, 'synset': 'sac_fungus.n.01', 'name': 'sac_fungus'}, {'id': 21331, 'synset': 'ascomycete.n.01', 'name': 'ascomycete'}, {'id': 21332, 'synset': 'clavicipitaceae.n.01', 'name': 'Clavicipitaceae'}, {'id': 21333, 'synset': 'grainy_club.n.01', 'name': 'grainy_club'}, {'id': 21334, 'synset': 'yeast.n.02', 'name': 'yeast'}, {'id': 21335, 'synset': "baker's_yeast.n.01", 'name': "baker's_yeast"}, {'id': 21336, 'synset': "wine-maker's_yeast.n.01", 'name': "wine-maker's_yeast"}, {'id': 21337, 'synset': 'aspergillus_fumigatus.n.01', 'name': 'Aspergillus_fumigatus'}, {'id': 21338, 'synset': 'brown_root_rot_fungus.n.01', 'name': 'brown_root_rot_fungus'}, {'id': 21339, 'synset': 'discomycete.n.01', 'name': 'discomycete'}, {'id': 21340, 'synset': 'leotia_lubrica.n.01', 'name': 'Leotia_lubrica'}, {'id': 21341, 'synset': 'mitrula_elegans.n.01', 'name': 'Mitrula_elegans'}, {'id': 21342, 'synset': 'sarcoscypha_coccinea.n.01', 'name': 'Sarcoscypha_coccinea'}, {'id': 21343, 'synset': 'caloscypha_fulgens.n.01', 'name': 'Caloscypha_fulgens'}, {'id': 21344, 'synset': 'aleuria_aurantia.n.01', 'name': 'Aleuria_aurantia'}, {'id': 21345, 'synset': 'elf_cup.n.01', 'name': 'elf_cup'}, {'id': 21346, 'synset': 'peziza_domicilina.n.01', 'name': 'Peziza_domicilina'}, {'id': 21347, 'synset': 'blood_cup.n.01', 'name': 'blood_cup'}, {'id': 21348, 'synset': 'urnula_craterium.n.01', 'name': 'Urnula_craterium'}, {'id': 21349, 'synset': 'galiella_rufa.n.01', 'name': 'Galiella_rufa'}, {'id': 21350, 'synset': 'jafnea_semitosta.n.01', 'name': 'Jafnea_semitosta'}, {'id': 21351, 'synset': 'morel.n.01', 'name': 'morel'}, {'id': 21352, 'synset': 'common_morel.n.01', 'name': 'common_morel'}, {'id': 21353, 'synset': 'disciotis_venosa.n.01', 'name': 'Disciotis_venosa'}, {'id': 21354, 'synset': 'verpa.n.01', 'name': 'Verpa'}, {'id': 21355, 'synset': 'verpa_bohemica.n.01', 'name': 'Verpa_bohemica'}, {'id': 21356, 'synset': 'verpa_conica.n.01', 'name': 'Verpa_conica'}, {'id': 21357, 'synset': 'black_morel.n.01', 'name': 'black_morel'}, {'id': 21358, 'synset': 'morchella_crassipes.n.01', 'name': 'Morchella_crassipes'}, {'id': 21359, 'synset': 'morchella_semilibera.n.01', 'name': 'Morchella_semilibera'}, {'id': 21360, 'synset': 'wynnea_americana.n.01', 'name': 'Wynnea_americana'}, {'id': 21361, 'synset': 'wynnea_sparassoides.n.01', 'name': 'Wynnea_sparassoides'}, {'id': 21362, 'synset': 'false_morel.n.01', 'name': 'false_morel'}, {'id': 21363, 'synset': 'lorchel.n.01', 'name': 'lorchel'}, {'id': 21364, 'synset': 'helvella.n.01', 'name': 'helvella'}, {'id': 21365, 'synset': 'helvella_crispa.n.01', 'name': 'Helvella_crispa'}, {'id': 21366, 'synset': 'helvella_acetabulum.n.01', 'name': 'Helvella_acetabulum'}, {'id': 21367, 'synset': 'helvella_sulcata.n.01', 'name': 'Helvella_sulcata'}, {'id': 21368, 'synset': 'discina.n.01', 'name': 'discina'}, {'id': 21369, 'synset': 'gyromitra.n.01', 'name': 'gyromitra'}, {'id': 21370, 'synset': 'gyromitra_californica.n.01', 'name': 'Gyromitra_californica'}, {'id': 21371, 'synset': 'gyromitra_sphaerospora.n.01', 'name': 'Gyromitra_sphaerospora'}, {'id': 21372, 'synset': 'gyromitra_esculenta.n.01', 'name': 'Gyromitra_esculenta'}, {'id': 21373, 'synset': 'gyromitra_infula.n.01', 'name': 'Gyromitra_infula'}, {'id': 21374, 'synset': 'gyromitra_fastigiata.n.01', 'name': 'Gyromitra_fastigiata'}, {'id': 21375, 'synset': 'gyromitra_gigas.n.01', 'name': 'Gyromitra_gigas'}, {'id': 21376, 'synset': 'gasteromycete.n.01', 'name': 'gasteromycete'}, {'id': 21377, 'synset': 'stinkhorn.n.01', 'name': 'stinkhorn'}, {'id': 21378, 'synset': 'common_stinkhorn.n.01', 'name': 'common_stinkhorn'}, {'id': 21379, 'synset': 'phallus_ravenelii.n.01', 'name': 'Phallus_ravenelii'}, {'id': 21380, 'synset': 'dog_stinkhorn.n.01', 'name': 'dog_stinkhorn'}, {'id': 21381, 'synset': 'calostoma_lutescens.n.01', 'name': 'Calostoma_lutescens'}, {'id': 21382, 'synset': 'calostoma_cinnabarina.n.01', 'name': 'Calostoma_cinnabarina'}, {'id': 21383, 'synset': 'calostoma_ravenelii.n.01', 'name': 'Calostoma_ravenelii'}, {'id': 21384, 'synset': 'stinky_squid.n.01', 'name': 'stinky_squid'}, {'id': 21385, 'synset': 'puffball.n.01', 'name': 'puffball'}, {'id': 21386, 'synset': 'giant_puffball.n.01', 'name': 'giant_puffball'}, {'id': 21387, 'synset': 'earthstar.n.01', 'name': 'earthstar'}, {'id': 21388, 'synset': 'geastrum_coronatum.n.01', 'name': 'Geastrum_coronatum'}, {'id': 21389, 'synset': 'radiigera_fuscogleba.n.01', 'name': 'Radiigera_fuscogleba'}, {'id': 21390, 'synset': 'astreus_pteridis.n.01', 'name': 'Astreus_pteridis'}, {'id': 21391, 'synset': 'astreus_hygrometricus.n.01', 'name': 'Astreus_hygrometricus'}, {'id': 21392, 'synset': "bird's-nest_fungus.n.01", 'name': "bird's-nest_fungus"}, {'id': 21393, 'synset': 'gastrocybe_lateritia.n.01', 'name': 'Gastrocybe_lateritia'}, {'id': 21394, 'synset': 'macowanites_americanus.n.01', 'name': 'Macowanites_americanus'}, {'id': 21395, 'synset': 'polypore.n.01', 'name': 'polypore'}, {'id': 21396, 'synset': 'bracket_fungus.n.01', 'name': 'bracket_fungus'}, {'id': 21397, 'synset': 'albatrellus_dispansus.n.01', 'name': 'Albatrellus_dispansus'}, {'id': 21398, 'synset': 'albatrellus_ovinus.n.01', 'name': 'Albatrellus_ovinus'}, {'id': 21399, 'synset': 'neolentinus_ponderosus.n.01', 'name': 'Neolentinus_ponderosus'}, {'id': 21400, 'synset': 'oligoporus_leucospongia.n.01', 'name': 'Oligoporus_leucospongia'}, {'id': 21401, 'synset': 'polyporus_tenuiculus.n.01', 'name': 'Polyporus_tenuiculus'}, {'id': 21402, 'synset': 'hen-of-the-woods.n.01', 'name': 'hen-of-the-woods'}, {'id': 21403, 'synset': 'polyporus_squamosus.n.01', 'name': 'Polyporus_squamosus'}, {'id': 21404, 'synset': 'beefsteak_fungus.n.01', 'name': 'beefsteak_fungus'}, {'id': 21405, 'synset': 'agaric.n.01', 'name': 'agaric'}, {'id': 21406, 'synset': 'bolete.n.01', 'name': 'bolete'}, {'id': 21407, 'synset': 'boletus_chrysenteron.n.01', 'name': 'Boletus_chrysenteron'}, {'id': 21408, 'synset': 'boletus_edulis.n.01', 'name': 'Boletus_edulis'}, {'id': 21409, 'synset': "frost's_bolete.n.01", 'name': "Frost's_bolete"}, {'id': 21410, 'synset': 'boletus_luridus.n.01', 'name': 'Boletus_luridus'}, {'id': 21411, 'synset': 'boletus_mirabilis.n.01', 'name': 'Boletus_mirabilis'}, {'id': 21412, 'synset': 'boletus_pallidus.n.01', 'name': 'Boletus_pallidus'}, {'id': 21413, 'synset': 'boletus_pulcherrimus.n.01', 'name': 'Boletus_pulcherrimus'}, {'id': 21414, 'synset': 'boletus_pulverulentus.n.01', 'name': 'Boletus_pulverulentus'}, {'id': 21415, 'synset': 'boletus_roxanae.n.01', 'name': 'Boletus_roxanae'}, {'id': 21416, 'synset': 'boletus_subvelutipes.n.01', 'name': 'Boletus_subvelutipes'}, {'id': 21417, 'synset': 'boletus_variipes.n.01', 'name': 'Boletus_variipes'}, {'id': 21418, 'synset': 'boletus_zelleri.n.01', 'name': 'Boletus_zelleri'}, {'id': 21419, 'synset': 'fuscoboletinus_paluster.n.01', 'name': 'Fuscoboletinus_paluster'}, {'id': 21420, 'synset': 'fuscoboletinus_serotinus.n.01', 'name': 'Fuscoboletinus_serotinus'}, {'id': 21421, 'synset': 'leccinum_fibrillosum.n.01', 'name': 'Leccinum_fibrillosum'}, {'id': 21422, 'synset': 'suillus_albivelatus.n.01', 'name': 'Suillus_albivelatus'}, {'id': 21423, 'synset': 'old-man-of-the-woods.n.01', 'name': 'old-man-of-the-woods'}, {'id': 21424, 'synset': 'boletellus_russellii.n.01', 'name': 'Boletellus_russellii'}, {'id': 21425, 'synset': 'jelly_fungus.n.01', 'name': 'jelly_fungus'}, {'id': 21426, 'synset': 'snow_mushroom.n.01', 'name': 'snow_mushroom'}, {'id': 21427, 'synset': "witches'_butter.n.01", 'name': "witches'_butter"}, {'id': 21428, 'synset': 'tremella_foliacea.n.01', 'name': 'Tremella_foliacea'}, {'id': 21429, 'synset': 'tremella_reticulata.n.01', 'name': 'Tremella_reticulata'}, {'id': 21430, 'synset': "jew's-ear.n.01", 'name': "Jew's-ear"}, {'id': 21431, 'synset': 'rust.n.04', 'name': 'rust'}, {'id': 21432, 'synset': 'aecium.n.01', 'name': 'aecium'}, {'id': 21433, 'synset': 'flax_rust.n.01', 'name': 'flax_rust'}, {'id': 21434, 'synset': 'blister_rust.n.02', 'name': 'blister_rust'}, {'id': 21435, 'synset': 'wheat_rust.n.01', 'name': 'wheat_rust'}, {'id': 21436, 'synset': 'apple_rust.n.01', 'name': 'apple_rust'}, {'id': 21437, 'synset': 'smut.n.03', 'name': 'smut'}, {'id': 21438, 'synset': 'covered_smut.n.01', 'name': 'covered_smut'}, {'id': 21439, 'synset': 'loose_smut.n.02', 'name': 'loose_smut'}, {'id': 21440, 'synset': 'cornsmut.n.01', 'name': 'cornsmut'}, {'id': 21441, 'synset': 'boil_smut.n.01', 'name': 'boil_smut'}, {'id': 21442, 'synset': 'sphacelotheca.n.01', 'name': 'Sphacelotheca'}, {'id': 21443, 'synset': 'head_smut.n.01', 'name': 'head_smut'}, {'id': 21444, 'synset': 'bunt.n.04', 'name': 'bunt'}, {'id': 21445, 'synset': 'bunt.n.03', 'name': 'bunt'}, {'id': 21446, 'synset': 'onion_smut.n.01', 'name': 'onion_smut'}, {'id': 21447, 'synset': 'flag_smut_fungus.n.01', 'name': 'flag_smut_fungus'}, {'id': 21448, 'synset': 'wheat_flag_smut.n.01', 'name': 'wheat_flag_smut'}, {'id': 21449, 'synset': 'felt_fungus.n.01', 'name': 'felt_fungus'}, {'id': 21450, 'synset': 'waxycap.n.01', 'name': 'waxycap'}, {'id': 21451, 'synset': 'hygrocybe_acutoconica.n.01', 'name': 'Hygrocybe_acutoconica'}, {'id': 21452, 'synset': 'hygrophorus_borealis.n.01', 'name': 'Hygrophorus_borealis'}, {'id': 21453, 'synset': 'hygrophorus_caeruleus.n.01', 'name': 'Hygrophorus_caeruleus'}, {'id': 21454, 'synset': 'hygrophorus_inocybiformis.n.01', 'name': 'Hygrophorus_inocybiformis'}, {'id': 21455, 'synset': 'hygrophorus_kauffmanii.n.01', 'name': 'Hygrophorus_kauffmanii'}, {'id': 21456, 'synset': 'hygrophorus_marzuolus.n.01', 'name': 'Hygrophorus_marzuolus'}, {'id': 21457, 'synset': 'hygrophorus_purpurascens.n.01', 'name': 'Hygrophorus_purpurascens'}, {'id': 21458, 'synset': 'hygrophorus_russula.n.01', 'name': 'Hygrophorus_russula'}, {'id': 21459, 'synset': 'hygrophorus_sordidus.n.01', 'name': 'Hygrophorus_sordidus'}, {'id': 21460, 'synset': 'hygrophorus_tennesseensis.n.01', 'name': 'Hygrophorus_tennesseensis'}, {'id': 21461, 'synset': 'hygrophorus_turundus.n.01', 'name': 'Hygrophorus_turundus'}, {'id': 21462, 'synset': 'neohygrophorus_angelesianus.n.01', 'name': 'Neohygrophorus_angelesianus'}, {'id': 21463, 'synset': 'cortinarius_armillatus.n.01', 'name': 'Cortinarius_armillatus'}, {'id': 21464, 'synset': 'cortinarius_atkinsonianus.n.01', 'name': 'Cortinarius_atkinsonianus'}, {'id': 21465, 'synset': 'cortinarius_corrugatus.n.01', 'name': 'Cortinarius_corrugatus'}, {'id': 21466, 'synset': 'cortinarius_gentilis.n.01', 'name': 'Cortinarius_gentilis'}, {'id': 21467, 'synset': 'cortinarius_mutabilis.n.01', 'name': 'Cortinarius_mutabilis'}, {'id': 21468, 'synset': 'cortinarius_semisanguineus.n.01', 'name': 'Cortinarius_semisanguineus'}, {'id': 21469, 'synset': 'cortinarius_subfoetidus.n.01', 'name': 'Cortinarius_subfoetidus'}, {'id': 21470, 'synset': 'cortinarius_violaceus.n.01', 'name': 'Cortinarius_violaceus'}, {'id': 21471, 'synset': 'gymnopilus_spectabilis.n.01', 'name': 'Gymnopilus_spectabilis'}, {'id': 21472, 'synset': 'gymnopilus_validipes.n.01', 'name': 'Gymnopilus_validipes'}, {'id': 21473, 'synset': 'gymnopilus_ventricosus.n.01', 'name': 'Gymnopilus_ventricosus'}, {'id': 21474, 'synset': 'mold.n.05', 'name': 'mold'}, {'id': 21475, 'synset': 'mildew.n.02', 'name': 'mildew'}, {'id': 21476, 'synset': 'verticillium.n.01', 'name': 'verticillium'}, {'id': 21477, 'synset': 'monilia.n.01', 'name': 'monilia'}, {'id': 21478, 'synset': 'candida.n.01', 'name': 'candida'}, {'id': 21479, 'synset': 'candida_albicans.n.01', 'name': 'Candida_albicans'}, {'id': 21480, 'synset': 'blastomycete.n.01', 'name': 'blastomycete'}, {'id': 21481, 'synset': 'yellow_spot_fungus.n.01', 'name': 'yellow_spot_fungus'}, {'id': 21482, 'synset': 'green_smut_fungus.n.01', 'name': 'green_smut_fungus'}, {'id': 21483, 'synset': 'dry_rot.n.02', 'name': 'dry_rot'}, {'id': 21484, 'synset': 'rhizoctinia.n.01', 'name': 'rhizoctinia'}, {'id': 21485, 'synset': 'houseplant.n.01', 'name': 'houseplant'}, {'id': 21486, 'synset': 'bedder.n.01', 'name': 'bedder'}, {'id': 21487, 'synset': 'succulent.n.01', 'name': 'succulent'}, {'id': 21488, 'synset': 'cultivar.n.01', 'name': 'cultivar'}, {'id': 21489, 'synset': 'weed.n.01', 'name': 'weed'}, {'id': 21490, 'synset': 'wort.n.01', 'name': 'wort'}, {'id': 21491, 'synset': 'brier.n.02', 'name': 'brier'}, {'id': 21492, 'synset': 'aril.n.01', 'name': 'aril'}, {'id': 21493, 'synset': 'sporophyll.n.01', 'name': 'sporophyll'}, {'id': 21494, 'synset': 'sporangium.n.01', 'name': 'sporangium'}, {'id': 21495, 'synset': 'sporangiophore.n.01', 'name': 'sporangiophore'}, {'id': 21496, 'synset': 'ascus.n.01', 'name': 'ascus'}, {'id': 21497, 'synset': 'ascospore.n.01', 'name': 'ascospore'}, {'id': 21498, 'synset': 'arthrospore.n.02', 'name': 'arthrospore'}, {'id': 21499, 'synset': 'eusporangium.n.01', 'name': 'eusporangium'}, {'id': 21500, 'synset': 'tetrasporangium.n.01', 'name': 'tetrasporangium'}, {'id': 21501, 'synset': 'gametangium.n.01', 'name': 'gametangium'}, {'id': 21502, 'synset': 'sorus.n.02', 'name': 'sorus'}, {'id': 21503, 'synset': 'sorus.n.01', 'name': 'sorus'}, {'id': 21504, 'synset': 'partial_veil.n.01', 'name': 'partial_veil'}, {'id': 21505, 'synset': 'lignum.n.01', 'name': 'lignum'}, {'id': 21506, 'synset': 'vascular_ray.n.01', 'name': 'vascular_ray'}, {'id': 21507, 'synset': 'phloem.n.01', 'name': 'phloem'}, {'id': 21508, 'synset': 'evergreen.n.01', 'name': 'evergreen'}, {'id': 21509, 'synset': 'deciduous_plant.n.01', 'name': 'deciduous_plant'}, {'id': 21510, 'synset': 'poisonous_plant.n.01', 'name': 'poisonous_plant'}, {'id': 21511, 'synset': 'vine.n.01', 'name': 'vine'}, {'id': 21512, 'synset': 'creeper.n.01', 'name': 'creeper'}, {'id': 21513, 'synset': 'tendril.n.01', 'name': 'tendril'}, {'id': 21514, 'synset': 'root_climber.n.01', 'name': 'root_climber'}, {'id': 21515, 'synset': 'lignosae.n.01', 'name': 'lignosae'}, {'id': 21516, 'synset': 'arborescent_plant.n.01', 'name': 'arborescent_plant'}, {'id': 21517, 'synset': 'snag.n.02', 'name': 'snag'}, {'id': 21518, 'synset': 'tree.n.01', 'name': 'tree'}, {'id': 21519, 'synset': 'timber_tree.n.01', 'name': 'timber_tree'}, {'id': 21520, 'synset': 'treelet.n.01', 'name': 'treelet'}, {'id': 21521, 'synset': 'arbor.n.01', 'name': 'arbor'}, {'id': 21522, 'synset': 'bean_tree.n.01', 'name': 'bean_tree'}, {'id': 21523, 'synset': 'pollard.n.01', 'name': 'pollard'}, {'id': 21524, 'synset': 'sapling.n.01', 'name': 'sapling'}, {'id': 21525, 'synset': 'shade_tree.n.01', 'name': 'shade_tree'}, {'id': 21526, 'synset': 'gymnospermous_tree.n.01', 'name': 'gymnospermous_tree'}, {'id': 21527, 'synset': 'conifer.n.01', 'name': 'conifer'}, {'id': 21528, 'synset': 'angiospermous_tree.n.01', 'name': 'angiospermous_tree'}, {'id': 21529, 'synset': 'nut_tree.n.01', 'name': 'nut_tree'}, {'id': 21530, 'synset': 'spice_tree.n.01', 'name': 'spice_tree'}, {'id': 21531, 'synset': 'fever_tree.n.01', 'name': 'fever_tree'}, {'id': 21532, 'synset': 'stump.n.01', 'name': 'stump'}, {'id': 21533, 'synset': 'bonsai.n.01', 'name': 'bonsai'}, {'id': 21534, 'synset': 'ming_tree.n.02', 'name': 'ming_tree'}, {'id': 21535, 'synset': 'ming_tree.n.01', 'name': 'ming_tree'}, {'id': 21536, 'synset': 'undershrub.n.01', 'name': 'undershrub'}, {'id': 21537, 'synset': 'subshrub.n.01', 'name': 'subshrub'}, {'id': 21538, 'synset': 'bramble.n.01', 'name': 'bramble'}, {'id': 21539, 'synset': 'liana.n.01', 'name': 'liana'}, {'id': 21540, 'synset': 'geophyte.n.01', 'name': 'geophyte'}, {'id': 21541, 'synset': 'desert_plant.n.01', 'name': 'desert_plant'}, {'id': 21542, 'synset': 'mesophyte.n.01', 'name': 'mesophyte'}, {'id': 21543, 'synset': 'marsh_plant.n.01', 'name': 'marsh_plant'}, {'id': 21544, 'synset': 'hemiepiphyte.n.01', 'name': 'hemiepiphyte'}, {'id': 21545, 'synset': 'strangler.n.01', 'name': 'strangler'}, {'id': 21546, 'synset': 'lithophyte.n.01', 'name': 'lithophyte'}, {'id': 21547, 'synset': 'saprobe.n.01', 'name': 'saprobe'}, {'id': 21548, 'synset': 'autophyte.n.01', 'name': 'autophyte'}, {'id': 21549, 'synset': 'root.n.01', 'name': 'root'}, {'id': 21550, 'synset': 'taproot.n.01', 'name': 'taproot'}, {'id': 21551, 'synset': 'prop_root.n.01', 'name': 'prop_root'}, {'id': 21552, 'synset': 'prophyll.n.01', 'name': 'prophyll'}, {'id': 21553, 'synset': 'rootstock.n.02', 'name': 'rootstock'}, {'id': 21554, 'synset': 'quickset.n.01', 'name': 'quickset'}, {'id': 21555, 'synset': 'stolon.n.01', 'name': 'stolon'}, {'id': 21556, 'synset': 'tuberous_plant.n.01', 'name': 'tuberous_plant'}, {'id': 21557, 'synset': 'rhizome.n.01', 'name': 'rhizome'}, {'id': 21558, 'synset': 'rachis.n.01', 'name': 'rachis'}, {'id': 21559, 'synset': 'caudex.n.02', 'name': 'caudex'}, {'id': 21560, 'synset': 'cladode.n.01', 'name': 'cladode'}, {'id': 21561, 'synset': 'receptacle.n.02', 'name': 'receptacle'}, {'id': 21562, 'synset': 'scape.n.01', 'name': 'scape'}, {'id': 21563, 'synset': 'umbel.n.01', 'name': 'umbel'}, {'id': 21564, 'synset': 'petiole.n.01', 'name': 'petiole'}, {'id': 21565, 'synset': 'peduncle.n.02', 'name': 'peduncle'}, {'id': 21566, 'synset': 'pedicel.n.01', 'name': 'pedicel'}, {'id': 21567, 'synset': 'flower_cluster.n.01', 'name': 'flower_cluster'}, {'id': 21568, 'synset': 'raceme.n.01', 'name': 'raceme'}, {'id': 21569, 'synset': 'panicle.n.01', 'name': 'panicle'}, {'id': 21570, 'synset': 'thyrse.n.01', 'name': 'thyrse'}, {'id': 21571, 'synset': 'cyme.n.01', 'name': 'cyme'}, {'id': 21572, 'synset': 'cymule.n.01', 'name': 'cymule'}, {'id': 21573, 'synset': 'glomerule.n.01', 'name': 'glomerule'}, {'id': 21574, 'synset': 'scorpioid_cyme.n.01', 'name': 'scorpioid_cyme'}, {'id': 21575, 'synset': 'ear.n.05', 'name': 'ear'}, {'id': 21576, 'synset': 'spadix.n.01', 'name': 'spadix'}, {'id': 21577, 'synset': 'bulbous_plant.n.01', 'name': 'bulbous_plant'}, {'id': 21578, 'synset': 'bulbil.n.01', 'name': 'bulbil'}, {'id': 21579, 'synset': 'cormous_plant.n.01', 'name': 'cormous_plant'}, {'id': 21580, 'synset': 'fruit.n.01', 'name': 'fruit'}, {'id': 21581, 'synset': 'fruitlet.n.01', 'name': 'fruitlet'}, {'id': 21582, 'synset': 'seed.n.01', 'name': 'seed'}, {'id': 21583, 'synset': 'bean.n.02', 'name': 'bean'}, {'id': 21584, 'synset': 'nut.n.01', 'name': 'nut'}, {'id': 21585, 'synset': 'nutlet.n.01', 'name': 'nutlet'}, {'id': 21586, 'synset': 'kernel.n.01', 'name': 'kernel'}, {'id': 21587, 'synset': 'syconium.n.01', 'name': 'syconium'}, {'id': 21588, 'synset': 'berry.n.02', 'name': 'berry'}, {'id': 21589, 'synset': 'aggregate_fruit.n.01', 'name': 'aggregate_fruit'}, {'id': 21590, 'synset': 'simple_fruit.n.01', 'name': 'simple_fruit'}, {'id': 21591, 'synset': 'acinus.n.01', 'name': 'acinus'}, {'id': 21592, 'synset': 'drupe.n.01', 'name': 'drupe'}, {'id': 21593, 'synset': 'drupelet.n.01', 'name': 'drupelet'}, {'id': 21594, 'synset': 'pome.n.01', 'name': 'pome'}, {'id': 21595, 'synset': 'pod.n.02', 'name': 'pod'}, {'id': 21596, 'synset': 'loment.n.01', 'name': 'loment'}, {'id': 21597, 'synset': 'pyxidium.n.01', 'name': 'pyxidium'}, {'id': 21598, 'synset': 'husk.n.02', 'name': 'husk'}, {'id': 21599, 'synset': 'cornhusk.n.01', 'name': 'cornhusk'}, {'id': 21600, 'synset': 'pod.n.01', 'name': 'pod'}, {'id': 21601, 'synset': 'accessory_fruit.n.01', 'name': 'accessory_fruit'}, {'id': 21602, 'synset': 'buckthorn.n.01', 'name': 'buckthorn'}, {'id': 21603, 'synset': 'buckthorn_berry.n.01', 'name': 'buckthorn_berry'}, {'id': 21604, 'synset': 'cascara_buckthorn.n.01', 'name': 'cascara_buckthorn'}, {'id': 21605, 'synset': 'cascara.n.01', 'name': 'cascara'}, {'id': 21606, 'synset': 'carolina_buckthorn.n.01', 'name': 'Carolina_buckthorn'}, {'id': 21607, 'synset': 'coffeeberry.n.01', 'name': 'coffeeberry'}, {'id': 21608, 'synset': 'redberry.n.01', 'name': 'redberry'}, {'id': 21609, 'synset': 'nakedwood.n.01', 'name': 'nakedwood'}, {'id': 21610, 'synset': 'jujube.n.01', 'name': 'jujube'}, {'id': 21611, 'synset': "christ's-thorn.n.01", 'name': "Christ's-thorn"}, {'id': 21612, 'synset': 'hazel.n.01', 'name': 'hazel'}, {'id': 21613, 'synset': 'fox_grape.n.01', 'name': 'fox_grape'}, {'id': 21614, 'synset': 'muscadine.n.01', 'name': 'muscadine'}, {'id': 21615, 'synset': 'vinifera.n.01', 'name': 'vinifera'}, {'id': 21616, 'synset': 'pinot_blanc.n.01', 'name': 'Pinot_blanc'}, {'id': 21617, 'synset': 'sauvignon_grape.n.01', 'name': 'Sauvignon_grape'}, {'id': 21618, 'synset': 'sauvignon_blanc.n.01', 'name': 'Sauvignon_blanc'}, {'id': 21619, 'synset': 'muscadet.n.01', 'name': 'Muscadet'}, {'id': 21620, 'synset': 'riesling.n.01', 'name': 'Riesling'}, {'id': 21621, 'synset': 'zinfandel.n.01', 'name': 'Zinfandel'}, {'id': 21622, 'synset': 'chenin_blanc.n.01', 'name': 'Chenin_blanc'}, {'id': 21623, 'synset': 'malvasia.n.01', 'name': 'malvasia'}, {'id': 21624, 'synset': 'verdicchio.n.01', 'name': 'Verdicchio'}, {'id': 21625, 'synset': 'boston_ivy.n.01', 'name': 'Boston_ivy'}, {'id': 21626, 'synset': 'virginia_creeper.n.01', 'name': 'Virginia_creeper'}, {'id': 21627, 'synset': 'true_pepper.n.01', 'name': 'true_pepper'}, {'id': 21628, 'synset': 'betel.n.01', 'name': 'betel'}, {'id': 21629, 'synset': 'cubeb.n.01', 'name': 'cubeb'}, {'id': 21630, 'synset': 'schizocarp.n.01', 'name': 'schizocarp'}, {'id': 21631, 'synset': 'peperomia.n.01', 'name': 'peperomia'}, {'id': 21632, 'synset': 'watermelon_begonia.n.01', 'name': 'watermelon_begonia'}, {'id': 21633, 'synset': 'yerba_mansa.n.01', 'name': 'yerba_mansa'}, {'id': 21634, 'synset': 'pinna.n.01', 'name': 'pinna'}, {'id': 21635, 'synset': 'frond.n.01', 'name': 'frond'}, {'id': 21636, 'synset': 'bract.n.01', 'name': 'bract'}, {'id': 21637, 'synset': 'bracteole.n.01', 'name': 'bracteole'}, {'id': 21638, 'synset': 'involucre.n.01', 'name': 'involucre'}, {'id': 21639, 'synset': 'glume.n.01', 'name': 'glume'}, {'id': 21640, 'synset': 'palmate_leaf.n.01', 'name': 'palmate_leaf'}, {'id': 21641, 'synset': 'pinnate_leaf.n.01', 'name': 'pinnate_leaf'}, {'id': 21642, 'synset': 'bijugate_leaf.n.01', 'name': 'bijugate_leaf'}, {'id': 21643, 'synset': 'decompound_leaf.n.01', 'name': 'decompound_leaf'}, {'id': 21644, 'synset': 'acuminate_leaf.n.01', 'name': 'acuminate_leaf'}, {'id': 21645, 'synset': 'deltoid_leaf.n.01', 'name': 'deltoid_leaf'}, {'id': 21646, 'synset': 'ensiform_leaf.n.01', 'name': 'ensiform_leaf'}, {'id': 21647, 'synset': 'linear_leaf.n.01', 'name': 'linear_leaf'}, {'id': 21648, 'synset': 'lyrate_leaf.n.01', 'name': 'lyrate_leaf'}, {'id': 21649, 'synset': 'obtuse_leaf.n.01', 'name': 'obtuse_leaf'}, {'id': 21650, 'synset': 'oblanceolate_leaf.n.01', 'name': 'oblanceolate_leaf'}, {'id': 21651, 'synset': 'pandurate_leaf.n.01', 'name': 'pandurate_leaf'}, {'id': 21652, 'synset': 'reniform_leaf.n.01', 'name': 'reniform_leaf'}, {'id': 21653, 'synset': 'spatulate_leaf.n.01', 'name': 'spatulate_leaf'}, {'id': 21654, 'synset': 'even-pinnate_leaf.n.01', 'name': 'even-pinnate_leaf'}, {'id': 21655, 'synset': 'odd-pinnate_leaf.n.01', 'name': 'odd-pinnate_leaf'}, {'id': 21656, 'synset': 'pedate_leaf.n.01', 'name': 'pedate_leaf'}, {'id': 21657, 'synset': 'crenate_leaf.n.01', 'name': 'crenate_leaf'}, {'id': 21658, 'synset': 'dentate_leaf.n.01', 'name': 'dentate_leaf'}, {'id': 21659, 'synset': 'denticulate_leaf.n.01', 'name': 'denticulate_leaf'}, {'id': 21660, 'synset': 'erose_leaf.n.01', 'name': 'erose_leaf'}, {'id': 21661, 'synset': 'runcinate_leaf.n.01', 'name': 'runcinate_leaf'}, {'id': 21662, 'synset': 'prickly-edged_leaf.n.01', 'name': 'prickly-edged_leaf'}, {'id': 21663, 'synset': 'deadwood.n.01', 'name': 'deadwood'}, {'id': 21664, 'synset': 'haulm.n.01', 'name': 'haulm'}, {'id': 21665, 'synset': 'branchlet.n.01', 'name': 'branchlet'}, {'id': 21666, 'synset': 'osier.n.01', 'name': 'osier'}, {'id': 21667, 'synset': 'giant_scrambling_fern.n.01', 'name': 'giant_scrambling_fern'}, {'id': 21668, 'synset': 'umbrella_fern.n.01', 'name': 'umbrella_fern'}, {'id': 21669, 'synset': 'floating_fern.n.02', 'name': 'floating_fern'}, {'id': 21670, 'synset': 'polypody.n.01', 'name': 'polypody'}, {'id': 21671, 'synset': 'licorice_fern.n.01', 'name': 'licorice_fern'}, {'id': 21672, 'synset': 'grey_polypody.n.01', 'name': 'grey_polypody'}, {'id': 21673, 'synset': 'leatherleaf.n.01', 'name': 'leatherleaf'}, {'id': 21674, 'synset': 'rock_polypody.n.01', 'name': 'rock_polypody'}, {'id': 21675, 'synset': 'common_polypody.n.01', 'name': 'common_polypody'}, {'id': 21676, 'synset': "bear's-paw_fern.n.01", 'name': "bear's-paw_fern"}, {'id': 21677, 'synset': 'strap_fern.n.01', 'name': 'strap_fern'}, {'id': 21678, 'synset': 'florida_strap_fern.n.01', 'name': 'Florida_strap_fern'}, {'id': 21679, 'synset': 'basket_fern.n.02', 'name': 'basket_fern'}, {'id': 21680, 'synset': 'snake_polypody.n.01', 'name': 'snake_polypody'}, {'id': 21681, 'synset': "climbing_bird's_nest_fern.n.01", 'name': "climbing_bird's_nest_fern"}, {'id': 21682, 'synset': 'golden_polypody.n.01', 'name': 'golden_polypody'}, {'id': 21683, 'synset': 'staghorn_fern.n.01', 'name': 'staghorn_fern'}, {'id': 21684, 'synset': 'south_american_staghorn.n.01', 'name': 'South_American_staghorn'}, {'id': 21685, 'synset': 'common_staghorn_fern.n.01', 'name': 'common_staghorn_fern'}, {'id': 21686, 'synset': 'felt_fern.n.01', 'name': 'felt_fern'}, {'id': 21687, 'synset': 'potato_fern.n.02', 'name': 'potato_fern'}, {'id': 21688, 'synset': 'myrmecophyte.n.01', 'name': 'myrmecophyte'}, {'id': 21689, 'synset': 'grass_fern.n.01', 'name': 'grass_fern'}, {'id': 21690, 'synset': 'spleenwort.n.01', 'name': 'spleenwort'}, {'id': 21691, 'synset': 'black_spleenwort.n.01', 'name': 'black_spleenwort'}, {'id': 21692, 'synset': "bird's_nest_fern.n.01", 'name': "bird's_nest_fern"}, {'id': 21693, 'synset': 'ebony_spleenwort.n.01', 'name': 'ebony_spleenwort'}, {'id': 21694, 'synset': 'black-stem_spleenwort.n.01', 'name': 'black-stem_spleenwort'}, {'id': 21695, 'synset': 'walking_fern.n.01', 'name': 'walking_fern'}, {'id': 21696, 'synset': 'green_spleenwort.n.01', 'name': 'green_spleenwort'}, {'id': 21697, 'synset': 'mountain_spleenwort.n.01', 'name': 'mountain_spleenwort'}, {'id': 21698, 'synset': 'lobed_spleenwort.n.01', 'name': 'lobed_spleenwort'}, {'id': 21699, 'synset': 'lanceolate_spleenwort.n.01', 'name': 'lanceolate_spleenwort'}, {'id': 21700, 'synset': "hart's-tongue.n.02", 'name': "hart's-tongue"}, {'id': 21701, 'synset': 'scale_fern.n.01', 'name': 'scale_fern'}, {'id': 21702, 'synset': 'scolopendrium.n.01', 'name': 'scolopendrium'}, {'id': 21703, 'synset': 'deer_fern.n.01', 'name': 'deer_fern'}, {'id': 21704, 'synset': 'doodia.n.01', 'name': 'doodia'}, {'id': 21705, 'synset': 'chain_fern.n.01', 'name': 'chain_fern'}, {'id': 21706, 'synset': 'virginia_chain_fern.n.01', 'name': 'Virginia_chain_fern'}, {'id': 21707, 'synset': 'silver_tree_fern.n.01', 'name': 'silver_tree_fern'}, {'id': 21708, 'synset': 'davallia.n.01', 'name': 'davallia'}, {'id': 21709, 'synset': "hare's-foot_fern.n.01", 'name': "hare's-foot_fern"}, {'id': 21710, 'synset': "canary_island_hare's_foot_fern.n.01", 'name': "Canary_Island_hare's_foot_fern"}, {'id': 21711, 'synset': "squirrel's-foot_fern.n.01", 'name': "squirrel's-foot_fern"}, {'id': 21712, 'synset': 'bracken.n.01', 'name': 'bracken'}, {'id': 21713, 'synset': 'soft_tree_fern.n.01', 'name': 'soft_tree_fern'}, {'id': 21714, 'synset': 'scythian_lamb.n.01', 'name': 'Scythian_lamb'}, {'id': 21715, 'synset': 'false_bracken.n.01', 'name': 'false_bracken'}, {'id': 21716, 'synset': 'thyrsopteris.n.01', 'name': 'thyrsopteris'}, {'id': 21717, 'synset': 'shield_fern.n.01', 'name': 'shield_fern'}, {'id': 21718, 'synset': 'broad_buckler-fern.n.01', 'name': 'broad_buckler-fern'}, {'id': 21719, 'synset': 'fragrant_cliff_fern.n.01', 'name': 'fragrant_cliff_fern'}, {'id': 21720, 'synset': "goldie's_fern.n.01", 'name': "Goldie's_fern"}, {'id': 21721, 'synset': 'wood_fern.n.01', 'name': 'wood_fern'}, {'id': 21722, 'synset': 'male_fern.n.01', 'name': 'male_fern'}, {'id': 21723, 'synset': 'marginal_wood_fern.n.01', 'name': 'marginal_wood_fern'}, {'id': 21724, 'synset': 'mountain_male_fern.n.01', 'name': 'mountain_male_fern'}, {'id': 21725, 'synset': 'lady_fern.n.01', 'name': 'lady_fern'}, {'id': 21726, 'synset': 'alpine_lady_fern.n.01', 'name': 'Alpine_lady_fern'}, {'id': 21727, 'synset': 'silvery_spleenwort.n.02', 'name': 'silvery_spleenwort'}, {'id': 21728, 'synset': 'holly_fern.n.02', 'name': 'holly_fern'}, {'id': 21729, 'synset': 'bladder_fern.n.01', 'name': 'bladder_fern'}, {'id': 21730, 'synset': 'brittle_bladder_fern.n.01', 'name': 'brittle_bladder_fern'}, {'id': 21731, 'synset': 'mountain_bladder_fern.n.01', 'name': 'mountain_bladder_fern'}, {'id': 21732, 'synset': 'bulblet_fern.n.01', 'name': 'bulblet_fern'}, {'id': 21733, 'synset': 'silvery_spleenwort.n.01', 'name': 'silvery_spleenwort'}, {'id': 21734, 'synset': 'oak_fern.n.01', 'name': 'oak_fern'}, {'id': 21735, 'synset': 'limestone_fern.n.01', 'name': 'limestone_fern'}, {'id': 21736, 'synset': 'ostrich_fern.n.01', 'name': 'ostrich_fern'}, {'id': 21737, 'synset': "hart's-tongue.n.01", 'name': "hart's-tongue"}, {'id': 21738, 'synset': 'sensitive_fern.n.01', 'name': 'sensitive_fern'}, {'id': 21739, 'synset': 'christmas_fern.n.01', 'name': 'Christmas_fern'}, {'id': 21740, 'synset': 'holly_fern.n.01', 'name': 'holly_fern'}, {'id': 21741, 'synset': "braun's_holly_fern.n.01", 'name': "Braun's_holly_fern"}, {'id': 21742, 'synset': 'western_holly_fern.n.01', 'name': 'western_holly_fern'}, {'id': 21743, 'synset': 'soft_shield_fern.n.01', 'name': 'soft_shield_fern'}, {'id': 21744, 'synset': 'leather_fern.n.02', 'name': 'leather_fern'}, {'id': 21745, 'synset': 'button_fern.n.02', 'name': 'button_fern'}, {'id': 21746, 'synset': 'indian_button_fern.n.01', 'name': 'Indian_button_fern'}, {'id': 21747, 'synset': 'woodsia.n.01', 'name': 'woodsia'}, {'id': 21748, 'synset': 'rusty_woodsia.n.01', 'name': 'rusty_woodsia'}, {'id': 21749, 'synset': 'alpine_woodsia.n.01', 'name': 'Alpine_woodsia'}, {'id': 21750, 'synset': 'smooth_woodsia.n.01', 'name': 'smooth_woodsia'}, {'id': 21751, 'synset': 'boston_fern.n.01', 'name': 'Boston_fern'}, {'id': 21752, 'synset': 'basket_fern.n.01', 'name': 'basket_fern'}, {'id': 21753, 'synset': 'golden_fern.n.02', 'name': 'golden_fern'}, {'id': 21754, 'synset': 'maidenhair.n.01', 'name': 'maidenhair'}, {'id': 21755, 'synset': 'common_maidenhair.n.01', 'name': 'common_maidenhair'}, {'id': 21756, 'synset': 'american_maidenhair_fern.n.01', 'name': 'American_maidenhair_fern'}, {'id': 21757, 'synset': 'bermuda_maidenhair.n.01', 'name': 'Bermuda_maidenhair'}, {'id': 21758, 'synset': 'brittle_maidenhair.n.01', 'name': 'brittle_maidenhair'}, {'id': 21759, 'synset': 'farley_maidenhair.n.01', 'name': 'Farley_maidenhair'}, {'id': 21760, 'synset': 'annual_fern.n.01', 'name': 'annual_fern'}, {'id': 21761, 'synset': 'lip_fern.n.01', 'name': 'lip_fern'}, {'id': 21762, 'synset': 'smooth_lip_fern.n.01', 'name': 'smooth_lip_fern'}, {'id': 21763, 'synset': 'lace_fern.n.01', 'name': 'lace_fern'}, {'id': 21764, 'synset': 'wooly_lip_fern.n.01', 'name': 'wooly_lip_fern'}, {'id': 21765, 'synset': 'southwestern_lip_fern.n.01', 'name': 'southwestern_lip_fern'}, {'id': 21766, 'synset': 'bamboo_fern.n.01', 'name': 'bamboo_fern'}, {'id': 21767, 'synset': 'american_rock_brake.n.01', 'name': 'American_rock_brake'}, {'id': 21768, 'synset': 'european_parsley_fern.n.01', 'name': 'European_parsley_fern'}, {'id': 21769, 'synset': 'hand_fern.n.01', 'name': 'hand_fern'}, {'id': 21770, 'synset': 'cliff_brake.n.01', 'name': 'cliff_brake'}, {'id': 21771, 'synset': 'coffee_fern.n.01', 'name': 'coffee_fern'}, {'id': 21772, 'synset': 'purple_rock_brake.n.01', 'name': 'purple_rock_brake'}, {'id': 21773, 'synset': "bird's-foot_fern.n.01", 'name': "bird's-foot_fern"}, {'id': 21774, 'synset': 'button_fern.n.01', 'name': 'button_fern'}, {'id': 21775, 'synset': 'silver_fern.n.02', 'name': 'silver_fern'}, {'id': 21776, 'synset': 'golden_fern.n.01', 'name': 'golden_fern'}, {'id': 21777, 'synset': 'gold_fern.n.01', 'name': 'gold_fern'}, {'id': 21778, 'synset': 'pteris_cretica.n.01', 'name': 'Pteris_cretica'}, {'id': 21779, 'synset': 'spider_brake.n.01', 'name': 'spider_brake'}, {'id': 21780, 'synset': 'ribbon_fern.n.01', 'name': 'ribbon_fern'}, {'id': 21781, 'synset': 'potato_fern.n.01', 'name': 'potato_fern'}, {'id': 21782, 'synset': 'angiopteris.n.01', 'name': 'angiopteris'}, {'id': 21783, 'synset': 'skeleton_fork_fern.n.01', 'name': 'skeleton_fork_fern'}, {'id': 21784, 'synset': 'horsetail.n.01', 'name': 'horsetail'}, {'id': 21785, 'synset': 'common_horsetail.n.01', 'name': 'common_horsetail'}, {'id': 21786, 'synset': 'swamp_horsetail.n.01', 'name': 'swamp_horsetail'}, {'id': 21787, 'synset': 'scouring_rush.n.01', 'name': 'scouring_rush'}, {'id': 21788, 'synset': 'marsh_horsetail.n.01', 'name': 'marsh_horsetail'}, {'id': 21789, 'synset': 'wood_horsetail.n.01', 'name': 'wood_horsetail'}, {'id': 21790, 'synset': 'variegated_horsetail.n.01', 'name': 'variegated_horsetail'}, {'id': 21791, 'synset': 'club_moss.n.01', 'name': 'club_moss'}, {'id': 21792, 'synset': 'shining_clubmoss.n.01', 'name': 'shining_clubmoss'}, {'id': 21793, 'synset': 'alpine_clubmoss.n.01', 'name': 'alpine_clubmoss'}, {'id': 21794, 'synset': 'fir_clubmoss.n.01', 'name': 'fir_clubmoss'}, {'id': 21795, 'synset': 'ground_cedar.n.01', 'name': 'ground_cedar'}, {'id': 21796, 'synset': 'ground_fir.n.01', 'name': 'ground_fir'}, {'id': 21797, 'synset': 'foxtail_grass.n.01', 'name': 'foxtail_grass'}, {'id': 21798, 'synset': 'spikemoss.n.01', 'name': 'spikemoss'}, {'id': 21799, 'synset': 'meadow_spikemoss.n.01', 'name': 'meadow_spikemoss'}, {'id': 21800, 'synset': 'desert_selaginella.n.01', 'name': 'desert_selaginella'}, {'id': 21801, 'synset': 'resurrection_plant.n.01', 'name': 'resurrection_plant'}, {'id': 21802, 'synset': 'florida_selaginella.n.01', 'name': 'florida_selaginella'}, {'id': 21803, 'synset': 'quillwort.n.01', 'name': 'quillwort'}, {'id': 21804, 'synset': 'earthtongue.n.01', 'name': 'earthtongue'}, {'id': 21805, 'synset': 'snuffbox_fern.n.01', 'name': 'snuffbox_fern'}, {'id': 21806, 'synset': 'christella.n.01', 'name': 'christella'}, {'id': 21807, 'synset': 'mountain_fern.n.01', 'name': 'mountain_fern'}, {'id': 21808, 'synset': 'new_york_fern.n.01', 'name': 'New_York_fern'}, {'id': 21809, 'synset': 'massachusetts_fern.n.01', 'name': 'Massachusetts_fern'}, {'id': 21810, 'synset': 'beech_fern.n.01', 'name': 'beech_fern'}, {'id': 21811, 'synset': 'broad_beech_fern.n.01', 'name': 'broad_beech_fern'}, {'id': 21812, 'synset': 'long_beech_fern.n.01', 'name': 'long_beech_fern'}, {'id': 21813, 'synset': 'shoestring_fungus.n.01', 'name': 'shoestring_fungus'}, {'id': 21814, 'synset': 'armillaria_caligata.n.01', 'name': 'Armillaria_caligata'}, {'id': 21815, 'synset': 'armillaria_ponderosa.n.01', 'name': 'Armillaria_ponderosa'}, {'id': 21816, 'synset': 'armillaria_zelleri.n.01', 'name': 'Armillaria_zelleri'}, {'id': 21817, 'synset': 'honey_mushroom.n.01', 'name': 'honey_mushroom'}, {'id': 21818, 'synset': 'milkweed.n.01', 'name': 'milkweed'}, {'id': 21819, 'synset': 'white_milkweed.n.01', 'name': 'white_milkweed'}, {'id': 21820, 'synset': 'poke_milkweed.n.01', 'name': 'poke_milkweed'}, {'id': 21821, 'synset': 'swamp_milkweed.n.01', 'name': 'swamp_milkweed'}, {'id': 21822, 'synset': "mead's_milkweed.n.01", 'name': "Mead's_milkweed"}, {'id': 21823, 'synset': 'purple_silkweed.n.01', 'name': 'purple_silkweed'}, {'id': 21824, 'synset': 'showy_milkweed.n.01', 'name': 'showy_milkweed'}, {'id': 21825, 'synset': 'poison_milkweed.n.01', 'name': 'poison_milkweed'}, {'id': 21826, 'synset': 'butterfly_weed.n.01', 'name': 'butterfly_weed'}, {'id': 21827, 'synset': 'whorled_milkweed.n.01', 'name': 'whorled_milkweed'}, {'id': 21828, 'synset': 'cruel_plant.n.01', 'name': 'cruel_plant'}, {'id': 21829, 'synset': 'wax_plant.n.01', 'name': 'wax_plant'}, {'id': 21830, 'synset': 'silk_vine.n.01', 'name': 'silk_vine'}, {'id': 21831, 'synset': 'stapelia.n.01', 'name': 'stapelia'}, {'id': 21832, 'synset': 'stapelias_asterias.n.01', 'name': 'Stapelias_asterias'}, {'id': 21833, 'synset': 'stephanotis.n.01', 'name': 'stephanotis'}, {'id': 21834, 'synset': 'madagascar_jasmine.n.01', 'name': 'Madagascar_jasmine'}, {'id': 21835, 'synset': 'negro_vine.n.01', 'name': 'negro_vine'}, {'id': 21836, 'synset': 'zygospore.n.01', 'name': 'zygospore'}, {'id': 21837, 'synset': 'tree_of_knowledge.n.01', 'name': 'tree_of_knowledge'}, {'id': 21838, 'synset': 'orangery.n.01', 'name': 'orangery'}, {'id': 21839, 'synset': 'pocketbook.n.01', 'name': 'pocketbook'}, {'id': 21840, 'synset': 'shit.n.04', 'name': 'shit'}, {'id': 21841, 'synset': 'cordage.n.01', 'name': 'cordage'}, {'id': 21842, 'synset': 'yard.n.01', 'name': 'yard'}, {'id': 21843, 'synset': 'extremum.n.02', 'name': 'extremum'}, {'id': 21844, 'synset': 'leaf_shape.n.01', 'name': 'leaf_shape'}, {'id': 21845, 'synset': 'equilateral.n.01', 'name': 'equilateral'}, {'id': 21846, 'synset': 'figure.n.06', 'name': 'figure'}, {'id': 21847, 'synset': 'pencil.n.03', 'name': 'pencil'}, {'id': 21848, 'synset': 'plane_figure.n.01', 'name': 'plane_figure'}, {'id': 21849, 'synset': 'solid_figure.n.01', 'name': 'solid_figure'}, {'id': 21850, 'synset': 'line.n.04', 'name': 'line'}, {'id': 21851, 'synset': 'bulb.n.04', 'name': 'bulb'}, {'id': 21852, 'synset': 'convex_shape.n.01', 'name': 'convex_shape'}, {'id': 21853, 'synset': 'concave_shape.n.01', 'name': 'concave_shape'}, {'id': 21854, 'synset': 'cylinder.n.01', 'name': 'cylinder'}, {'id': 21855, 'synset': 'round_shape.n.01', 'name': 'round_shape'}, {'id': 21856, 'synset': 'heart.n.07', 'name': 'heart'}, {'id': 21857, 'synset': 'polygon.n.01', 'name': 'polygon'}, {'id': 21858, 'synset': 'convex_polygon.n.01', 'name': 'convex_polygon'}, {'id': 21859, 'synset': 'concave_polygon.n.01', 'name': 'concave_polygon'}, {'id': 21860, 'synset': 'reentrant_polygon.n.01', 'name': 'reentrant_polygon'}, {'id': 21861, 'synset': 'amorphous_shape.n.01', 'name': 'amorphous_shape'}, {'id': 21862, 'synset': 'closed_curve.n.01', 'name': 'closed_curve'}, {'id': 21863, 'synset': 'simple_closed_curve.n.01', 'name': 'simple_closed_curve'}, {'id': 21864, 'synset': 's-shape.n.01', 'name': 'S-shape'}, {'id': 21865, 'synset': 'wave.n.07', 'name': 'wave'}, {'id': 21866, 'synset': 'extrados.n.01', 'name': 'extrados'}, {'id': 21867, 'synset': 'hook.n.02', 'name': 'hook'}, {'id': 21868, 'synset': 'envelope.n.03', 'name': 'envelope'}, {'id': 21869, 'synset': 'bight.n.02', 'name': 'bight'}, {'id': 21870, 'synset': 'diameter.n.02', 'name': 'diameter'}, {'id': 21871, 'synset': 'cone.n.02', 'name': 'cone'}, {'id': 21872, 'synset': 'funnel.n.01', 'name': 'funnel'}, {'id': 21873, 'synset': 'oblong.n.01', 'name': 'oblong'}, {'id': 21874, 'synset': 'circle.n.01', 'name': 'circle'}, {'id': 21875, 'synset': 'circle.n.03', 'name': 'circle'}, {'id': 21876, 'synset': 'equator.n.02', 'name': 'equator'}, {'id': 21877, 'synset': 'scallop.n.01', 'name': 'scallop'}, {'id': 21878, 'synset': 'ring.n.02', 'name': 'ring'}, {'id': 21879, 'synset': 'loop.n.02', 'name': 'loop'}, {'id': 21880, 'synset': 'bight.n.01', 'name': 'bight'}, {'id': 21881, 'synset': 'helix.n.01', 'name': 'helix'}, {'id': 21882, 'synset': 'element_of_a_cone.n.01', 'name': 'element_of_a_cone'}, {'id': 21883, 'synset': 'element_of_a_cylinder.n.01', 'name': 'element_of_a_cylinder'}, {'id': 21884, 'synset': 'ellipse.n.01', 'name': 'ellipse'}, {'id': 21885, 'synset': 'quadrate.n.02', 'name': 'quadrate'}, {'id': 21886, 'synset': 'triangle.n.01', 'name': 'triangle'}, {'id': 21887, 'synset': 'acute_triangle.n.01', 'name': 'acute_triangle'}, {'id': 21888, 'synset': 'isosceles_triangle.n.01', 'name': 'isosceles_triangle'}, {'id': 21889, 'synset': 'obtuse_triangle.n.01', 'name': 'obtuse_triangle'}, {'id': 21890, 'synset': 'right_triangle.n.01', 'name': 'right_triangle'}, {'id': 21891, 'synset': 'scalene_triangle.n.01', 'name': 'scalene_triangle'}, {'id': 21892, 'synset': 'parallel.n.03', 'name': 'parallel'}, {'id': 21893, 'synset': 'trapezoid.n.01', 'name': 'trapezoid'}, {'id': 21894, 'synset': 'star.n.05', 'name': 'star'}, {'id': 21895, 'synset': 'pentagon.n.03', 'name': 'pentagon'}, {'id': 21896, 'synset': 'hexagon.n.01', 'name': 'hexagon'}, {'id': 21897, 'synset': 'heptagon.n.01', 'name': 'heptagon'}, {'id': 21898, 'synset': 'octagon.n.01', 'name': 'octagon'}, {'id': 21899, 'synset': 'nonagon.n.01', 'name': 'nonagon'}, {'id': 21900, 'synset': 'decagon.n.01', 'name': 'decagon'}, {'id': 21901, 'synset': 'rhombus.n.01', 'name': 'rhombus'}, {'id': 21902, 'synset': 'spherical_polygon.n.01', 'name': 'spherical_polygon'}, {'id': 21903, 'synset': 'spherical_triangle.n.01', 'name': 'spherical_triangle'}, {'id': 21904, 'synset': 'convex_polyhedron.n.01', 'name': 'convex_polyhedron'}, {'id': 21905, 'synset': 'concave_polyhedron.n.01', 'name': 'concave_polyhedron'}, {'id': 21906, 'synset': 'cuboid.n.01', 'name': 'cuboid'}, {'id': 21907, 'synset': 'quadrangular_prism.n.01', 'name': 'quadrangular_prism'}, {'id': 21908, 'synset': 'bell.n.05', 'name': 'bell'}, {'id': 21909, 'synset': 'angular_distance.n.01', 'name': 'angular_distance'}, {'id': 21910, 'synset': 'true_anomaly.n.01', 'name': 'true_anomaly'}, {'id': 21911, 'synset': 'spherical_angle.n.01', 'name': 'spherical_angle'}, {'id': 21912, 'synset': 'angle_of_refraction.n.01', 'name': 'angle_of_refraction'}, {'id': 21913, 'synset': 'acute_angle.n.01', 'name': 'acute_angle'}, {'id': 21914, 'synset': 'groove.n.01', 'name': 'groove'}, {'id': 21915, 'synset': 'rut.n.01', 'name': 'rut'}, {'id': 21916, 'synset': 'bulge.n.01', 'name': 'bulge'}, {'id': 21917, 'synset': 'belly.n.03', 'name': 'belly'}, {'id': 21918, 'synset': 'bow.n.05', 'name': 'bow'}, {'id': 21919, 'synset': 'crescent.n.01', 'name': 'crescent'}, {'id': 21920, 'synset': 'ellipsoid.n.01', 'name': 'ellipsoid'}, {'id': 21921, 'synset': 'hypotenuse.n.01', 'name': 'hypotenuse'}, {'id': 21922, 'synset': 'balance.n.04', 'name': 'balance'}, {'id': 21923, 'synset': 'conformation.n.01', 'name': 'conformation'}, {'id': 21924, 'synset': 'symmetry.n.02', 'name': 'symmetry'}, {'id': 21925, 'synset': 'spheroid.n.01', 'name': 'spheroid'}, {'id': 21926, 'synset': 'spherule.n.01', 'name': 'spherule'}, {'id': 21927, 'synset': 'toroid.n.01', 'name': 'toroid'}, {'id': 21928, 'synset': 'column.n.04', 'name': 'column'}, {'id': 21929, 'synset': 'barrel.n.03', 'name': 'barrel'}, {'id': 21930, 'synset': 'pipe.n.03', 'name': 'pipe'}, {'id': 21931, 'synset': 'pellet.n.01', 'name': 'pellet'}, {'id': 21932, 'synset': 'bolus.n.01', 'name': 'bolus'}, {'id': 21933, 'synset': 'dewdrop.n.01', 'name': 'dewdrop'}, {'id': 21934, 'synset': 'ridge.n.02', 'name': 'ridge'}, {'id': 21935, 'synset': 'rim.n.01', 'name': 'rim'}, {'id': 21936, 'synset': 'taper.n.01', 'name': 'taper'}, {'id': 21937, 'synset': 'boundary.n.02', 'name': 'boundary'}, {'id': 21938, 'synset': 'incisure.n.01', 'name': 'incisure'}, {'id': 21939, 'synset': 'notch.n.01', 'name': 'notch'}, {'id': 21940, 'synset': 'wrinkle.n.01', 'name': 'wrinkle'}, {'id': 21941, 'synset': 'dermatoglyphic.n.01', 'name': 'dermatoglyphic'}, {'id': 21942, 'synset': 'frown_line.n.01', 'name': 'frown_line'}, {'id': 21943, 'synset': 'line_of_life.n.01', 'name': 'line_of_life'}, {'id': 21944, 'synset': 'line_of_heart.n.01', 'name': 'line_of_heart'}, {'id': 21945, 'synset': 'crevice.n.01', 'name': 'crevice'}, {'id': 21946, 'synset': 'cleft.n.01', 'name': 'cleft'}, {'id': 21947, 'synset': 'roulette.n.01', 'name': 'roulette'}, {'id': 21948, 'synset': 'node.n.01', 'name': 'node'}, {'id': 21949, 'synset': 'tree.n.02', 'name': 'tree'}, {'id': 21950, 'synset': 'stemma.n.01', 'name': 'stemma'}, {'id': 21951, 'synset': 'brachium.n.01', 'name': 'brachium'}, {'id': 21952, 'synset': 'fork.n.03', 'name': 'fork'}, {'id': 21953, 'synset': 'block.n.03', 'name': 'block'}, {'id': 21954, 'synset': 'ovoid.n.01', 'name': 'ovoid'}, {'id': 21955, 'synset': 'tetrahedron.n.01', 'name': 'tetrahedron'}, {'id': 21956, 'synset': 'pentahedron.n.01', 'name': 'pentahedron'}, {'id': 21957, 'synset': 'hexahedron.n.01', 'name': 'hexahedron'}, {'id': 21958, 'synset': 'regular_polyhedron.n.01', 'name': 'regular_polyhedron'}, {'id': 21959, 'synset': 'polyhedral_angle.n.01', 'name': 'polyhedral_angle'}, {'id': 21960, 'synset': 'cube.n.01', 'name': 'cube'}, {'id': 21961, 'synset': 'truncated_pyramid.n.01', 'name': 'truncated_pyramid'}, {'id': 21962, 'synset': 'truncated_cone.n.01', 'name': 'truncated_cone'}, {'id': 21963, 'synset': 'tail.n.03', 'name': 'tail'}, {'id': 21964, 'synset': 'tongue.n.03', 'name': 'tongue'}, {'id': 21965, 'synset': 'trapezohedron.n.01', 'name': 'trapezohedron'}, {'id': 21966, 'synset': 'wedge.n.01', 'name': 'wedge'}, {'id': 21967, 'synset': 'keel.n.01', 'name': 'keel'}, {'id': 21968, 'synset': 'place.n.06', 'name': 'place'}, {'id': 21969, 'synset': 'herpes.n.01', 'name': 'herpes'}, {'id': 21970, 'synset': 'chlamydia.n.01', 'name': 'chlamydia'}, {'id': 21971, 'synset': 'wall.n.04', 'name': 'wall'}, {'id': 21972, 'synset': 'micronutrient.n.01', 'name': 'micronutrient'}, {'id': 21973, 'synset': 'chyme.n.01', 'name': 'chyme'}, {'id': 21974, 'synset': 'ragweed_pollen.n.01', 'name': 'ragweed_pollen'}, {'id': 21975, 'synset': 'pina_cloth.n.01', 'name': 'pina_cloth'}, {'id': 21976, 'synset': 'chlorobenzylidenemalononitrile.n.01', 'name': 'chlorobenzylidenemalononitrile'}, {'id': 21977, 'synset': 'carbon.n.01', 'name': 'carbon'}, {'id': 21978, 'synset': 'charcoal.n.01', 'name': 'charcoal'}, {'id': 21979, 'synset': 'rock.n.02', 'name': 'rock'}, {'id': 21980, 'synset': 'gravel.n.01', 'name': 'gravel'}, {'id': 21981, 'synset': 'aflatoxin.n.01', 'name': 'aflatoxin'}, {'id': 21982, 'synset': 'alpha-tocopheral.n.01', 'name': 'alpha-tocopheral'}, {'id': 21983, 'synset': 'leopard.n.01', 'name': 'leopard'}, {'id': 21984, 'synset': 'bricks_and_mortar.n.01', 'name': 'bricks_and_mortar'}, {'id': 21985, 'synset': 'lagging.n.01', 'name': 'lagging'}, {'id': 21986, 'synset': 'hydraulic_cement.n.01', 'name': 'hydraulic_cement'}, {'id': 21987, 'synset': 'choline.n.01', 'name': 'choline'}, {'id': 21988, 'synset': 'concrete.n.01', 'name': 'concrete'}, {'id': 21989, 'synset': 'glass_wool.n.01', 'name': 'glass_wool'}, {'id': 21990, 'synset': 'soil.n.02', 'name': 'soil'}, {'id': 21991, 'synset': 'high_explosive.n.01', 'name': 'high_explosive'}, {'id': 21992, 'synset': 'litter.n.02', 'name': 'litter'}, {'id': 21993, 'synset': 'fish_meal.n.01', 'name': 'fish_meal'}, {'id': 21994, 'synset': 'greek_fire.n.01', 'name': 'Greek_fire'}, {'id': 21995, 'synset': 'culture_medium.n.01', 'name': 'culture_medium'}, {'id': 21996, 'synset': 'agar.n.01', 'name': 'agar'}, {'id': 21997, 'synset': 'blood_agar.n.01', 'name': 'blood_agar'}, {'id': 21998, 'synset': 'hip_tile.n.01', 'name': 'hip_tile'}, {'id': 21999, 'synset': 'hyacinth.n.01', 'name': 'hyacinth'}, {'id': 22000, 'synset': 'hydroxide_ion.n.01', 'name': 'hydroxide_ion'}, {'id': 22001, 'synset': 'ice.n.01', 'name': 'ice'}, {'id': 22002, 'synset': 'inositol.n.01', 'name': 'inositol'}, {'id': 22003, 'synset': 'linoleum.n.01', 'name': 'linoleum'}, {'id': 22004, 'synset': 'lithia_water.n.01', 'name': 'lithia_water'}, {'id': 22005, 'synset': 'lodestone.n.01', 'name': 'lodestone'}, {'id': 22006, 'synset': 'pantothenic_acid.n.01', 'name': 'pantothenic_acid'}, {'id': 22007, 'synset': 'paper.n.01', 'name': 'paper'}, {'id': 22008, 'synset': 'papyrus.n.01', 'name': 'papyrus'}, {'id': 22009, 'synset': 'pantile.n.01', 'name': 'pantile'}, {'id': 22010, 'synset': 'blacktop.n.01', 'name': 'blacktop'}, {'id': 22011, 'synset': 'tarmacadam.n.01', 'name': 'tarmacadam'}, {'id': 22012, 'synset': 'paving.n.01', 'name': 'paving'}, {'id': 22013, 'synset': 'plaster.n.01', 'name': 'plaster'}, {'id': 22014, 'synset': 'poison_gas.n.01', 'name': 'poison_gas'}, {'id': 22015, 'synset': 'ridge_tile.n.01', 'name': 'ridge_tile'}, {'id': 22016, 'synset': 'roughcast.n.01', 'name': 'roughcast'}, {'id': 22017, 'synset': 'sand.n.01', 'name': 'sand'}, {'id': 22018, 'synset': 'spackle.n.01', 'name': 'spackle'}, {'id': 22019, 'synset': 'render.n.01', 'name': 'render'}, {'id': 22020, 'synset': 'wattle_and_daub.n.01', 'name': 'wattle_and_daub'}, {'id': 22021, 'synset': 'stucco.n.01', 'name': 'stucco'}, {'id': 22022, 'synset': 'tear_gas.n.01', 'name': 'tear_gas'}, {'id': 22023, 'synset': 'linseed.n.01', 'name': 'linseed'}, {'id': 22024, 'synset': 'vitamin.n.01', 'name': 'vitamin'}, {'id': 22025, 'synset': 'fat-soluble_vitamin.n.01', 'name': 'fat-soluble_vitamin'}, {'id': 22026, 'synset': 'water-soluble_vitamin.n.01', 'name': 'water-soluble_vitamin'}, {'id': 22027, 'synset': 'vitamin_a.n.01', 'name': 'vitamin_A'}, {'id': 22028, 'synset': 'vitamin_a1.n.01', 'name': 'vitamin_A1'}, {'id': 22029, 'synset': 'vitamin_a2.n.01', 'name': 'vitamin_A2'}, {'id': 22030, 'synset': 'b-complex_vitamin.n.01', 'name': 'B-complex_vitamin'}, {'id': 22031, 'synset': 'vitamin_b1.n.01', 'name': 'vitamin_B1'}, {'id': 22032, 'synset': 'vitamin_b12.n.01', 'name': 'vitamin_B12'}, {'id': 22033, 'synset': 'vitamin_b2.n.01', 'name': 'vitamin_B2'}, {'id': 22034, 'synset': 'vitamin_b6.n.01', 'name': 'vitamin_B6'}, {'id': 22035, 'synset': 'vitamin_bc.n.01', 'name': 'vitamin_Bc'}, {'id': 22036, 'synset': 'niacin.n.01', 'name': 'niacin'}, {'id': 22037, 'synset': 'vitamin_d.n.01', 'name': 'vitamin_D'}, {'id': 22038, 'synset': 'vitamin_e.n.01', 'name': 'vitamin_E'}, {'id': 22039, 'synset': 'biotin.n.01', 'name': 'biotin'}, {'id': 22040, 'synset': 'vitamin_k.n.01', 'name': 'vitamin_K'}, {'id': 22041, 'synset': 'vitamin_k1.n.01', 'name': 'vitamin_K1'}, {'id': 22042, 'synset': 'vitamin_k3.n.01', 'name': 'vitamin_K3'}, {'id': 22043, 'synset': 'vitamin_p.n.01', 'name': 'vitamin_P'}, {'id': 22044, 'synset': 'vitamin_c.n.01', 'name': 'vitamin_C'}, {'id': 22045, 'synset': 'planking.n.01', 'name': 'planking'}, {'id': 22046, 'synset': 'chipboard.n.01', 'name': 'chipboard'}, {'id': 22047, 'synset': 'knothole.n.01', 'name': 'knothole'}] # noqa ================================================ FILE: vldet/data/datasets/lvis_v1.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import os from fvcore.common.timer import Timer from detectron2.structures import BoxMode from fvcore.common.file_io import PathManager from detectron2.data import DatasetCatalog, MetadataCatalog from detectron2.data.datasets.lvis import get_lvis_instances_meta logger = logging.getLogger(__name__) __all__ = ["custom_load_lvis_json", "custom_register_lvis_instances"] def custom_register_lvis_instances(name, metadata, json_file, image_root): """ """ DatasetCatalog.register(name, lambda: custom_load_lvis_json( json_file, image_root, name)) MetadataCatalog.get(name).set( json_file=json_file, image_root=image_root, evaluator_type="lvis", **metadata ) def custom_load_lvis_json(json_file, image_root, dataset_name=None): ''' Modifications: use `file_name` convert neg_category_ids add pos_category_ids ''' from lvis import LVIS json_file = PathManager.get_local_path(json_file) timer = Timer() lvis_api = LVIS(json_file) if timer.seconds() > 1: logger.info("Loading {} takes {:.2f} seconds.".format( json_file, timer.seconds())) catid2contid = {x['id']: i for i, x in enumerate( sorted(lvis_api.dataset['categories'], key=lambda x: x['id']))} if len(lvis_api.dataset['categories']) == 1203: for x in lvis_api.dataset['categories']: assert catid2contid[x['id']] == x['id'] - 1 img_ids = sorted(lvis_api.imgs.keys()) imgs = lvis_api.load_imgs(img_ids) anns = [lvis_api.img_ann_map[img_id] for img_id in img_ids] ann_ids = [ann["id"] for anns_per_image in anns for ann in anns_per_image] assert len(set(ann_ids)) == len(ann_ids), \ "Annotation ids in '{}' are not unique".format(json_file) imgs_anns = list(zip(imgs, anns)) logger.info("Loaded {} images in the LVIS v1 format from {}".format( len(imgs_anns), json_file)) dataset_dicts = [] for (img_dict, anno_dict_list) in imgs_anns: record = {} if "file_name" in img_dict: file_name = img_dict["file_name"] if img_dict["file_name"].startswith("COCO"): file_name = file_name[-16:] record["file_name"] = os.path.join(image_root, file_name) elif 'coco_url' in img_dict: # e.g., http://images.cocodataset.org/train2017/000000391895.jpg file_name = img_dict["coco_url"][30:] record["file_name"] = os.path.join(image_root, file_name) elif 'tar_index' in img_dict: record['tar_index'] = img_dict['tar_index'] record["height"] = img_dict["height"] record["width"] = img_dict["width"] record["not_exhaustive_category_ids"] = img_dict.get( "not_exhaustive_category_ids", []) record["neg_category_ids"] = img_dict.get("neg_category_ids", []) # NOTE: modified by Xingyi: convert to 0-based record["neg_category_ids"] = [ catid2contid[x] for x in record["neg_category_ids"]] if 'pos_category_ids' in img_dict: record['pos_category_ids'] = [ catid2contid[x] for x in img_dict.get("pos_category_ids", [])] if 'captions' in img_dict: record['captions'] = img_dict['captions'] if 'caption_features' in img_dict: record['caption_features'] = img_dict['caption_features'] image_id = record["image_id"] = img_dict["id"] objs = [] for anno in anno_dict_list: assert anno["image_id"] == image_id if anno.get('iscrowd', 0) > 0: continue obj = {"bbox": anno["bbox"], "bbox_mode": BoxMode.XYWH_ABS} obj["category_id"] = catid2contid[anno['category_id']] if 'segmentation' in anno: segm = anno["segmentation"] valid_segm = [poly for poly in segm \ if len(poly) % 2 == 0 and len(poly) >= 6] # assert len(segm) == len( # valid_segm # ), "Annotation contains an invalid polygon with < 3 points" if not len(segm) == len(valid_segm): print('Annotation contains an invalid polygon with < 3 points') assert len(segm) > 0 obj["segmentation"] = segm objs.append(obj) record["annotations"] = objs dataset_dicts.append(record) return dataset_dicts _CUSTOM_SPLITS_LVIS = { "lvis_v1_train+coco": ("coco/", "lvis/lvis_v1_train+coco_mask.json"), "lvis_v1_train_norare": ("coco/", "lvis/lvis_v1_train_norare.json"), } for key, (image_root, json_file) in _CUSTOM_SPLITS_LVIS.items(): custom_register_lvis_instances( key, get_lvis_instances_meta(key), os.path.join("datasets", json_file) if "://" not in json_file else json_file, os.path.join("datasets", image_root), ) def get_lvis_22k_meta(): from .lvis_22k_categories import CATEGORIES cat_ids = [k["id"] for k in CATEGORIES] assert min(cat_ids) == 1 and max(cat_ids) == len( cat_ids ), "Category ids are not in [1, #categories], as expected" # Ensure that the category list is sorted by id lvis_categories = sorted(CATEGORIES, key=lambda x: x["id"]) thing_classes = [k["name"] for k in lvis_categories] meta = {"thing_classes": thing_classes} return meta _CUSTOM_SPLITS_LVIS_22K = { "lvis_v1_train_22k": ("coco/", "lvis/lvis_v1_train_lvis-22k.json"), } for key, (image_root, json_file) in _CUSTOM_SPLITS_LVIS_22K.items(): custom_register_lvis_instances( key, get_lvis_22k_meta(), os.path.join("datasets", json_file) if "://" not in json_file else json_file, os.path.join("datasets", image_root), ) ================================================ FILE: vldet/data/datasets/objects365.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. from detectron2.data.datasets.register_coco import register_coco_instances import os # categories_v2 = [ # {'id': 1, 'name': 'Person'}, # {'id': 2, 'name': 'Sneakers'}, # {'id': 3, 'name': 'Chair'}, # {'id': 4, 'name': 'Other Shoes'}, # {'id': 5, 'name': 'Hat'}, # {'id': 6, 'name': 'Car'}, # {'id': 7, 'name': 'Lamp'}, # {'id': 8, 'name': 'Glasses'}, # {'id': 9, 'name': 'Bottle'}, # {'id': 10, 'name': 'Desk'}, # {'id': 11, 'name': 'Cup'}, # {'id': 12, 'name': 'Street Lights'}, # {'id': 13, 'name': 'Cabinet/shelf'}, # {'id': 14, 'name': 'Handbag/Satchel'}, # {'id': 15, 'name': 'Bracelet'}, # {'id': 16, 'name': 'Plate'}, # {'id': 17, 'name': 'Picture/Frame'}, # {'id': 18, 'name': 'Helmet'}, # {'id': 19, 'name': 'Book'}, # {'id': 20, 'name': 'Gloves'}, # {'id': 21, 'name': 'Storage box'}, # {'id': 22, 'name': 'Boat'}, # {'id': 23, 'name': 'Leather Shoes'}, # {'id': 24, 'name': 'Flower'}, # {'id': 25, 'name': 'Bench'}, # {'id': 26, 'name': 'Potted Plant'}, # {'id': 27, 'name': 'Bowl/Basin'}, # {'id': 28, 'name': 'Flag'}, # {'id': 29, 'name': 'Pillow'}, # {'id': 30, 'name': 'Boots'}, # {'id': 31, 'name': 'Vase'}, # {'id': 32, 'name': 'Microphone'}, # {'id': 33, 'name': 'Necklace'}, # {'id': 34, 'name': 'Ring'}, # {'id': 35, 'name': 'SUV'}, # {'id': 36, 'name': 'Wine Glass'}, # {'id': 37, 'name': 'Belt'}, # {'id': 38, 'name': 'Moniter/TV'}, # {'id': 39, 'name': 'Backpack'}, # {'id': 40, 'name': 'Umbrella'}, # {'id': 41, 'name': 'Traffic Light'}, # {'id': 42, 'name': 'Speaker'}, # {'id': 43, 'name': 'Watch'}, # {'id': 44, 'name': 'Tie'}, # {'id': 45, 'name': 'Trash bin Can'}, # {'id': 46, 'name': 'Slippers'}, # {'id': 47, 'name': 'Bicycle'}, # {'id': 48, 'name': 'Stool'}, # {'id': 49, 'name': 'Barrel/bucket'}, # {'id': 50, 'name': 'Van'}, # {'id': 51, 'name': 'Couch'}, # {'id': 52, 'name': 'Sandals'}, # {'id': 53, 'name': 'Bakset'}, # {'id': 54, 'name': 'Drum'}, # {'id': 55, 'name': 'Pen/Pencil'}, # {'id': 56, 'name': 'Bus'}, # {'id': 57, 'name': 'Wild Bird'}, # {'id': 58, 'name': 'High Heels'}, # {'id': 59, 'name': 'Motorcycle'}, # {'id': 60, 'name': 'Guitar'}, # {'id': 61, 'name': 'Carpet'}, # {'id': 62, 'name': 'Cell Phone'}, # {'id': 63, 'name': 'Bread'}, # {'id': 64, 'name': 'Camera'}, # {'id': 65, 'name': 'Canned'}, # {'id': 66, 'name': 'Truck'}, # {'id': 67, 'name': 'Traffic cone'}, # {'id': 68, 'name': 'Cymbal'}, # {'id': 69, 'name': 'Lifesaver'}, # {'id': 70, 'name': 'Towel'}, # {'id': 71, 'name': 'Stuffed Toy'}, # {'id': 72, 'name': 'Candle'}, # {'id': 73, 'name': 'Sailboat'}, # {'id': 74, 'name': 'Laptop'}, # {'id': 75, 'name': 'Awning'}, # {'id': 76, 'name': 'Bed'}, # {'id': 77, 'name': 'Faucet'}, # {'id': 78, 'name': 'Tent'}, # {'id': 79, 'name': 'Horse'}, # {'id': 80, 'name': 'Mirror'}, # {'id': 81, 'name': 'Power outlet'}, # {'id': 82, 'name': 'Sink'}, # {'id': 83, 'name': 'Apple'}, # {'id': 84, 'name': 'Air Conditioner'}, # {'id': 85, 'name': 'Knife'}, # {'id': 86, 'name': 'Hockey Stick'}, # {'id': 87, 'name': 'Paddle'}, # {'id': 88, 'name': 'Pickup Truck'}, # {'id': 89, 'name': 'Fork'}, # {'id': 90, 'name': 'Traffic Sign'}, # {'id': 91, 'name': 'Ballon'}, # {'id': 92, 'name': 'Tripod'}, # {'id': 93, 'name': 'Dog'}, # {'id': 94, 'name': 'Spoon'}, # {'id': 95, 'name': 'Clock'}, # {'id': 96, 'name': 'Pot'}, # {'id': 97, 'name': 'Cow'}, # {'id': 98, 'name': 'Cake'}, # {'id': 99, 'name': 'Dinning Table'}, # {'id': 100, 'name': 'Sheep'}, # {'id': 101, 'name': 'Hanger'}, # {'id': 102, 'name': 'Blackboard/Whiteboard'}, # {'id': 103, 'name': 'Napkin'}, # {'id': 104, 'name': 'Other Fish'}, # {'id': 105, 'name': 'Orange/Tangerine'}, # {'id': 106, 'name': 'Toiletry'}, # {'id': 107, 'name': 'Keyboard'}, # {'id': 108, 'name': 'Tomato'}, # {'id': 109, 'name': 'Lantern'}, # {'id': 110, 'name': 'Machinery Vehicle'}, # {'id': 111, 'name': 'Fan'}, # {'id': 112, 'name': 'Green Vegetables'}, # {'id': 113, 'name': 'Banana'}, # {'id': 114, 'name': 'Baseball Glove'}, # {'id': 115, 'name': 'Airplane'}, # {'id': 116, 'name': 'Mouse'}, # {'id': 117, 'name': 'Train'}, # {'id': 118, 'name': 'Pumpkin'}, # {'id': 119, 'name': 'Soccer'}, # {'id': 120, 'name': 'Skiboard'}, # {'id': 121, 'name': 'Luggage'}, # {'id': 122, 'name': 'Nightstand'}, # {'id': 123, 'name': 'Tea pot'}, # {'id': 124, 'name': 'Telephone'}, # {'id': 125, 'name': 'Trolley'}, # {'id': 126, 'name': 'Head Phone'}, # {'id': 127, 'name': 'Sports Car'}, # {'id': 128, 'name': 'Stop Sign'}, # {'id': 129, 'name': 'Dessert'}, # {'id': 130, 'name': 'Scooter'}, # {'id': 131, 'name': 'Stroller'}, # {'id': 132, 'name': 'Crane'}, # {'id': 133, 'name': 'Remote'}, # {'id': 134, 'name': 'Refrigerator'}, # {'id': 135, 'name': 'Oven'}, # {'id': 136, 'name': 'Lemon'}, # {'id': 137, 'name': 'Duck'}, # {'id': 138, 'name': 'Baseball Bat'}, # {'id': 139, 'name': 'Surveillance Camera'}, # {'id': 140, 'name': 'Cat'}, # {'id': 141, 'name': 'Jug'}, # {'id': 142, 'name': 'Broccoli'}, # {'id': 143, 'name': 'Piano'}, # {'id': 144, 'name': 'Pizza'}, # {'id': 145, 'name': 'Elephant'}, # {'id': 146, 'name': 'Skateboard'}, # {'id': 147, 'name': 'Surfboard'}, # {'id': 148, 'name': 'Gun'}, # {'id': 149, 'name': 'Skating and Skiing shoes'}, # {'id': 150, 'name': 'Gas stove'}, # {'id': 151, 'name': 'Donut'}, # {'id': 152, 'name': 'Bow Tie'}, # {'id': 153, 'name': 'Carrot'}, # {'id': 154, 'name': 'Toilet'}, # {'id': 155, 'name': 'Kite'}, # {'id': 156, 'name': 'Strawberry'}, # {'id': 157, 'name': 'Other Balls'}, # {'id': 158, 'name': 'Shovel'}, # {'id': 159, 'name': 'Pepper'}, # {'id': 160, 'name': 'Computer Box'}, # {'id': 161, 'name': 'Toilet Paper'}, # {'id': 162, 'name': 'Cleaning Products'}, # {'id': 163, 'name': 'Chopsticks'}, # {'id': 164, 'name': 'Microwave'}, # {'id': 165, 'name': 'Pigeon'}, # {'id': 166, 'name': 'Baseball'}, # {'id': 167, 'name': 'Cutting/chopping Board'}, # {'id': 168, 'name': 'Coffee Table'}, # {'id': 169, 'name': 'Side Table'}, # {'id': 170, 'name': 'Scissors'}, # {'id': 171, 'name': 'Marker'}, # {'id': 172, 'name': 'Pie'}, # {'id': 173, 'name': 'Ladder'}, # {'id': 174, 'name': 'Snowboard'}, # {'id': 175, 'name': 'Cookies'}, # {'id': 176, 'name': 'Radiator'}, # {'id': 177, 'name': 'Fire Hydrant'}, # {'id': 178, 'name': 'Basketball'}, # {'id': 179, 'name': 'Zebra'}, # {'id': 180, 'name': 'Grape'}, # {'id': 181, 'name': 'Giraffe'}, # {'id': 182, 'name': 'Potato'}, # {'id': 183, 'name': 'Sausage'}, # {'id': 184, 'name': 'Tricycle'}, # {'id': 185, 'name': 'Violin'}, # {'id': 186, 'name': 'Egg'}, # {'id': 187, 'name': 'Fire Extinguisher'}, # {'id': 188, 'name': 'Candy'}, # {'id': 189, 'name': 'Fire Truck'}, # {'id': 190, 'name': 'Billards'}, # {'id': 191, 'name': 'Converter'}, # {'id': 192, 'name': 'Bathtub'}, # {'id': 193, 'name': 'Wheelchair'}, # {'id': 194, 'name': 'Golf Club'}, # {'id': 195, 'name': 'Briefcase'}, # {'id': 196, 'name': 'Cucumber'}, # {'id': 197, 'name': 'Cigar/Cigarette '}, # {'id': 198, 'name': 'Paint Brush'}, # {'id': 199, 'name': 'Pear'}, # {'id': 200, 'name': 'Heavy Truck'}, # {'id': 201, 'name': 'Hamburger'}, # {'id': 202, 'name': 'Extractor'}, # {'id': 203, 'name': 'Extention Cord'}, # {'id': 204, 'name': 'Tong'}, # {'id': 205, 'name': 'Tennis Racket'}, # {'id': 206, 'name': 'Folder'}, # {'id': 207, 'name': 'American Football'}, # {'id': 208, 'name': 'earphone'}, # {'id': 209, 'name': 'Mask'}, # {'id': 210, 'name': 'Kettle'}, # {'id': 211, 'name': 'Tennis'}, # {'id': 212, 'name': 'Ship'}, # {'id': 213, 'name': 'Swing'}, # {'id': 214, 'name': 'Coffee Machine'}, # {'id': 215, 'name': 'Slide'}, # {'id': 216, 'name': 'Carriage'}, # {'id': 217, 'name': 'Onion'}, # {'id': 218, 'name': 'Green beans'}, # {'id': 219, 'name': 'Projector'}, # {'id': 220, 'name': 'Frisbee'}, # {'id': 221, 'name': 'Washing Machine/Drying Machine'}, # {'id': 222, 'name': 'Chicken'}, # {'id': 223, 'name': 'Printer'}, # {'id': 224, 'name': 'Watermelon'}, # {'id': 225, 'name': 'Saxophone'}, # {'id': 226, 'name': 'Tissue'}, # {'id': 227, 'name': 'Toothbrush'}, # {'id': 228, 'name': 'Ice cream'}, # {'id': 229, 'name': 'Hotair ballon'}, # {'id': 230, 'name': 'Cello'}, # {'id': 231, 'name': 'French Fries'}, # {'id': 232, 'name': 'Scale'}, # {'id': 233, 'name': 'Trophy'}, # {'id': 234, 'name': 'Cabbage'}, # {'id': 235, 'name': 'Hot dog'}, # {'id': 236, 'name': 'Blender'}, # {'id': 237, 'name': 'Peach'}, # {'id': 238, 'name': 'Rice'}, # {'id': 239, 'name': 'Wallet/Purse'}, # {'id': 240, 'name': 'Volleyball'}, # {'id': 241, 'name': 'Deer'}, # {'id': 242, 'name': 'Goose'}, # {'id': 243, 'name': 'Tape'}, # {'id': 244, 'name': 'Tablet'}, # {'id': 245, 'name': 'Cosmetics'}, # {'id': 246, 'name': 'Trumpet'}, # {'id': 247, 'name': 'Pineapple'}, # {'id': 248, 'name': 'Golf Ball'}, # {'id': 249, 'name': 'Ambulance'}, # {'id': 250, 'name': 'Parking meter'}, # {'id': 251, 'name': 'Mango'}, # {'id': 252, 'name': 'Key'}, # {'id': 253, 'name': 'Hurdle'}, # {'id': 254, 'name': 'Fishing Rod'}, # {'id': 255, 'name': 'Medal'}, # {'id': 256, 'name': 'Flute'}, # {'id': 257, 'name': 'Brush'}, # {'id': 258, 'name': 'Penguin'}, # {'id': 259, 'name': 'Megaphone'}, # {'id': 260, 'name': 'Corn'}, # {'id': 261, 'name': 'Lettuce'}, # {'id': 262, 'name': 'Garlic'}, # {'id': 263, 'name': 'Swan'}, # {'id': 264, 'name': 'Helicopter'}, # {'id': 265, 'name': 'Green Onion'}, # {'id': 266, 'name': 'Sandwich'}, # {'id': 267, 'name': 'Nuts'}, # {'id': 268, 'name': 'Speed Limit Sign'}, # {'id': 269, 'name': 'Induction Cooker'}, # {'id': 270, 'name': 'Broom'}, # {'id': 271, 'name': 'Trombone'}, # {'id': 272, 'name': 'Plum'}, # {'id': 273, 'name': 'Rickshaw'}, # {'id': 274, 'name': 'Goldfish'}, # {'id': 275, 'name': 'Kiwi fruit'}, # {'id': 276, 'name': 'Router/modem'}, # {'id': 277, 'name': 'Poker Card'}, # {'id': 278, 'name': 'Toaster'}, # {'id': 279, 'name': 'Shrimp'}, # {'id': 280, 'name': 'Sushi'}, # {'id': 281, 'name': 'Cheese'}, # {'id': 282, 'name': 'Notepaper'}, # {'id': 283, 'name': 'Cherry'}, # {'id': 284, 'name': 'Pliers'}, # {'id': 285, 'name': 'CD'}, # {'id': 286, 'name': 'Pasta'}, # {'id': 287, 'name': 'Hammer'}, # {'id': 288, 'name': 'Cue'}, # {'id': 289, 'name': 'Avocado'}, # {'id': 290, 'name': 'Hamimelon'}, # {'id': 291, 'name': 'Flask'}, # {'id': 292, 'name': 'Mushroon'}, # {'id': 293, 'name': 'Screwdriver'}, # {'id': 294, 'name': 'Soap'}, # {'id': 295, 'name': 'Recorder'}, # {'id': 296, 'name': 'Bear'}, # {'id': 297, 'name': 'Eggplant'}, # {'id': 298, 'name': 'Board Eraser'}, # {'id': 299, 'name': 'Coconut'}, # {'id': 300, 'name': 'Tape Measur/ Ruler'}, # {'id': 301, 'name': 'Pig'}, # {'id': 302, 'name': 'Showerhead'}, # {'id': 303, 'name': 'Globe'}, # {'id': 304, 'name': 'Chips'}, # {'id': 305, 'name': 'Steak'}, # {'id': 306, 'name': 'Crosswalk Sign'}, # {'id': 307, 'name': 'Stapler'}, # {'id': 308, 'name': 'Campel'}, # {'id': 309, 'name': 'Formula 1 '}, # {'id': 310, 'name': 'Pomegranate'}, # {'id': 311, 'name': 'Dishwasher'}, # {'id': 312, 'name': 'Crab'}, # {'id': 313, 'name': 'Hoverboard'}, # {'id': 314, 'name': 'Meat ball'}, # {'id': 315, 'name': 'Rice Cooker'}, # {'id': 316, 'name': 'Tuba'}, # {'id': 317, 'name': 'Calculator'}, # {'id': 318, 'name': 'Papaya'}, # {'id': 319, 'name': 'Antelope'}, # {'id': 320, 'name': 'Parrot'}, # {'id': 321, 'name': 'Seal'}, # {'id': 322, 'name': 'Buttefly'}, # {'id': 323, 'name': 'Dumbbell'}, # {'id': 324, 'name': 'Donkey'}, # {'id': 325, 'name': 'Lion'}, # {'id': 326, 'name': 'Urinal'}, # {'id': 327, 'name': 'Dolphin'}, # {'id': 328, 'name': 'Electric Drill'}, # {'id': 329, 'name': 'Hair Dryer'}, # {'id': 330, 'name': 'Egg tart'}, # {'id': 331, 'name': 'Jellyfish'}, # {'id': 332, 'name': 'Treadmill'}, # {'id': 333, 'name': 'Lighter'}, # {'id': 334, 'name': 'Grapefruit'}, # {'id': 335, 'name': 'Game board'}, # {'id': 336, 'name': 'Mop'}, # {'id': 337, 'name': 'Radish'}, # {'id': 338, 'name': 'Baozi'}, # {'id': 339, 'name': 'Target'}, # {'id': 340, 'name': 'French'}, # {'id': 341, 'name': 'Spring Rolls'}, # {'id': 342, 'name': 'Monkey'}, # {'id': 343, 'name': 'Rabbit'}, # {'id': 344, 'name': 'Pencil Case'}, # {'id': 345, 'name': 'Yak'}, # {'id': 346, 'name': 'Red Cabbage'}, # {'id': 347, 'name': 'Binoculars'}, # {'id': 348, 'name': 'Asparagus'}, # {'id': 349, 'name': 'Barbell'}, # {'id': 350, 'name': 'Scallop'}, # {'id': 351, 'name': 'Noddles'}, # {'id': 352, 'name': 'Comb'}, # {'id': 353, 'name': 'Dumpling'}, # {'id': 354, 'name': 'Oyster'}, # {'id': 355, 'name': 'Table Teniis paddle'}, # {'id': 356, 'name': 'Cosmetics Brush/Eyeliner Pencil'}, # {'id': 357, 'name': 'Chainsaw'}, # {'id': 358, 'name': 'Eraser'}, # {'id': 359, 'name': 'Lobster'}, # {'id': 360, 'name': 'Durian'}, # {'id': 361, 'name': 'Okra'}, # {'id': 362, 'name': 'Lipstick'}, # {'id': 363, 'name': 'Cosmetics Mirror'}, # {'id': 364, 'name': 'Curling'}, # {'id': 365, 'name': 'Table Tennis '}, # ] ''' The official Objects365 category names contains typos. Below is a manual fix. ''' categories_v2_fix = [ {'id': 1, 'name': 'Person'}, {'id': 2, 'name': 'Sneakers'}, {'id': 3, 'name': 'Chair'}, {'id': 4, 'name': 'Other Shoes'}, {'id': 5, 'name': 'Hat'}, {'id': 6, 'name': 'Car'}, {'id': 7, 'name': 'Lamp'}, {'id': 8, 'name': 'Glasses'}, {'id': 9, 'name': 'Bottle'}, {'id': 10, 'name': 'Desk'}, {'id': 11, 'name': 'Cup'}, {'id': 12, 'name': 'Street Lights'}, {'id': 13, 'name': 'Cabinet/shelf'}, {'id': 14, 'name': 'Handbag/Satchel'}, {'id': 15, 'name': 'Bracelet'}, {'id': 16, 'name': 'Plate'}, {'id': 17, 'name': 'Picture/Frame'}, {'id': 18, 'name': 'Helmet'}, {'id': 19, 'name': 'Book'}, {'id': 20, 'name': 'Gloves'}, {'id': 21, 'name': 'Storage box'}, {'id': 22, 'name': 'Boat'}, {'id': 23, 'name': 'Leather Shoes'}, {'id': 24, 'name': 'Flower'}, {'id': 25, 'name': 'Bench'}, {'id': 26, 'name': 'Potted Plant'}, {'id': 27, 'name': 'Bowl/Basin'}, {'id': 28, 'name': 'Flag'}, {'id': 29, 'name': 'Pillow'}, {'id': 30, 'name': 'Boots'}, {'id': 31, 'name': 'Vase'}, {'id': 32, 'name': 'Microphone'}, {'id': 33, 'name': 'Necklace'}, {'id': 34, 'name': 'Ring'}, {'id': 35, 'name': 'SUV'}, {'id': 36, 'name': 'Wine Glass'}, {'id': 37, 'name': 'Belt'}, {'id': 38, 'name': 'Monitor/TV'}, {'id': 39, 'name': 'Backpack'}, {'id': 40, 'name': 'Umbrella'}, {'id': 41, 'name': 'Traffic Light'}, {'id': 42, 'name': 'Speaker'}, {'id': 43, 'name': 'Watch'}, {'id': 44, 'name': 'Tie'}, {'id': 45, 'name': 'Trash bin Can'}, {'id': 46, 'name': 'Slippers'}, {'id': 47, 'name': 'Bicycle'}, {'id': 48, 'name': 'Stool'}, {'id': 49, 'name': 'Barrel/bucket'}, {'id': 50, 'name': 'Van'}, {'id': 51, 'name': 'Couch'}, {'id': 52, 'name': 'Sandals'}, {'id': 53, 'name': 'Basket'}, {'id': 54, 'name': 'Drum'}, {'id': 55, 'name': 'Pen/Pencil'}, {'id': 56, 'name': 'Bus'}, {'id': 57, 'name': 'Wild Bird'}, {'id': 58, 'name': 'High Heels'}, {'id': 59, 'name': 'Motorcycle'}, {'id': 60, 'name': 'Guitar'}, {'id': 61, 'name': 'Carpet'}, {'id': 62, 'name': 'Cell Phone'}, {'id': 63, 'name': 'Bread'}, {'id': 64, 'name': 'Camera'}, {'id': 65, 'name': 'Canned'}, {'id': 66, 'name': 'Truck'}, {'id': 67, 'name': 'Traffic cone'}, {'id': 68, 'name': 'Cymbal'}, {'id': 69, 'name': 'Lifesaver'}, {'id': 70, 'name': 'Towel'}, {'id': 71, 'name': 'Stuffed Toy'}, {'id': 72, 'name': 'Candle'}, {'id': 73, 'name': 'Sailboat'}, {'id': 74, 'name': 'Laptop'}, {'id': 75, 'name': 'Awning'}, {'id': 76, 'name': 'Bed'}, {'id': 77, 'name': 'Faucet'}, {'id': 78, 'name': 'Tent'}, {'id': 79, 'name': 'Horse'}, {'id': 80, 'name': 'Mirror'}, {'id': 81, 'name': 'Power outlet'}, {'id': 82, 'name': 'Sink'}, {'id': 83, 'name': 'Apple'}, {'id': 84, 'name': 'Air Conditioner'}, {'id': 85, 'name': 'Knife'}, {'id': 86, 'name': 'Hockey Stick'}, {'id': 87, 'name': 'Paddle'}, {'id': 88, 'name': 'Pickup Truck'}, {'id': 89, 'name': 'Fork'}, {'id': 90, 'name': 'Traffic Sign'}, {'id': 91, 'name': 'Ballon'}, {'id': 92, 'name': 'Tripod'}, {'id': 93, 'name': 'Dog'}, {'id': 94, 'name': 'Spoon'}, {'id': 95, 'name': 'Clock'}, {'id': 96, 'name': 'Pot'}, {'id': 97, 'name': 'Cow'}, {'id': 98, 'name': 'Cake'}, {'id': 99, 'name': 'Dining Table'}, {'id': 100, 'name': 'Sheep'}, {'id': 101, 'name': 'Hanger'}, {'id': 102, 'name': 'Blackboard/Whiteboard'}, {'id': 103, 'name': 'Napkin'}, {'id': 104, 'name': 'Other Fish'}, {'id': 105, 'name': 'Orange/Tangerine'}, {'id': 106, 'name': 'Toiletry'}, {'id': 107, 'name': 'Keyboard'}, {'id': 108, 'name': 'Tomato'}, {'id': 109, 'name': 'Lantern'}, {'id': 110, 'name': 'Machinery Vehicle'}, {'id': 111, 'name': 'Fan'}, {'id': 112, 'name': 'Green Vegetables'}, {'id': 113, 'name': 'Banana'}, {'id': 114, 'name': 'Baseball Glove'}, {'id': 115, 'name': 'Airplane'}, {'id': 116, 'name': 'Mouse'}, {'id': 117, 'name': 'Train'}, {'id': 118, 'name': 'Pumpkin'}, {'id': 119, 'name': 'Soccer'}, {'id': 120, 'name': 'Skiboard'}, {'id': 121, 'name': 'Luggage'}, {'id': 122, 'name': 'Nightstand'}, {'id': 123, 'name': 'Teapot'}, {'id': 124, 'name': 'Telephone'}, {'id': 125, 'name': 'Trolley'}, {'id': 126, 'name': 'Head Phone'}, {'id': 127, 'name': 'Sports Car'}, {'id': 128, 'name': 'Stop Sign'}, {'id': 129, 'name': 'Dessert'}, {'id': 130, 'name': 'Scooter'}, {'id': 131, 'name': 'Stroller'}, {'id': 132, 'name': 'Crane'}, {'id': 133, 'name': 'Remote'}, {'id': 134, 'name': 'Refrigerator'}, {'id': 135, 'name': 'Oven'}, {'id': 136, 'name': 'Lemon'}, {'id': 137, 'name': 'Duck'}, {'id': 138, 'name': 'Baseball Bat'}, {'id': 139, 'name': 'Surveillance Camera'}, {'id': 140, 'name': 'Cat'}, {'id': 141, 'name': 'Jug'}, {'id': 142, 'name': 'Broccoli'}, {'id': 143, 'name': 'Piano'}, {'id': 144, 'name': 'Pizza'}, {'id': 145, 'name': 'Elephant'}, {'id': 146, 'name': 'Skateboard'}, {'id': 147, 'name': 'Surfboard'}, {'id': 148, 'name': 'Gun'}, {'id': 149, 'name': 'Skating and Skiing shoes'}, {'id': 150, 'name': 'Gas stove'}, {'id': 151, 'name': 'Donut'}, {'id': 152, 'name': 'Bow Tie'}, {'id': 153, 'name': 'Carrot'}, {'id': 154, 'name': 'Toilet'}, {'id': 155, 'name': 'Kite'}, {'id': 156, 'name': 'Strawberry'}, {'id': 157, 'name': 'Other Balls'}, {'id': 158, 'name': 'Shovel'}, {'id': 159, 'name': 'Pepper'}, {'id': 160, 'name': 'Computer Box'}, {'id': 161, 'name': 'Toilet Paper'}, {'id': 162, 'name': 'Cleaning Products'}, {'id': 163, 'name': 'Chopsticks'}, {'id': 164, 'name': 'Microwave'}, {'id': 165, 'name': 'Pigeon'}, {'id': 166, 'name': 'Baseball'}, {'id': 167, 'name': 'Cutting/chopping Board'}, {'id': 168, 'name': 'Coffee Table'}, {'id': 169, 'name': 'Side Table'}, {'id': 170, 'name': 'Scissors'}, {'id': 171, 'name': 'Marker'}, {'id': 172, 'name': 'Pie'}, {'id': 173, 'name': 'Ladder'}, {'id': 174, 'name': 'Snowboard'}, {'id': 175, 'name': 'Cookies'}, {'id': 176, 'name': 'Radiator'}, {'id': 177, 'name': 'Fire Hydrant'}, {'id': 178, 'name': 'Basketball'}, {'id': 179, 'name': 'Zebra'}, {'id': 180, 'name': 'Grape'}, {'id': 181, 'name': 'Giraffe'}, {'id': 182, 'name': 'Potato'}, {'id': 183, 'name': 'Sausage'}, {'id': 184, 'name': 'Tricycle'}, {'id': 185, 'name': 'Violin'}, {'id': 186, 'name': 'Egg'}, {'id': 187, 'name': 'Fire Extinguisher'}, {'id': 188, 'name': 'Candy'}, {'id': 189, 'name': 'Fire Truck'}, {'id': 190, 'name': 'Billards'}, {'id': 191, 'name': 'Converter'}, {'id': 192, 'name': 'Bathtub'}, {'id': 193, 'name': 'Wheelchair'}, {'id': 194, 'name': 'Golf Club'}, {'id': 195, 'name': 'Briefcase'}, {'id': 196, 'name': 'Cucumber'}, {'id': 197, 'name': 'Cigar/Cigarette '}, {'id': 198, 'name': 'Paint Brush'}, {'id': 199, 'name': 'Pear'}, {'id': 200, 'name': 'Heavy Truck'}, {'id': 201, 'name': 'Hamburger'}, {'id': 202, 'name': 'Extractor'}, {'id': 203, 'name': 'Extension Cord'}, {'id': 204, 'name': 'Tong'}, {'id': 205, 'name': 'Tennis Racket'}, {'id': 206, 'name': 'Folder'}, {'id': 207, 'name': 'American Football'}, {'id': 208, 'name': 'earphone'}, {'id': 209, 'name': 'Mask'}, {'id': 210, 'name': 'Kettle'}, {'id': 211, 'name': 'Tennis'}, {'id': 212, 'name': 'Ship'}, {'id': 213, 'name': 'Swing'}, {'id': 214, 'name': 'Coffee Machine'}, {'id': 215, 'name': 'Slide'}, {'id': 216, 'name': 'Carriage'}, {'id': 217, 'name': 'Onion'}, {'id': 218, 'name': 'Green beans'}, {'id': 219, 'name': 'Projector'}, {'id': 220, 'name': 'Frisbee'}, {'id': 221, 'name': 'Washing Machine/Drying Machine'}, {'id': 222, 'name': 'Chicken'}, {'id': 223, 'name': 'Printer'}, {'id': 224, 'name': 'Watermelon'}, {'id': 225, 'name': 'Saxophone'}, {'id': 226, 'name': 'Tissue'}, {'id': 227, 'name': 'Toothbrush'}, {'id': 228, 'name': 'Ice cream'}, {'id': 229, 'name': 'Hot air balloon'}, {'id': 230, 'name': 'Cello'}, {'id': 231, 'name': 'French Fries'}, {'id': 232, 'name': 'Scale'}, {'id': 233, 'name': 'Trophy'}, {'id': 234, 'name': 'Cabbage'}, {'id': 235, 'name': 'Hot dog'}, {'id': 236, 'name': 'Blender'}, {'id': 237, 'name': 'Peach'}, {'id': 238, 'name': 'Rice'}, {'id': 239, 'name': 'Wallet/Purse'}, {'id': 240, 'name': 'Volleyball'}, {'id': 241, 'name': 'Deer'}, {'id': 242, 'name': 'Goose'}, {'id': 243, 'name': 'Tape'}, {'id': 244, 'name': 'Tablet'}, {'id': 245, 'name': 'Cosmetics'}, {'id': 246, 'name': 'Trumpet'}, {'id': 247, 'name': 'Pineapple'}, {'id': 248, 'name': 'Golf Ball'}, {'id': 249, 'name': 'Ambulance'}, {'id': 250, 'name': 'Parking meter'}, {'id': 251, 'name': 'Mango'}, {'id': 252, 'name': 'Key'}, {'id': 253, 'name': 'Hurdle'}, {'id': 254, 'name': 'Fishing Rod'}, {'id': 255, 'name': 'Medal'}, {'id': 256, 'name': 'Flute'}, {'id': 257, 'name': 'Brush'}, {'id': 258, 'name': 'Penguin'}, {'id': 259, 'name': 'Megaphone'}, {'id': 260, 'name': 'Corn'}, {'id': 261, 'name': 'Lettuce'}, {'id': 262, 'name': 'Garlic'}, {'id': 263, 'name': 'Swan'}, {'id': 264, 'name': 'Helicopter'}, {'id': 265, 'name': 'Green Onion'}, {'id': 266, 'name': 'Sandwich'}, {'id': 267, 'name': 'Nuts'}, {'id': 268, 'name': 'Speed Limit Sign'}, {'id': 269, 'name': 'Induction Cooker'}, {'id': 270, 'name': 'Broom'}, {'id': 271, 'name': 'Trombone'}, {'id': 272, 'name': 'Plum'}, {'id': 273, 'name': 'Rickshaw'}, {'id': 274, 'name': 'Goldfish'}, {'id': 275, 'name': 'Kiwi fruit'}, {'id': 276, 'name': 'Router/modem'}, {'id': 277, 'name': 'Poker Card'}, {'id': 278, 'name': 'Toaster'}, {'id': 279, 'name': 'Shrimp'}, {'id': 280, 'name': 'Sushi'}, {'id': 281, 'name': 'Cheese'}, {'id': 282, 'name': 'Notepaper'}, {'id': 283, 'name': 'Cherry'}, {'id': 284, 'name': 'Pliers'}, {'id': 285, 'name': 'CD'}, {'id': 286, 'name': 'Pasta'}, {'id': 287, 'name': 'Hammer'}, {'id': 288, 'name': 'Cue'}, {'id': 289, 'name': 'Avocado'}, {'id': 290, 'name': 'Hami melon'}, {'id': 291, 'name': 'Flask'}, {'id': 292, 'name': 'Mushroom'}, {'id': 293, 'name': 'Screwdriver'}, {'id': 294, 'name': 'Soap'}, {'id': 295, 'name': 'Recorder'}, {'id': 296, 'name': 'Bear'}, {'id': 297, 'name': 'Eggplant'}, {'id': 298, 'name': 'Board Eraser'}, {'id': 299, 'name': 'Coconut'}, {'id': 300, 'name': 'Tape Measure/ Ruler'}, {'id': 301, 'name': 'Pig'}, {'id': 302, 'name': 'Showerhead'}, {'id': 303, 'name': 'Globe'}, {'id': 304, 'name': 'Chips'}, {'id': 305, 'name': 'Steak'}, {'id': 306, 'name': 'Crosswalk Sign'}, {'id': 307, 'name': 'Stapler'}, {'id': 308, 'name': 'Camel'}, {'id': 309, 'name': 'Formula 1 '}, {'id': 310, 'name': 'Pomegranate'}, {'id': 311, 'name': 'Dishwasher'}, {'id': 312, 'name': 'Crab'}, {'id': 313, 'name': 'Hoverboard'}, {'id': 314, 'name': 'Meatball'}, {'id': 315, 'name': 'Rice Cooker'}, {'id': 316, 'name': 'Tuba'}, {'id': 317, 'name': 'Calculator'}, {'id': 318, 'name': 'Papaya'}, {'id': 319, 'name': 'Antelope'}, {'id': 320, 'name': 'Parrot'}, {'id': 321, 'name': 'Seal'}, {'id': 322, 'name': 'Butterfly'}, {'id': 323, 'name': 'Dumbbell'}, {'id': 324, 'name': 'Donkey'}, {'id': 325, 'name': 'Lion'}, {'id': 326, 'name': 'Urinal'}, {'id': 327, 'name': 'Dolphin'}, {'id': 328, 'name': 'Electric Drill'}, {'id': 329, 'name': 'Hair Dryer'}, {'id': 330, 'name': 'Egg tart'}, {'id': 331, 'name': 'Jellyfish'}, {'id': 332, 'name': 'Treadmill'}, {'id': 333, 'name': 'Lighter'}, {'id': 334, 'name': 'Grapefruit'}, {'id': 335, 'name': 'Game board'}, {'id': 336, 'name': 'Mop'}, {'id': 337, 'name': 'Radish'}, {'id': 338, 'name': 'Baozi'}, {'id': 339, 'name': 'Target'}, {'id': 340, 'name': 'French'}, {'id': 341, 'name': 'Spring Rolls'}, {'id': 342, 'name': 'Monkey'}, {'id': 343, 'name': 'Rabbit'}, {'id': 344, 'name': 'Pencil Case'}, {'id': 345, 'name': 'Yak'}, {'id': 346, 'name': 'Red Cabbage'}, {'id': 347, 'name': 'Binoculars'}, {'id': 348, 'name': 'Asparagus'}, {'id': 349, 'name': 'Barbell'}, {'id': 350, 'name': 'Scallop'}, {'id': 351, 'name': 'Noddles'}, {'id': 352, 'name': 'Comb'}, {'id': 353, 'name': 'Dumpling'}, {'id': 354, 'name': 'Oyster'}, {'id': 355, 'name': 'Table Tennis paddle'}, {'id': 356, 'name': 'Cosmetics Brush/Eyeliner Pencil'}, {'id': 357, 'name': 'Chainsaw'}, {'id': 358, 'name': 'Eraser'}, {'id': 359, 'name': 'Lobster'}, {'id': 360, 'name': 'Durian'}, {'id': 361, 'name': 'Okra'}, {'id': 362, 'name': 'Lipstick'}, {'id': 363, 'name': 'Cosmetics Mirror'}, {'id': 364, 'name': 'Curling'}, {'id': 365, 'name': 'Table Tennis '}, ] def _get_builtin_metadata(): id_to_name = {x['id']: x['name'] for x in categories_v2_fix} thing_dataset_id_to_contiguous_id = { x['id']: i for i, x in enumerate( sorted(categories_v2_fix, key=lambda x: x['id']))} thing_classes = [id_to_name[k] for k in sorted(id_to_name)] return { "thing_dataset_id_to_contiguous_id": thing_dataset_id_to_contiguous_id, "thing_classes": thing_classes} _PREDEFINED_SPLITS_OBJECTS365 = { "objects365_v2_train": ("objects365/train", "objects365/annotations/zhiyuan_objv2_train_fixname_fixmiss.json"), # 80,000 images, 1,240,587 annotations "objects365_v2_val": ("objects365/val", "objects365/annotations/zhiyuan_objv2_val_fixname.json"), "objects365_v2_val_rare": ("objects365/val", "objects365/annotations/zhiyuan_objv2_val_fixname_rare.json"), } for key, (image_root, json_file) in _PREDEFINED_SPLITS_OBJECTS365.items(): register_coco_instances( key, _get_builtin_metadata(), os.path.join("datasets", json_file) if "://" not in json_file else json_file, os.path.join("datasets", image_root), ) ================================================ FILE: vldet/data/datasets/oid.py ================================================ # Part of the code is from https://github.com/xingyizhou/UniDet/blob/master/projects/UniDet/unidet/data/datasets/oid.py # Copyright (c) Facebook, Inc. and its affiliates. from .register_oid import register_oid_instances import os categories = [ {'id': 1, 'name': 'Infant bed', 'freebase_id': '/m/061hd_'}, {'id': 2, 'name': 'Rose', 'freebase_id': '/m/06m11'}, {'id': 3, 'name': 'Flag', 'freebase_id': '/m/03120'}, {'id': 4, 'name': 'Flashlight', 'freebase_id': '/m/01kb5b'}, {'id': 5, 'name': 'Sea turtle', 'freebase_id': '/m/0120dh'}, {'id': 6, 'name': 'Camera', 'freebase_id': '/m/0dv5r'}, {'id': 7, 'name': 'Animal', 'freebase_id': '/m/0jbk'}, {'id': 8, 'name': 'Glove', 'freebase_id': '/m/0174n1'}, {'id': 9, 'name': 'Crocodile', 'freebase_id': '/m/09f_2'}, {'id': 10, 'name': 'Cattle', 'freebase_id': '/m/01xq0k1'}, {'id': 11, 'name': 'House', 'freebase_id': '/m/03jm5'}, {'id': 12, 'name': 'Guacamole', 'freebase_id': '/m/02g30s'}, {'id': 13, 'name': 'Penguin', 'freebase_id': '/m/05z6w'}, {'id': 14, 'name': 'Vehicle registration plate', 'freebase_id': '/m/01jfm_'}, {'id': 15, 'name': 'Bench', 'freebase_id': '/m/076lb9'}, {'id': 16, 'name': 'Ladybug', 'freebase_id': '/m/0gj37'}, {'id': 17, 'name': 'Human nose', 'freebase_id': '/m/0k0pj'}, {'id': 18, 'name': 'Watermelon', 'freebase_id': '/m/0kpqd'}, {'id': 19, 'name': 'Flute', 'freebase_id': '/m/0l14j_'}, {'id': 20, 'name': 'Butterfly', 'freebase_id': '/m/0cyf8'}, {'id': 21, 'name': 'Washing machine', 'freebase_id': '/m/0174k2'}, {'id': 22, 'name': 'Raccoon', 'freebase_id': '/m/0dq75'}, {'id': 23, 'name': 'Segway', 'freebase_id': '/m/076bq'}, {'id': 24, 'name': 'Taco', 'freebase_id': '/m/07crc'}, {'id': 25, 'name': 'Jellyfish', 'freebase_id': '/m/0d8zb'}, {'id': 26, 'name': 'Cake', 'freebase_id': '/m/0fszt'}, {'id': 27, 'name': 'Pen', 'freebase_id': '/m/0k1tl'}, {'id': 28, 'name': 'Cannon', 'freebase_id': '/m/020kz'}, {'id': 29, 'name': 'Bread', 'freebase_id': '/m/09728'}, {'id': 30, 'name': 'Tree', 'freebase_id': '/m/07j7r'}, {'id': 31, 'name': 'Shellfish', 'freebase_id': '/m/0fbdv'}, {'id': 32, 'name': 'Bed', 'freebase_id': '/m/03ssj5'}, {'id': 33, 'name': 'Hamster', 'freebase_id': '/m/03qrc'}, {'id': 34, 'name': 'Hat', 'freebase_id': '/m/02dl1y'}, {'id': 35, 'name': 'Toaster', 'freebase_id': '/m/01k6s3'}, {'id': 36, 'name': 'Sombrero', 'freebase_id': '/m/02jfl0'}, {'id': 37, 'name': 'Tiara', 'freebase_id': '/m/01krhy'}, {'id': 38, 'name': 'Bowl', 'freebase_id': '/m/04kkgm'}, {'id': 39, 'name': 'Dragonfly', 'freebase_id': '/m/0ft9s'}, {'id': 40, 'name': 'Moths and butterflies', 'freebase_id': '/m/0d_2m'}, {'id': 41, 'name': 'Antelope', 'freebase_id': '/m/0czz2'}, {'id': 42, 'name': 'Vegetable', 'freebase_id': '/m/0f4s2w'}, {'id': 43, 'name': 'Torch', 'freebase_id': '/m/07dd4'}, {'id': 44, 'name': 'Building', 'freebase_id': '/m/0cgh4'}, {'id': 45, 'name': 'Power plugs and sockets', 'freebase_id': '/m/03bbps'}, {'id': 46, 'name': 'Blender', 'freebase_id': '/m/02pjr4'}, {'id': 47, 'name': 'Billiard table', 'freebase_id': '/m/04p0qw'}, {'id': 48, 'name': 'Cutting board', 'freebase_id': '/m/02pdsw'}, {'id': 49, 'name': 'Bronze sculpture', 'freebase_id': '/m/01yx86'}, {'id': 50, 'name': 'Turtle', 'freebase_id': '/m/09dzg'}, {'id': 51, 'name': 'Broccoli', 'freebase_id': '/m/0hkxq'}, {'id': 52, 'name': 'Tiger', 'freebase_id': '/m/07dm6'}, {'id': 53, 'name': 'Mirror', 'freebase_id': '/m/054_l'}, {'id': 54, 'name': 'Bear', 'freebase_id': '/m/01dws'}, {'id': 55, 'name': 'Zucchini', 'freebase_id': '/m/027pcv'}, {'id': 56, 'name': 'Dress', 'freebase_id': '/m/01d40f'}, {'id': 57, 'name': 'Volleyball', 'freebase_id': '/m/02rgn06'}, {'id': 58, 'name': 'Guitar', 'freebase_id': '/m/0342h'}, {'id': 59, 'name': 'Reptile', 'freebase_id': '/m/06bt6'}, {'id': 60, 'name': 'Golf cart', 'freebase_id': '/m/0323sq'}, {'id': 61, 'name': 'Tart', 'freebase_id': '/m/02zvsm'}, {'id': 62, 'name': 'Fedora', 'freebase_id': '/m/02fq_6'}, {'id': 63, 'name': 'Carnivore', 'freebase_id': '/m/01lrl'}, {'id': 64, 'name': 'Car', 'freebase_id': '/m/0k4j'}, {'id': 65, 'name': 'Lighthouse', 'freebase_id': '/m/04h7h'}, {'id': 66, 'name': 'Coffeemaker', 'freebase_id': '/m/07xyvk'}, {'id': 67, 'name': 'Food processor', 'freebase_id': '/m/03y6mg'}, {'id': 68, 'name': 'Truck', 'freebase_id': '/m/07r04'}, {'id': 69, 'name': 'Bookcase', 'freebase_id': '/m/03__z0'}, {'id': 70, 'name': 'Surfboard', 'freebase_id': '/m/019w40'}, {'id': 71, 'name': 'Footwear', 'freebase_id': '/m/09j5n'}, {'id': 72, 'name': 'Bench', 'freebase_id': '/m/0cvnqh'}, {'id': 73, 'name': 'Necklace', 'freebase_id': '/m/01llwg'}, {'id': 74, 'name': 'Flower', 'freebase_id': '/m/0c9ph5'}, {'id': 75, 'name': 'Radish', 'freebase_id': '/m/015x5n'}, {'id': 76, 'name': 'Marine mammal', 'freebase_id': '/m/0gd2v'}, {'id': 77, 'name': 'Frying pan', 'freebase_id': '/m/04v6l4'}, {'id': 78, 'name': 'Tap', 'freebase_id': '/m/02jz0l'}, {'id': 79, 'name': 'Peach', 'freebase_id': '/m/0dj6p'}, {'id': 80, 'name': 'Knife', 'freebase_id': '/m/04ctx'}, {'id': 81, 'name': 'Handbag', 'freebase_id': '/m/080hkjn'}, {'id': 82, 'name': 'Laptop', 'freebase_id': '/m/01c648'}, {'id': 83, 'name': 'Tent', 'freebase_id': '/m/01j61q'}, {'id': 84, 'name': 'Ambulance', 'freebase_id': '/m/012n7d'}, {'id': 85, 'name': 'Christmas tree', 'freebase_id': '/m/025nd'}, {'id': 86, 'name': 'Eagle', 'freebase_id': '/m/09csl'}, {'id': 87, 'name': 'Limousine', 'freebase_id': '/m/01lcw4'}, {'id': 88, 'name': 'Kitchen & dining room table', 'freebase_id': '/m/0h8n5zk'}, {'id': 89, 'name': 'Polar bear', 'freebase_id': '/m/0633h'}, {'id': 90, 'name': 'Tower', 'freebase_id': '/m/01fdzj'}, {'id': 91, 'name': 'Football', 'freebase_id': '/m/01226z'}, {'id': 92, 'name': 'Willow', 'freebase_id': '/m/0mw_6'}, {'id': 93, 'name': 'Human head', 'freebase_id': '/m/04hgtk'}, {'id': 94, 'name': 'Stop sign', 'freebase_id': '/m/02pv19'}, {'id': 95, 'name': 'Banana', 'freebase_id': '/m/09qck'}, {'id': 96, 'name': 'Mixer', 'freebase_id': '/m/063rgb'}, {'id': 97, 'name': 'Binoculars', 'freebase_id': '/m/0lt4_'}, {'id': 98, 'name': 'Dessert', 'freebase_id': '/m/0270h'}, {'id': 99, 'name': 'Bee', 'freebase_id': '/m/01h3n'}, {'id': 100, 'name': 'Chair', 'freebase_id': '/m/01mzpv'}, {'id': 101, 'name': 'Wood-burning stove', 'freebase_id': '/m/04169hn'}, {'id': 102, 'name': 'Flowerpot', 'freebase_id': '/m/0fm3zh'}, {'id': 103, 'name': 'Beaker', 'freebase_id': '/m/0d20w4'}, {'id': 104, 'name': 'Oyster', 'freebase_id': '/m/0_cp5'}, {'id': 105, 'name': 'Woodpecker', 'freebase_id': '/m/01dy8n'}, {'id': 106, 'name': 'Harp', 'freebase_id': '/m/03m5k'}, {'id': 107, 'name': 'Bathtub', 'freebase_id': '/m/03dnzn'}, {'id': 108, 'name': 'Wall clock', 'freebase_id': '/m/0h8mzrc'}, {'id': 109, 'name': 'Sports uniform', 'freebase_id': '/m/0h8mhzd'}, {'id': 110, 'name': 'Rhinoceros', 'freebase_id': '/m/03d443'}, {'id': 111, 'name': 'Beehive', 'freebase_id': '/m/01gllr'}, {'id': 112, 'name': 'Cupboard', 'freebase_id': '/m/0642b4'}, {'id': 113, 'name': 'Chicken', 'freebase_id': '/m/09b5t'}, {'id': 114, 'name': 'Man', 'freebase_id': '/m/04yx4'}, {'id': 115, 'name': 'Blue jay', 'freebase_id': '/m/01f8m5'}, {'id': 116, 'name': 'Cucumber', 'freebase_id': '/m/015x4r'}, {'id': 117, 'name': 'Balloon', 'freebase_id': '/m/01j51'}, {'id': 118, 'name': 'Kite', 'freebase_id': '/m/02zt3'}, {'id': 119, 'name': 'Fireplace', 'freebase_id': '/m/03tw93'}, {'id': 120, 'name': 'Lantern', 'freebase_id': '/m/01jfsr'}, {'id': 121, 'name': 'Missile', 'freebase_id': '/m/04ylt'}, {'id': 122, 'name': 'Book', 'freebase_id': '/m/0bt_c3'}, {'id': 123, 'name': 'Spoon', 'freebase_id': '/m/0cmx8'}, {'id': 124, 'name': 'Grapefruit', 'freebase_id': '/m/0hqkz'}, {'id': 125, 'name': 'Squirrel', 'freebase_id': '/m/071qp'}, {'id': 126, 'name': 'Orange', 'freebase_id': '/m/0cyhj_'}, {'id': 127, 'name': 'Coat', 'freebase_id': '/m/01xygc'}, {'id': 128, 'name': 'Punching bag', 'freebase_id': '/m/0420v5'}, {'id': 129, 'name': 'Zebra', 'freebase_id': '/m/0898b'}, {'id': 130, 'name': 'Billboard', 'freebase_id': '/m/01knjb'}, {'id': 131, 'name': 'Bicycle', 'freebase_id': '/m/0199g'}, {'id': 132, 'name': 'Door handle', 'freebase_id': '/m/03c7gz'}, {'id': 133, 'name': 'Mechanical fan', 'freebase_id': '/m/02x984l'}, {'id': 134, 'name': 'Ring binder', 'freebase_id': '/m/04zwwv'}, {'id': 135, 'name': 'Table', 'freebase_id': '/m/04bcr3'}, {'id': 136, 'name': 'Parrot', 'freebase_id': '/m/0gv1x'}, {'id': 137, 'name': 'Sock', 'freebase_id': '/m/01nq26'}, {'id': 138, 'name': 'Vase', 'freebase_id': '/m/02s195'}, {'id': 139, 'name': 'Weapon', 'freebase_id': '/m/083kb'}, {'id': 140, 'name': 'Shotgun', 'freebase_id': '/m/06nrc'}, {'id': 141, 'name': 'Glasses', 'freebase_id': '/m/0jyfg'}, {'id': 142, 'name': 'Seahorse', 'freebase_id': '/m/0nybt'}, {'id': 143, 'name': 'Belt', 'freebase_id': '/m/0176mf'}, {'id': 144, 'name': 'Watercraft', 'freebase_id': '/m/01rzcn'}, {'id': 145, 'name': 'Window', 'freebase_id': '/m/0d4v4'}, {'id': 146, 'name': 'Giraffe', 'freebase_id': '/m/03bk1'}, {'id': 147, 'name': 'Lion', 'freebase_id': '/m/096mb'}, {'id': 148, 'name': 'Tire', 'freebase_id': '/m/0h9mv'}, {'id': 149, 'name': 'Vehicle', 'freebase_id': '/m/07yv9'}, {'id': 150, 'name': 'Canoe', 'freebase_id': '/m/0ph39'}, {'id': 151, 'name': 'Tie', 'freebase_id': '/m/01rkbr'}, {'id': 152, 'name': 'Shelf', 'freebase_id': '/m/0gjbg72'}, {'id': 153, 'name': 'Picture frame', 'freebase_id': '/m/06z37_'}, {'id': 154, 'name': 'Printer', 'freebase_id': '/m/01m4t'}, {'id': 155, 'name': 'Human leg', 'freebase_id': '/m/035r7c'}, {'id': 156, 'name': 'Boat', 'freebase_id': '/m/019jd'}, {'id': 157, 'name': 'Slow cooker', 'freebase_id': '/m/02tsc9'}, {'id': 158, 'name': 'Croissant', 'freebase_id': '/m/015wgc'}, {'id': 159, 'name': 'Candle', 'freebase_id': '/m/0c06p'}, {'id': 160, 'name': 'Pancake', 'freebase_id': '/m/01dwwc'}, {'id': 161, 'name': 'Pillow', 'freebase_id': '/m/034c16'}, {'id': 162, 'name': 'Coin', 'freebase_id': '/m/0242l'}, {'id': 163, 'name': 'Stretcher', 'freebase_id': '/m/02lbcq'}, {'id': 164, 'name': 'Sandal', 'freebase_id': '/m/03nfch'}, {'id': 165, 'name': 'Woman', 'freebase_id': '/m/03bt1vf'}, {'id': 166, 'name': 'Stairs', 'freebase_id': '/m/01lynh'}, {'id': 167, 'name': 'Harpsichord', 'freebase_id': '/m/03q5t'}, {'id': 168, 'name': 'Stool', 'freebase_id': '/m/0fqt361'}, {'id': 169, 'name': 'Bus', 'freebase_id': '/m/01bjv'}, {'id': 170, 'name': 'Suitcase', 'freebase_id': '/m/01s55n'}, {'id': 171, 'name': 'Human mouth', 'freebase_id': '/m/0283dt1'}, {'id': 172, 'name': 'Juice', 'freebase_id': '/m/01z1kdw'}, {'id': 173, 'name': 'Skull', 'freebase_id': '/m/016m2d'}, {'id': 174, 'name': 'Door', 'freebase_id': '/m/02dgv'}, {'id': 175, 'name': 'Violin', 'freebase_id': '/m/07y_7'}, {'id': 176, 'name': 'Chopsticks', 'freebase_id': '/m/01_5g'}, {'id': 177, 'name': 'Digital clock', 'freebase_id': '/m/06_72j'}, {'id': 178, 'name': 'Sunflower', 'freebase_id': '/m/0ftb8'}, {'id': 179, 'name': 'Leopard', 'freebase_id': '/m/0c29q'}, {'id': 180, 'name': 'Bell pepper', 'freebase_id': '/m/0jg57'}, {'id': 181, 'name': 'Harbor seal', 'freebase_id': '/m/02l8p9'}, {'id': 182, 'name': 'Snake', 'freebase_id': '/m/078jl'}, {'id': 183, 'name': 'Sewing machine', 'freebase_id': '/m/0llzx'}, {'id': 184, 'name': 'Goose', 'freebase_id': '/m/0dbvp'}, {'id': 185, 'name': 'Helicopter', 'freebase_id': '/m/09ct_'}, {'id': 186, 'name': 'Seat belt', 'freebase_id': '/m/0dkzw'}, {'id': 187, 'name': 'Coffee cup', 'freebase_id': '/m/02p5f1q'}, {'id': 188, 'name': 'Microwave oven', 'freebase_id': '/m/0fx9l'}, {'id': 189, 'name': 'Hot dog', 'freebase_id': '/m/01b9xk'}, {'id': 190, 'name': 'Countertop', 'freebase_id': '/m/0b3fp9'}, {'id': 191, 'name': 'Serving tray', 'freebase_id': '/m/0h8n27j'}, {'id': 192, 'name': 'Dog bed', 'freebase_id': '/m/0h8n6f9'}, {'id': 193, 'name': 'Beer', 'freebase_id': '/m/01599'}, {'id': 194, 'name': 'Sunglasses', 'freebase_id': '/m/017ftj'}, {'id': 195, 'name': 'Golf ball', 'freebase_id': '/m/044r5d'}, {'id': 196, 'name': 'Waffle', 'freebase_id': '/m/01dwsz'}, {'id': 197, 'name': 'Palm tree', 'freebase_id': '/m/0cdl1'}, {'id': 198, 'name': 'Trumpet', 'freebase_id': '/m/07gql'}, {'id': 199, 'name': 'Ruler', 'freebase_id': '/m/0hdln'}, {'id': 200, 'name': 'Helmet', 'freebase_id': '/m/0zvk5'}, {'id': 201, 'name': 'Ladder', 'freebase_id': '/m/012w5l'}, {'id': 202, 'name': 'Office building', 'freebase_id': '/m/021sj1'}, {'id': 203, 'name': 'Tablet computer', 'freebase_id': '/m/0bh9flk'}, {'id': 204, 'name': 'Toilet paper', 'freebase_id': '/m/09gtd'}, {'id': 205, 'name': 'Pomegranate', 'freebase_id': '/m/0jwn_'}, {'id': 206, 'name': 'Skirt', 'freebase_id': '/m/02wv6h6'}, {'id': 207, 'name': 'Gas stove', 'freebase_id': '/m/02wv84t'}, {'id': 208, 'name': 'Cookie', 'freebase_id': '/m/021mn'}, {'id': 209, 'name': 'Cart', 'freebase_id': '/m/018p4k'}, {'id': 210, 'name': 'Raven', 'freebase_id': '/m/06j2d'}, {'id': 211, 'name': 'Egg', 'freebase_id': '/m/033cnk'}, {'id': 212, 'name': 'Burrito', 'freebase_id': '/m/01j3zr'}, {'id': 213, 'name': 'Goat', 'freebase_id': '/m/03fwl'}, {'id': 214, 'name': 'Kitchen knife', 'freebase_id': '/m/058qzx'}, {'id': 215, 'name': 'Skateboard', 'freebase_id': '/m/06_fw'}, {'id': 216, 'name': 'Salt and pepper shakers', 'freebase_id': '/m/02x8cch'}, {'id': 217, 'name': 'Lynx', 'freebase_id': '/m/04g2r'}, {'id': 218, 'name': 'Boot', 'freebase_id': '/m/01b638'}, {'id': 219, 'name': 'Platter', 'freebase_id': '/m/099ssp'}, {'id': 220, 'name': 'Ski', 'freebase_id': '/m/071p9'}, {'id': 221, 'name': 'Swimwear', 'freebase_id': '/m/01gkx_'}, {'id': 222, 'name': 'Swimming pool', 'freebase_id': '/m/0b_rs'}, {'id': 223, 'name': 'Drinking straw', 'freebase_id': '/m/03v5tg'}, {'id': 224, 'name': 'Wrench', 'freebase_id': '/m/01j5ks'}, {'id': 225, 'name': 'Drum', 'freebase_id': '/m/026t6'}, {'id': 226, 'name': 'Ant', 'freebase_id': '/m/0_k2'}, {'id': 227, 'name': 'Human ear', 'freebase_id': '/m/039xj_'}, {'id': 228, 'name': 'Headphones', 'freebase_id': '/m/01b7fy'}, {'id': 229, 'name': 'Fountain', 'freebase_id': '/m/0220r2'}, {'id': 230, 'name': 'Bird', 'freebase_id': '/m/015p6'}, {'id': 231, 'name': 'Jeans', 'freebase_id': '/m/0fly7'}, {'id': 232, 'name': 'Television', 'freebase_id': '/m/07c52'}, {'id': 233, 'name': 'Crab', 'freebase_id': '/m/0n28_'}, {'id': 234, 'name': 'Microphone', 'freebase_id': '/m/0hg7b'}, {'id': 235, 'name': 'Home appliance', 'freebase_id': '/m/019dx1'}, {'id': 236, 'name': 'Snowplow', 'freebase_id': '/m/04vv5k'}, {'id': 237, 'name': 'Beetle', 'freebase_id': '/m/020jm'}, {'id': 238, 'name': 'Artichoke', 'freebase_id': '/m/047v4b'}, {'id': 239, 'name': 'Jet ski', 'freebase_id': '/m/01xs3r'}, {'id': 240, 'name': 'Stationary bicycle', 'freebase_id': '/m/03kt2w'}, {'id': 241, 'name': 'Human hair', 'freebase_id': '/m/03q69'}, {'id': 242, 'name': 'Brown bear', 'freebase_id': '/m/01dxs'}, {'id': 243, 'name': 'Starfish', 'freebase_id': '/m/01h8tj'}, {'id': 244, 'name': 'Fork', 'freebase_id': '/m/0dt3t'}, {'id': 245, 'name': 'Lobster', 'freebase_id': '/m/0cjq5'}, {'id': 246, 'name': 'Corded phone', 'freebase_id': '/m/0h8lkj8'}, {'id': 247, 'name': 'Drink', 'freebase_id': '/m/0271t'}, {'id': 248, 'name': 'Saucer', 'freebase_id': '/m/03q5c7'}, {'id': 249, 'name': 'Carrot', 'freebase_id': '/m/0fj52s'}, {'id': 250, 'name': 'Insect', 'freebase_id': '/m/03vt0'}, {'id': 251, 'name': 'Clock', 'freebase_id': '/m/01x3z'}, {'id': 252, 'name': 'Castle', 'freebase_id': '/m/0d5gx'}, {'id': 253, 'name': 'Tennis racket', 'freebase_id': '/m/0h8my_4'}, {'id': 254, 'name': 'Ceiling fan', 'freebase_id': '/m/03ldnb'}, {'id': 255, 'name': 'Asparagus', 'freebase_id': '/m/0cjs7'}, {'id': 256, 'name': 'Jaguar', 'freebase_id': '/m/0449p'}, {'id': 257, 'name': 'Musical instrument', 'freebase_id': '/m/04szw'}, {'id': 258, 'name': 'Train', 'freebase_id': '/m/07jdr'}, {'id': 259, 'name': 'Cat', 'freebase_id': '/m/01yrx'}, {'id': 260, 'name': 'Rifle', 'freebase_id': '/m/06c54'}, {'id': 261, 'name': 'Dumbbell', 'freebase_id': '/m/04h8sr'}, {'id': 262, 'name': 'Mobile phone', 'freebase_id': '/m/050k8'}, {'id': 263, 'name': 'Taxi', 'freebase_id': '/m/0pg52'}, {'id': 264, 'name': 'Shower', 'freebase_id': '/m/02f9f_'}, {'id': 265, 'name': 'Pitcher', 'freebase_id': '/m/054fyh'}, {'id': 266, 'name': 'Lemon', 'freebase_id': '/m/09k_b'}, {'id': 267, 'name': 'Invertebrate', 'freebase_id': '/m/03xxp'}, {'id': 268, 'name': 'Turkey', 'freebase_id': '/m/0jly1'}, {'id': 269, 'name': 'High heels', 'freebase_id': '/m/06k2mb'}, {'id': 270, 'name': 'Bust', 'freebase_id': '/m/04yqq2'}, {'id': 271, 'name': 'Elephant', 'freebase_id': '/m/0bwd_0j'}, {'id': 272, 'name': 'Scarf', 'freebase_id': '/m/02h19r'}, {'id': 273, 'name': 'Barrel', 'freebase_id': '/m/02zn6n'}, {'id': 274, 'name': 'Trombone', 'freebase_id': '/m/07c6l'}, {'id': 275, 'name': 'Pumpkin', 'freebase_id': '/m/05zsy'}, {'id': 276, 'name': 'Box', 'freebase_id': '/m/025dyy'}, {'id': 277, 'name': 'Tomato', 'freebase_id': '/m/07j87'}, {'id': 278, 'name': 'Frog', 'freebase_id': '/m/09ld4'}, {'id': 279, 'name': 'Bidet', 'freebase_id': '/m/01vbnl'}, {'id': 280, 'name': 'Human face', 'freebase_id': '/m/0dzct'}, {'id': 281, 'name': 'Houseplant', 'freebase_id': '/m/03fp41'}, {'id': 282, 'name': 'Van', 'freebase_id': '/m/0h2r6'}, {'id': 283, 'name': 'Shark', 'freebase_id': '/m/0by6g'}, {'id': 284, 'name': 'Ice cream', 'freebase_id': '/m/0cxn2'}, {'id': 285, 'name': 'Swim cap', 'freebase_id': '/m/04tn4x'}, {'id': 286, 'name': 'Falcon', 'freebase_id': '/m/0f6wt'}, {'id': 287, 'name': 'Ostrich', 'freebase_id': '/m/05n4y'}, {'id': 288, 'name': 'Handgun', 'freebase_id': '/m/0gxl3'}, {'id': 289, 'name': 'Whiteboard', 'freebase_id': '/m/02d9qx'}, {'id': 290, 'name': 'Lizard', 'freebase_id': '/m/04m9y'}, {'id': 291, 'name': 'Pasta', 'freebase_id': '/m/05z55'}, {'id': 292, 'name': 'Snowmobile', 'freebase_id': '/m/01x3jk'}, {'id': 293, 'name': 'Light bulb', 'freebase_id': '/m/0h8l4fh'}, {'id': 294, 'name': 'Window blind', 'freebase_id': '/m/031b6r'}, {'id': 295, 'name': 'Muffin', 'freebase_id': '/m/01tcjp'}, {'id': 296, 'name': 'Pretzel', 'freebase_id': '/m/01f91_'}, {'id': 297, 'name': 'Computer monitor', 'freebase_id': '/m/02522'}, {'id': 298, 'name': 'Horn', 'freebase_id': '/m/0319l'}, {'id': 299, 'name': 'Furniture', 'freebase_id': '/m/0c_jw'}, {'id': 300, 'name': 'Sandwich', 'freebase_id': '/m/0l515'}, {'id': 301, 'name': 'Fox', 'freebase_id': '/m/0306r'}, {'id': 302, 'name': 'Convenience store', 'freebase_id': '/m/0crjs'}, {'id': 303, 'name': 'Fish', 'freebase_id': '/m/0ch_cf'}, {'id': 304, 'name': 'Fruit', 'freebase_id': '/m/02xwb'}, {'id': 305, 'name': 'Earrings', 'freebase_id': '/m/01r546'}, {'id': 306, 'name': 'Curtain', 'freebase_id': '/m/03rszm'}, {'id': 307, 'name': 'Grape', 'freebase_id': '/m/0388q'}, {'id': 308, 'name': 'Sofa bed', 'freebase_id': '/m/03m3pdh'}, {'id': 309, 'name': 'Horse', 'freebase_id': '/m/03k3r'}, {'id': 310, 'name': 'Luggage and bags', 'freebase_id': '/m/0hf58v5'}, {'id': 311, 'name': 'Desk', 'freebase_id': '/m/01y9k5'}, {'id': 312, 'name': 'Crutch', 'freebase_id': '/m/05441v'}, {'id': 313, 'name': 'Bicycle helmet', 'freebase_id': '/m/03p3bw'}, {'id': 314, 'name': 'Tick', 'freebase_id': '/m/0175cv'}, {'id': 315, 'name': 'Airplane', 'freebase_id': '/m/0cmf2'}, {'id': 316, 'name': 'Canary', 'freebase_id': '/m/0ccs93'}, {'id': 317, 'name': 'Spatula', 'freebase_id': '/m/02d1br'}, {'id': 318, 'name': 'Watch', 'freebase_id': '/m/0gjkl'}, {'id': 319, 'name': 'Lily', 'freebase_id': '/m/0jqgx'}, {'id': 320, 'name': 'Kitchen appliance', 'freebase_id': '/m/0h99cwc'}, {'id': 321, 'name': 'Filing cabinet', 'freebase_id': '/m/047j0r'}, {'id': 322, 'name': 'Aircraft', 'freebase_id': '/m/0k5j'}, {'id': 323, 'name': 'Cake stand', 'freebase_id': '/m/0h8n6ft'}, {'id': 324, 'name': 'Candy', 'freebase_id': '/m/0gm28'}, {'id': 325, 'name': 'Sink', 'freebase_id': '/m/0130jx'}, {'id': 326, 'name': 'Mouse', 'freebase_id': '/m/04rmv'}, {'id': 327, 'name': 'Wine', 'freebase_id': '/m/081qc'}, {'id': 328, 'name': 'Wheelchair', 'freebase_id': '/m/0qmmr'}, {'id': 329, 'name': 'Goldfish', 'freebase_id': '/m/03fj2'}, {'id': 330, 'name': 'Refrigerator', 'freebase_id': '/m/040b_t'}, {'id': 331, 'name': 'French fries', 'freebase_id': '/m/02y6n'}, {'id': 332, 'name': 'Drawer', 'freebase_id': '/m/0fqfqc'}, {'id': 333, 'name': 'Treadmill', 'freebase_id': '/m/030610'}, {'id': 334, 'name': 'Picnic basket', 'freebase_id': '/m/07kng9'}, {'id': 335, 'name': 'Dice', 'freebase_id': '/m/029b3'}, {'id': 336, 'name': 'Cabbage', 'freebase_id': '/m/0fbw6'}, {'id': 337, 'name': 'Football helmet', 'freebase_id': '/m/07qxg_'}, {'id': 338, 'name': 'Pig', 'freebase_id': '/m/068zj'}, {'id': 339, 'name': 'Person', 'freebase_id': '/m/01g317'}, {'id': 340, 'name': 'Shorts', 'freebase_id': '/m/01bfm9'}, {'id': 341, 'name': 'Gondola', 'freebase_id': '/m/02068x'}, {'id': 342, 'name': 'Honeycomb', 'freebase_id': '/m/0fz0h'}, {'id': 343, 'name': 'Doughnut', 'freebase_id': '/m/0jy4k'}, {'id': 344, 'name': 'Chest of drawers', 'freebase_id': '/m/05kyg_'}, {'id': 345, 'name': 'Land vehicle', 'freebase_id': '/m/01prls'}, {'id': 346, 'name': 'Bat', 'freebase_id': '/m/01h44'}, {'id': 347, 'name': 'Monkey', 'freebase_id': '/m/08pbxl'}, {'id': 348, 'name': 'Dagger', 'freebase_id': '/m/02gzp'}, {'id': 349, 'name': 'Tableware', 'freebase_id': '/m/04brg2'}, {'id': 350, 'name': 'Human foot', 'freebase_id': '/m/031n1'}, {'id': 351, 'name': 'Mug', 'freebase_id': '/m/02jvh9'}, {'id': 352, 'name': 'Alarm clock', 'freebase_id': '/m/046dlr'}, {'id': 353, 'name': 'Pressure cooker', 'freebase_id': '/m/0h8ntjv'}, {'id': 354, 'name': 'Human hand', 'freebase_id': '/m/0k65p'}, {'id': 355, 'name': 'Tortoise', 'freebase_id': '/m/011k07'}, {'id': 356, 'name': 'Baseball glove', 'freebase_id': '/m/03grzl'}, {'id': 357, 'name': 'Sword', 'freebase_id': '/m/06y5r'}, {'id': 358, 'name': 'Pear', 'freebase_id': '/m/061_f'}, {'id': 359, 'name': 'Miniskirt', 'freebase_id': '/m/01cmb2'}, {'id': 360, 'name': 'Traffic sign', 'freebase_id': '/m/01mqdt'}, {'id': 361, 'name': 'Girl', 'freebase_id': '/m/05r655'}, {'id': 362, 'name': 'Roller skates', 'freebase_id': '/m/02p3w7d'}, {'id': 363, 'name': 'Dinosaur', 'freebase_id': '/m/029tx'}, {'id': 364, 'name': 'Porch', 'freebase_id': '/m/04m6gz'}, {'id': 365, 'name': 'Human beard', 'freebase_id': '/m/015h_t'}, {'id': 366, 'name': 'Submarine sandwich', 'freebase_id': '/m/06pcq'}, {'id': 367, 'name': 'Screwdriver', 'freebase_id': '/m/01bms0'}, {'id': 368, 'name': 'Strawberry', 'freebase_id': '/m/07fbm7'}, {'id': 369, 'name': 'Wine glass', 'freebase_id': '/m/09tvcd'}, {'id': 370, 'name': 'Seafood', 'freebase_id': '/m/06nwz'}, {'id': 371, 'name': 'Racket', 'freebase_id': '/m/0dv9c'}, {'id': 372, 'name': 'Wheel', 'freebase_id': '/m/083wq'}, {'id': 373, 'name': 'Sea lion', 'freebase_id': '/m/0gd36'}, {'id': 374, 'name': 'Toy', 'freebase_id': '/m/0138tl'}, {'id': 375, 'name': 'Tea', 'freebase_id': '/m/07clx'}, {'id': 376, 'name': 'Tennis ball', 'freebase_id': '/m/05ctyq'}, {'id': 377, 'name': 'Waste container', 'freebase_id': '/m/0bjyj5'}, {'id': 378, 'name': 'Mule', 'freebase_id': '/m/0dbzx'}, {'id': 379, 'name': 'Cricket ball', 'freebase_id': '/m/02ctlc'}, {'id': 380, 'name': 'Pineapple', 'freebase_id': '/m/0fp6w'}, {'id': 381, 'name': 'Coconut', 'freebase_id': '/m/0djtd'}, {'id': 382, 'name': 'Doll', 'freebase_id': '/m/0167gd'}, {'id': 383, 'name': 'Coffee table', 'freebase_id': '/m/078n6m'}, {'id': 384, 'name': 'Snowman', 'freebase_id': '/m/0152hh'}, {'id': 385, 'name': 'Lavender', 'freebase_id': '/m/04gth'}, {'id': 386, 'name': 'Shrimp', 'freebase_id': '/m/0ll1f78'}, {'id': 387, 'name': 'Maple', 'freebase_id': '/m/0cffdh'}, {'id': 388, 'name': 'Cowboy hat', 'freebase_id': '/m/025rp__'}, {'id': 389, 'name': 'Goggles', 'freebase_id': '/m/02_n6y'}, {'id': 390, 'name': 'Rugby ball', 'freebase_id': '/m/0wdt60w'}, {'id': 391, 'name': 'Caterpillar', 'freebase_id': '/m/0cydv'}, {'id': 392, 'name': 'Poster', 'freebase_id': '/m/01n5jq'}, {'id': 393, 'name': 'Rocket', 'freebase_id': '/m/09rvcxw'}, {'id': 394, 'name': 'Organ', 'freebase_id': '/m/013y1f'}, {'id': 395, 'name': 'Saxophone', 'freebase_id': '/m/06ncr'}, {'id': 396, 'name': 'Traffic light', 'freebase_id': '/m/015qff'}, {'id': 397, 'name': 'Cocktail', 'freebase_id': '/m/024g6'}, {'id': 398, 'name': 'Plastic bag', 'freebase_id': '/m/05gqfk'}, {'id': 399, 'name': 'Squash', 'freebase_id': '/m/0dv77'}, {'id': 400, 'name': 'Mushroom', 'freebase_id': '/m/052sf'}, {'id': 401, 'name': 'Hamburger', 'freebase_id': '/m/0cdn1'}, {'id': 402, 'name': 'Light switch', 'freebase_id': '/m/03jbxj'}, {'id': 403, 'name': 'Parachute', 'freebase_id': '/m/0cyfs'}, {'id': 404, 'name': 'Teddy bear', 'freebase_id': '/m/0kmg4'}, {'id': 405, 'name': 'Winter melon', 'freebase_id': '/m/02cvgx'}, {'id': 406, 'name': 'Deer', 'freebase_id': '/m/09kx5'}, {'id': 407, 'name': 'Musical keyboard', 'freebase_id': '/m/057cc'}, {'id': 408, 'name': 'Plumbing fixture', 'freebase_id': '/m/02pkr5'}, {'id': 409, 'name': 'Scoreboard', 'freebase_id': '/m/057p5t'}, {'id': 410, 'name': 'Baseball bat', 'freebase_id': '/m/03g8mr'}, {'id': 411, 'name': 'Envelope', 'freebase_id': '/m/0frqm'}, {'id': 412, 'name': 'Adhesive tape', 'freebase_id': '/m/03m3vtv'}, {'id': 413, 'name': 'Briefcase', 'freebase_id': '/m/0584n8'}, {'id': 414, 'name': 'Paddle', 'freebase_id': '/m/014y4n'}, {'id': 415, 'name': 'Bow and arrow', 'freebase_id': '/m/01g3x7'}, {'id': 416, 'name': 'Telephone', 'freebase_id': '/m/07cx4'}, {'id': 417, 'name': 'Sheep', 'freebase_id': '/m/07bgp'}, {'id': 418, 'name': 'Jacket', 'freebase_id': '/m/032b3c'}, {'id': 419, 'name': 'Boy', 'freebase_id': '/m/01bl7v'}, {'id': 420, 'name': 'Pizza', 'freebase_id': '/m/0663v'}, {'id': 421, 'name': 'Otter', 'freebase_id': '/m/0cn6p'}, {'id': 422, 'name': 'Office supplies', 'freebase_id': '/m/02rdsp'}, {'id': 423, 'name': 'Couch', 'freebase_id': '/m/02crq1'}, {'id': 424, 'name': 'Cello', 'freebase_id': '/m/01xqw'}, {'id': 425, 'name': 'Bull', 'freebase_id': '/m/0cnyhnx'}, {'id': 426, 'name': 'Camel', 'freebase_id': '/m/01x_v'}, {'id': 427, 'name': 'Ball', 'freebase_id': '/m/018xm'}, {'id': 428, 'name': 'Duck', 'freebase_id': '/m/09ddx'}, {'id': 429, 'name': 'Whale', 'freebase_id': '/m/084zz'}, {'id': 430, 'name': 'Shirt', 'freebase_id': '/m/01n4qj'}, {'id': 431, 'name': 'Tank', 'freebase_id': '/m/07cmd'}, {'id': 432, 'name': 'Motorcycle', 'freebase_id': '/m/04_sv'}, {'id': 433, 'name': 'Accordion', 'freebase_id': '/m/0mkg'}, {'id': 434, 'name': 'Owl', 'freebase_id': '/m/09d5_'}, {'id': 435, 'name': 'Porcupine', 'freebase_id': '/m/0c568'}, {'id': 436, 'name': 'Sun hat', 'freebase_id': '/m/02wbtzl'}, {'id': 437, 'name': 'Nail', 'freebase_id': '/m/05bm6'}, {'id': 438, 'name': 'Scissors', 'freebase_id': '/m/01lsmm'}, {'id': 439, 'name': 'Swan', 'freebase_id': '/m/0dftk'}, {'id': 440, 'name': 'Lamp', 'freebase_id': '/m/0dtln'}, {'id': 441, 'name': 'Crown', 'freebase_id': '/m/0nl46'}, {'id': 442, 'name': 'Piano', 'freebase_id': '/m/05r5c'}, {'id': 443, 'name': 'Sculpture', 'freebase_id': '/m/06msq'}, {'id': 444, 'name': 'Cheetah', 'freebase_id': '/m/0cd4d'}, {'id': 445, 'name': 'Oboe', 'freebase_id': '/m/05kms'}, {'id': 446, 'name': 'Tin can', 'freebase_id': '/m/02jnhm'}, {'id': 447, 'name': 'Mango', 'freebase_id': '/m/0fldg'}, {'id': 448, 'name': 'Tripod', 'freebase_id': '/m/073bxn'}, {'id': 449, 'name': 'Oven', 'freebase_id': '/m/029bxz'}, {'id': 450, 'name': 'Mouse', 'freebase_id': '/m/020lf'}, {'id': 451, 'name': 'Barge', 'freebase_id': '/m/01btn'}, {'id': 452, 'name': 'Coffee', 'freebase_id': '/m/02vqfm'}, {'id': 453, 'name': 'Snowboard', 'freebase_id': '/m/06__v'}, {'id': 454, 'name': 'Common fig', 'freebase_id': '/m/043nyj'}, {'id': 455, 'name': 'Salad', 'freebase_id': '/m/0grw1'}, {'id': 456, 'name': 'Marine invertebrates', 'freebase_id': '/m/03hl4l9'}, {'id': 457, 'name': 'Umbrella', 'freebase_id': '/m/0hnnb'}, {'id': 458, 'name': 'Kangaroo', 'freebase_id': '/m/04c0y'}, {'id': 459, 'name': 'Human arm', 'freebase_id': '/m/0dzf4'}, {'id': 460, 'name': 'Measuring cup', 'freebase_id': '/m/07v9_z'}, {'id': 461, 'name': 'Snail', 'freebase_id': '/m/0f9_l'}, {'id': 462, 'name': 'Loveseat', 'freebase_id': '/m/0703r8'}, {'id': 463, 'name': 'Suit', 'freebase_id': '/m/01xyhv'}, {'id': 464, 'name': 'Teapot', 'freebase_id': '/m/01fh4r'}, {'id': 465, 'name': 'Bottle', 'freebase_id': '/m/04dr76w'}, {'id': 466, 'name': 'Alpaca', 'freebase_id': '/m/0pcr'}, {'id': 467, 'name': 'Kettle', 'freebase_id': '/m/03s_tn'}, {'id': 468, 'name': 'Trousers', 'freebase_id': '/m/07mhn'}, {'id': 469, 'name': 'Popcorn', 'freebase_id': '/m/01hrv5'}, {'id': 470, 'name': 'Centipede', 'freebase_id': '/m/019h78'}, {'id': 471, 'name': 'Spider', 'freebase_id': '/m/09kmb'}, {'id': 472, 'name': 'Sparrow', 'freebase_id': '/m/0h23m'}, {'id': 473, 'name': 'Plate', 'freebase_id': '/m/050gv4'}, {'id': 474, 'name': 'Bagel', 'freebase_id': '/m/01fb_0'}, {'id': 475, 'name': 'Personal care', 'freebase_id': '/m/02w3_ws'}, {'id': 476, 'name': 'Apple', 'freebase_id': '/m/014j1m'}, {'id': 477, 'name': 'Brassiere', 'freebase_id': '/m/01gmv2'}, {'id': 478, 'name': 'Bathroom cabinet', 'freebase_id': '/m/04y4h8h'}, {'id': 479, 'name': 'studio couch', 'freebase_id': '/m/026qbn5'}, {'id': 480, 'name': 'Computer keyboard', 'freebase_id': '/m/01m2v'}, {'id': 481, 'name': 'Table tennis racket', 'freebase_id': '/m/05_5p_0'}, {'id': 482, 'name': 'Sushi', 'freebase_id': '/m/07030'}, {'id': 483, 'name': 'Cabinetry', 'freebase_id': '/m/01s105'}, {'id': 484, 'name': 'Street light', 'freebase_id': '/m/033rq4'}, {'id': 485, 'name': 'Towel', 'freebase_id': '/m/0162_1'}, {'id': 486, 'name': 'Nightstand', 'freebase_id': '/m/02z51p'}, {'id': 487, 'name': 'Rabbit', 'freebase_id': '/m/06mf6'}, {'id': 488, 'name': 'Dolphin', 'freebase_id': '/m/02hj4'}, {'id': 489, 'name': 'Dog', 'freebase_id': '/m/0bt9lr'}, {'id': 490, 'name': 'Jug', 'freebase_id': '/m/08hvt4'}, {'id': 491, 'name': 'Wok', 'freebase_id': '/m/084rd'}, {'id': 492, 'name': 'Fire hydrant', 'freebase_id': '/m/01pns0'}, {'id': 493, 'name': 'Human eye', 'freebase_id': '/m/014sv8'}, {'id': 494, 'name': 'Skyscraper', 'freebase_id': '/m/079cl'}, {'id': 495, 'name': 'Backpack', 'freebase_id': '/m/01940j'}, {'id': 496, 'name': 'Potato', 'freebase_id': '/m/05vtc'}, {'id': 497, 'name': 'Paper towel', 'freebase_id': '/m/02w3r3'}, {'id': 498, 'name': 'Lifejacket', 'freebase_id': '/m/054xkw'}, {'id': 499, 'name': 'Bicycle wheel', 'freebase_id': '/m/01bqk0'}, {'id': 500, 'name': 'Toilet', 'freebase_id': '/m/09g1w'}, ] def _get_builtin_metadata(cats): id_to_name = {x['id']: x['name'] for x in cats} thing_dataset_id_to_contiguous_id = {i + 1: i for i in range(len(cats))} thing_classes = [x['name'] for x in sorted(cats, key=lambda x: x['id'])] return { "thing_dataset_id_to_contiguous_id": thing_dataset_id_to_contiguous_id, "thing_classes": thing_classes} _PREDEFINED_SPLITS_OID = { # cat threshold: 500, 1500: r 170, c 151, f 179 "oid_train": ("oid/images/", "oid/annotations/oid_challenge_2019_train_bbox.json"), # "expanded" duplicates annotations to their father classes based on the official # hierarchy. This is used in the official evaulation protocol. # https://storage.googleapis.com/openimages/web/evaluation.html "oid_val_expanded": ("oid/images/validation/", "oid/annotations/oid_challenge_2019_val_expanded.json"), "oid_val_expanded_rare": ("oid/images/validation/", "oid/annotations/oid_challenge_2019_val_expanded_rare.json"), } for key, (image_root, json_file) in _PREDEFINED_SPLITS_OID.items(): register_oid_instances( key, _get_builtin_metadata(categories), os.path.join("datasets", json_file) if "://" not in json_file else json_file, os.path.join("datasets", image_root), ) ================================================ FILE: vldet/data/datasets/register_oid.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. # Modified by Xingyi Zhou from https://github.com/facebookresearch/detectron2/blob/master/detectron2/data/datasets/coco.py import copy import io import logging import contextlib import os import datetime import json import numpy as np from PIL import Image from fvcore.common.timer import Timer from fvcore.common.file_io import PathManager, file_lock from detectron2.structures import BoxMode, PolygonMasks, Boxes from detectron2.data import DatasetCatalog, MetadataCatalog logger = logging.getLogger(__name__) """ This file contains functions to register a COCO-format dataset to the DatasetCatalog. """ __all__ = ["register_coco_instances", "register_coco_panoptic_separated"] def register_oid_instances(name, metadata, json_file, image_root): """ """ # 1. register a function which returns dicts DatasetCatalog.register(name, lambda: load_coco_json_mem_efficient( json_file, image_root, name)) # 2. Optionally, add metadata about this dataset, # since they might be useful in evaluation, visualization or logging MetadataCatalog.get(name).set( json_file=json_file, image_root=image_root, evaluator_type="oid", **metadata ) def load_coco_json_mem_efficient(json_file, image_root, dataset_name=None, extra_annotation_keys=None): """ Actually not mem efficient """ from pycocotools.coco import COCO timer = Timer() json_file = PathManager.get_local_path(json_file) with contextlib.redirect_stdout(io.StringIO()): coco_api = COCO(json_file) if timer.seconds() > 1: logger.info("Loading {} takes {:.2f} seconds.".format(json_file, timer.seconds())) id_map = None if dataset_name is not None: meta = MetadataCatalog.get(dataset_name) cat_ids = sorted(coco_api.getCatIds()) cats = coco_api.loadCats(cat_ids) # The categories in a custom json file may not be sorted. thing_classes = [c["name"] for c in sorted(cats, key=lambda x: x["id"])] meta.thing_classes = thing_classes if not (min(cat_ids) == 1 and max(cat_ids) == len(cat_ids)): if "coco" not in dataset_name: logger.warning( """ Category ids in annotations are not in [1, #categories]! We'll apply a mapping for you. """ ) id_map = {v: i for i, v in enumerate(cat_ids)} meta.thing_dataset_id_to_contiguous_id = id_map # sort indices for reproducible results img_ids = sorted(coco_api.imgs.keys()) imgs = coco_api.loadImgs(img_ids) logger.info("Loaded {} images in COCO format from {}".format(len(imgs), json_file)) dataset_dicts = [] ann_keys = ["iscrowd", "bbox", "category_id"] + (extra_annotation_keys or []) for img_dict in imgs: record = {} record["file_name"] = os.path.join(image_root, img_dict["file_name"]) record["height"] = img_dict["height"] record["width"] = img_dict["width"] image_id = record["image_id"] = img_dict["id"] anno_dict_list = coco_api.imgToAnns[image_id] if 'neg_category_ids' in img_dict: record['neg_category_ids'] = \ [id_map[x] for x in img_dict['neg_category_ids']] objs = [] for anno in anno_dict_list: assert anno["image_id"] == image_id assert anno.get("ignore", 0) == 0 obj = {key: anno[key] for key in ann_keys if key in anno} segm = anno.get("segmentation", None) if segm: # either list[list[float]] or dict(RLE) if not isinstance(segm, dict): # filter out invalid polygons (< 3 points) segm = [poly for poly in segm if len(poly) % 2 == 0 and len(poly) >= 6] if len(segm) == 0: num_instances_without_valid_segmentation += 1 continue # ignore this instance obj["segmentation"] = segm obj["bbox_mode"] = BoxMode.XYWH_ABS if id_map: obj["category_id"] = id_map[obj["category_id"]] objs.append(obj) record["annotations"] = objs dataset_dicts.append(record) del coco_api return dataset_dicts ================================================ FILE: vldet/data/tar_dataset.py ================================================ #!/usr/bin/env python3 # Copyright (c) Facebook, Inc. and its affiliates. import os import gzip import numpy as np import io from PIL import Image from torch.utils.data import Dataset try: from PIL import UnidentifiedImageError unidentified_error_available = True except ImportError: # UnidentifiedImageError isn't available in older versions of PIL unidentified_error_available = False class DiskTarDataset(Dataset): def __init__(self, tarfile_path='dataset/imagenet/ImageNet-21k/metadata/tar_files.npy', tar_index_dir='dataset/imagenet/ImageNet-21k/metadata/tarindex_npy', preload=False, num_synsets="all"): """ - preload (bool): Recommend to set preload to False when using - num_synsets (integer or string "all"): set to small number for debugging will load subset of dataset """ tar_files = np.load(tarfile_path) chunk_datasets = [] dataset_lens = [] if isinstance(num_synsets, int): assert num_synsets < len(tar_files) tar_files = tar_files[:num_synsets] for tar_file in tar_files: dataset = _TarDataset(tar_file, tar_index_dir, preload=preload) chunk_datasets.append(dataset) dataset_lens.append(len(dataset)) self.chunk_datasets = chunk_datasets self.dataset_lens = np.array(dataset_lens).astype(np.int32) self.dataset_cumsums = np.cumsum(self.dataset_lens) self.num_samples = sum(self.dataset_lens) labels = np.zeros(self.dataset_lens.sum(), dtype=np.int64) sI = 0 for k in range(len(self.dataset_lens)): assert (sI+self.dataset_lens[k]) <= len(labels), f"{k} {sI+self.dataset_lens[k]} vs. {len(labels)}" labels[sI:(sI+self.dataset_lens[k])] = k sI += self.dataset_lens[k] self.labels = labels def __len__(self): return self.num_samples def __getitem__(self, index): assert index >= 0 and index < len(self) # find the dataset file we need to go to d_index = np.searchsorted(self.dataset_cumsums, index) # edge case, if index is at edge of chunks, move right if index in self.dataset_cumsums: d_index += 1 assert d_index == self.labels[index], f"{d_index} vs. {self.labels[index]} mismatch for {index}" # change index to local dataset index if d_index == 0: local_index = index else: local_index = index - self.dataset_cumsums[d_index - 1] data_bytes = self.chunk_datasets[d_index][local_index] exception_to_catch = UnidentifiedImageError if unidentified_error_available else Exception try: image = Image.open(data_bytes).convert("RGB") except exception_to_catch: image = Image.fromarray(np.ones((224,224,3), dtype=np.uint8)*128) d_index = -1 # label is the dataset (synset) we indexed into return image, d_index, index def __repr__(self): st = f"DiskTarDataset(subdatasets={len(self.dataset_lens)},samples={self.num_samples})" return st class _TarDataset(object): def __init__(self, filename, npy_index_dir, preload=False): # translated from # fbcode/experimental/deeplearning/matthijs/comp_descs/tardataset.lua self.filename = filename self.names = [] self.offsets = [] self.npy_index_dir = npy_index_dir names, offsets = self.load_index() self.num_samples = len(names) if preload: self.data = np.memmap(filename, mode='r', dtype='uint8') self.offsets = offsets else: self.data = None def __len__(self): return self.num_samples def load_index(self): basename = os.path.basename(self.filename) basename = os.path.splitext(basename)[0] names = np.load(os.path.join(self.npy_index_dir, f"{basename}_names.npy")) offsets = np.load(os.path.join(self.npy_index_dir, f"{basename}_offsets.npy")) return names, offsets def __getitem__(self, idx): if self.data is None: self.data = np.memmap(self.filename, mode='r', dtype='uint8') _, self.offsets = self.load_index() ofs = self.offsets[idx] * 512 fsize = 512 * (self.offsets[idx + 1] - self.offsets[idx]) data = self.data[ofs:ofs + fsize] if data[:13].tostring() == '././@LongLink': data = data[3 * 512:] else: data = data[512:] # just to make it more fun a few JPEGs are GZIP compressed... # catch this case if tuple(data[:2]) == (0x1f, 0x8b): s = io.BytesIO(data.tostring()) g = gzip.GzipFile(None, 'r', 0, s) sdata = g.read() else: sdata = data.tostring() return io.BytesIO(sdata) ================================================ FILE: vldet/data/transforms/custom_augmentation_impl.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved # Part of the code is from https://github.com/rwightman/efficientdet-pytorch/blob/master/effdet/data/transforms.py # Modified by Xingyi Zhou # The original code is under Apache-2.0 License import numpy as np import sys from fvcore.transforms.transform import ( BlendTransform, CropTransform, HFlipTransform, NoOpTransform, Transform, VFlipTransform, ) from PIL import Image from detectron2.data.transforms.augmentation import Augmentation from .custom_transform import EfficientDetResizeCropTransform __all__ = [ "EfficientDetResizeCrop", ] class EfficientDetResizeCrop(Augmentation): """ Scale the shorter edge to the given size, with a limit of `max_size` on the longer edge. If `max_size` is reached, then downscale so that the longer edge does not exceed max_size. """ def __init__( self, size, scale, interp=Image.BILINEAR ): """ """ super().__init__() self.target_size = (size, size) self.scale = scale self.interp = interp def get_transform(self, img): # Select a random scale factor. scale_factor = np.random.uniform(*self.scale) scaled_target_height = scale_factor * self.target_size[0] scaled_target_width = scale_factor * self.target_size[1] # Recompute the accurate scale_factor using rounded scaled image size. width, height = img.shape[1], img.shape[0] img_scale_y = scaled_target_height / height img_scale_x = scaled_target_width / width img_scale = min(img_scale_y, img_scale_x) # Select non-zero random offset (x, y) if scaled image is larger than target size scaled_h = int(height * img_scale) scaled_w = int(width * img_scale) offset_y = scaled_h - self.target_size[0] offset_x = scaled_w - self.target_size[1] offset_y = int(max(0.0, float(offset_y)) * np.random.uniform(0, 1)) offset_x = int(max(0.0, float(offset_x)) * np.random.uniform(0, 1)) return EfficientDetResizeCropTransform( scaled_h, scaled_w, offset_y, offset_x, img_scale, self.target_size, self.interp) ================================================ FILE: vldet/data/transforms/custom_transform.py ================================================ # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved # Part of the code is from https://github.com/rwightman/efficientdet-pytorch/blob/master/effdet/data/transforms.py # Modified by Xingyi Zhou # The original code is under Apache-2.0 License import numpy as np import torch import torch.nn.functional as F from fvcore.transforms.transform import ( CropTransform, HFlipTransform, NoOpTransform, Transform, TransformList, ) from PIL import Image try: import cv2 # noqa except ImportError: # OpenCV is an optional dependency at the moment pass __all__ = [ "EfficientDetResizeCropTransform", ] class EfficientDetResizeCropTransform(Transform): """ """ def __init__(self, scaled_h, scaled_w, offset_y, offset_x, img_scale, \ target_size, interp=None): """ Args: h, w (int): original image size new_h, new_w (int): new image size interp: PIL interpolation methods, defaults to bilinear. """ # TODO decide on PIL vs opencv super().__init__() if interp is None: interp = Image.BILINEAR self._set_attributes(locals()) def apply_image(self, img, interp=None): assert len(img.shape) <= 4 if img.dtype == np.uint8: pil_image = Image.fromarray(img) interp_method = interp if interp is not None else self.interp try: pil_image = pil_image.resize((self.scaled_w, self.scaled_h), interp_method) except: print("image size:", pil_image.size) print("parameter:",self.scaled_h, self.scaled_w, self.offset_y, self.offset_x, self.img_scale, self.target_size) ret = np.asarray(pil_image) right = min(self.scaled_w, self.offset_x + self.target_size[1]) lower = min(self.scaled_h, self.offset_y + self.target_size[0]) if len(ret.shape) <= 3: ret = ret[self.offset_y: lower, self.offset_x: right] else: ret = ret[..., self.offset_y: lower, self.offset_x: right, :] else: # PIL only supports uint8 img = torch.from_numpy(img) shape = list(img.shape) shape_4d = shape[:2] + [1] * (4 - len(shape)) + shape[2:] img = img.view(shape_4d).permute(2, 3, 0, 1) # hw(c) -> nchw _PIL_RESIZE_TO_INTERPOLATE_MODE = {Image.BILINEAR: "bilinear", Image.BICUBIC: "bicubic"} mode = _PIL_RESIZE_TO_INTERPOLATE_MODE[self.interp] img = F.interpolate(img, (self.scaled_h, self.scaled_w), mode=mode, align_corners=False) shape[:2] = (self.scaled_h, self.scaled_w) ret = img.permute(2, 3, 0, 1).view(shape).numpy() # nchw -> hw(c) right = min(self.scaled_w, self.offset_x + self.target_size[1]) lower = min(self.scaled_h, self.offset_y + self.target_size[0]) if len(ret.shape) <= 3: ret = ret[self.offset_y: lower, self.offset_x: right] else: ret = ret[..., self.offset_y: lower, self.offset_x: right, :] return ret def apply_coords(self, coords): coords[:, 0] = coords[:, 0] * self.img_scale coords[:, 1] = coords[:, 1] * self.img_scale coords[:, 0] -= self.offset_x coords[:, 1] -= self.offset_y return coords def apply_segmentation(self, segmentation): segmentation = self.apply_image(segmentation, interp=Image.NEAREST) return segmentation def inverse(self): raise NotImplementedError def inverse_apply_coords(self, coords): coords[:, 0] += self.offset_x coords[:, 1] += self.offset_y coords[:, 0] = coords[:, 0] / self.img_scale coords[:, 1] = coords[:, 1] / self.img_scale return coords def inverse_apply_box(self, box: np.ndarray) -> np.ndarray: """ """ idxs = np.array([(0, 1), (2, 1), (0, 3), (2, 3)]).flatten() coords = np.asarray(box).reshape(-1, 4)[:, idxs].reshape(-1, 2) coords = self.inverse_apply_coords(coords).reshape((-1, 4, 2)) minxy = coords.min(axis=1) maxxy = coords.max(axis=1) trans_boxes = np.concatenate((minxy, maxxy), axis=1) return trans_boxes ================================================ FILE: vldet/evaluation/custom_coco_eval.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import contextlib import copy import io import itertools import json import logging import numpy as np import os import pickle from collections import OrderedDict import pycocotools.mask as mask_util import torch from pycocotools.coco import COCO from pycocotools.cocoeval import COCOeval from tabulate import tabulate import detectron2.utils.comm as comm from detectron2.config import CfgNode from detectron2.data import MetadataCatalog from detectron2.data.datasets.coco import convert_to_coco_json from detectron2.evaluation.coco_evaluation import COCOEvaluator from detectron2.structures import Boxes, BoxMode, pairwise_iou from detectron2.utils.file_io import PathManager from detectron2.utils.logger import create_small_table from ..data.datasets.coco_zeroshot import categories_seen, categories_unseen class CustomCOCOEvaluator(COCOEvaluator): def _derive_coco_results(self, coco_eval, iou_type, class_names=None): """ Additionally plot mAP for 'seen classes' and 'unseen classes' """ metrics = { "bbox": ["AP", "AP50", "AP75", "APs", "APm", "APl"], "segm": ["AP", "AP50", "AP75", "APs", "APm", "APl"], "keypoints": ["AP", "AP50", "AP75", "APm", "APl"], }[iou_type] if coco_eval is None: self._logger.warn("No predictions from the model!") return {metric: float("nan") for metric in metrics} # the standard metrics results = { metric: float(coco_eval.stats[idx] * 100 if coco_eval.stats[idx] >= 0 else "nan") for idx, metric in enumerate(metrics) } self._logger.info( "Evaluation results for {}: \n".format(iou_type) + create_small_table(results) ) if not np.isfinite(sum(results.values())): self._logger.info("Some metrics cannot be computed and is shown as NaN.") if class_names is None or len(class_names) <= 1: return results # Compute per-category AP # from https://github.com/facebookresearch/Detectron/blob/a6a835f5b8208c45d0dce217ce9bbda915f44df7/detectron/datasets/json_dataset_evaluator.py#L222-L252 # noqa precisions = coco_eval.eval["precision"] # precision has dims (iou, recall, cls, area range, max dets) assert len(class_names) == precisions.shape[2] seen_names = set([x['name'] for x in categories_seen]) unseen_names = set([x['name'] for x in categories_unseen]) results_per_category = [] results_per_category50 = [] results_per_category50_seen = [] results_per_category50_unseen = [] for idx, name in enumerate(class_names): # area range index 0: all area ranges # max dets index -1: typically 100 per image precision = precisions[:, :, idx, 0, -1] precision = precision[precision > -1] ap = np.mean(precision) if precision.size else float("nan") results_per_category.append(("{}".format(name), float(ap * 100))) precision50 = precisions[0, :, idx, 0, -1] precision50 = precision50[precision50 > -1] ap50 = np.mean(precision50) if precision50.size else float("nan") results_per_category50.append(("{}".format(name), float(ap50 * 100))) if name in seen_names: results_per_category50_seen.append(float(ap50 * 100)) if name in unseen_names: results_per_category50_unseen.append(float(ap50 * 100)) # tabulate it N_COLS = min(6, len(results_per_category) * 2) results_flatten = list(itertools.chain(*results_per_category)) results_2d = itertools.zip_longest(*[results_flatten[i::N_COLS] for i in range(N_COLS)]) table = tabulate( results_2d, tablefmt="pipe", floatfmt=".3f", headers=["category", "AP"] * (N_COLS // 2), numalign="left", ) self._logger.info("Per-category {} AP: \n".format(iou_type) + table) N_COLS = min(6, len(results_per_category50) * 2) results_flatten = list(itertools.chain(*results_per_category50)) results_2d = itertools.zip_longest(*[results_flatten[i::N_COLS] for i in range(N_COLS)]) table = tabulate( results_2d, tablefmt="pipe", floatfmt=".3f", headers=["category", "AP50"] * (N_COLS // 2), numalign="left", ) self._logger.info("Per-category {} AP50: \n".format(iou_type) + table) self._logger.info( "Seen {} AP50: {}".format( iou_type, sum(results_per_category50_seen) / len(results_per_category50_seen), )) self._logger.info( "Unseen {} AP50: {}".format( iou_type, sum(results_per_category50_unseen) / len(results_per_category50_unseen), )) results.update({"AP-" + name: ap for name, ap in results_per_category}) results["AP50-seen"] = sum(results_per_category50_seen) / len(results_per_category50_seen) results["AP50-unseen"] = sum(results_per_category50_unseen) / len(results_per_category50_unseen) return results ================================================ FILE: vldet/evaluation/oideval.py ================================================ # Part of the code is from https://github.com/tensorflow/models/blob/master/research/object_detection/metrics/oid_challenge_evaluation.py # Copyright 2018 The TensorFlow Authors. All Rights Reserved. # The original code is under Apache License, Version 2.0 (the "License"); # Part of the code is from https://github.com/lvis-dataset/lvis-api/blob/master/lvis/eval.py # Copyright (c) 2019, Agrim Gupta and Ross Girshick # Modified by Xingyi Zhou # This script re-implement OpenImages evaluation in detectron2 # The code is from https://github.com/xingyizhou/UniDet/blob/master/projects/UniDet/unidet/evaluation/oideval.py # The original code is under Apache-2.0 License # Copyright (c) Facebook, Inc. and its affiliates. import os import datetime import logging import itertools from collections import OrderedDict from collections import defaultdict import copy import json import numpy as np import torch from tabulate import tabulate from lvis.lvis import LVIS from lvis.results import LVISResults import pycocotools.mask as mask_utils from fvcore.common.file_io import PathManager import detectron2.utils.comm as comm from detectron2.data import MetadataCatalog from detectron2.evaluation.coco_evaluation import instances_to_coco_json from detectron2.utils.logger import create_small_table from detectron2.evaluation import DatasetEvaluator def compute_average_precision(precision, recall): """Compute Average Precision according to the definition in VOCdevkit. Precision is modified to ensure that it does not decrease as recall decrease. Args: precision: A float [N, 1] numpy array of precisions recall: A float [N, 1] numpy array of recalls Raises: ValueError: if the input is not of the correct format Returns: average_precison: The area under the precision recall curve. NaN if precision and recall are None. """ if precision is None: if recall is not None: raise ValueError("If precision is None, recall must also be None") return np.NAN if not isinstance(precision, np.ndarray) or not isinstance( recall, np.ndarray): raise ValueError("precision and recall must be numpy array") if precision.dtype != np.float or recall.dtype != np.float: raise ValueError("input must be float numpy array.") if len(precision) != len(recall): raise ValueError("precision and recall must be of the same size.") if not precision.size: return 0.0 if np.amin(precision) < 0 or np.amax(precision) > 1: raise ValueError("Precision must be in the range of [0, 1].") if np.amin(recall) < 0 or np.amax(recall) > 1: raise ValueError("recall must be in the range of [0, 1].") if not all(recall[i] <= recall[i + 1] for i in range(len(recall) - 1)): raise ValueError("recall must be a non-decreasing array") recall = np.concatenate([[0], recall, [1]]) precision = np.concatenate([[0], precision, [0]]) for i in range(len(precision) - 2, -1, -1): precision[i] = np.maximum(precision[i], precision[i + 1]) indices = np.where(recall[1:] != recall[:-1])[0] + 1 average_precision = np.sum( (recall[indices] - recall[indices - 1]) * precision[indices]) return average_precision class OIDEval: def __init__( self, lvis_gt, lvis_dt, iou_type="bbox", expand_pred_label=False, oid_hierarchy_path='./datasets/oid/annotations/challenge-2019-label500-hierarchy.json'): """Constructor for OIDEval. Args: lvis_gt (LVIS class instance, or str containing path of annotation file) lvis_dt (LVISResult class instance, or str containing path of result file, or list of dict) iou_type (str): segm or bbox evaluation """ self.logger = logging.getLogger(__name__) if iou_type not in ["bbox", "segm"]: raise ValueError("iou_type: {} is not supported.".format(iou_type)) if isinstance(lvis_gt, LVIS): self.lvis_gt = lvis_gt elif isinstance(lvis_gt, str): self.lvis_gt = LVIS(lvis_gt) else: raise TypeError("Unsupported type {} of lvis_gt.".format(lvis_gt)) if isinstance(lvis_dt, LVISResults): self.lvis_dt = lvis_dt elif isinstance(lvis_dt, (str, list)): # self.lvis_dt = LVISResults(self.lvis_gt, lvis_dt, max_dets=-1) self.lvis_dt = LVISResults(self.lvis_gt, lvis_dt) else: raise TypeError("Unsupported type {} of lvis_dt.".format(lvis_dt)) if expand_pred_label: oid_hierarchy = json.load(open(oid_hierarchy_path, 'r')) cat_info = self.lvis_gt.dataset['categories'] freebase2id = {x['freebase_id']: x['id'] for x in cat_info} id2freebase = {x['id']: x['freebase_id'] for x in cat_info} id2name = {x['id']: x['name'] for x in cat_info} fas = defaultdict(set) def dfs(hierarchy, cur_id): all_childs = set() all_keyed_child = {} if 'Subcategory' in hierarchy: for x in hierarchy['Subcategory']: childs = dfs(x, freebase2id[x['LabelName']]) all_childs.update(childs) if cur_id != -1: for c in all_childs: fas[c].add(cur_id) all_childs.add(cur_id) return all_childs dfs(oid_hierarchy, -1) expanded_pred = [] id_count = 0 for d in self.lvis_dt.dataset['annotations']: cur_id = d['category_id'] ids = [cur_id] + [x for x in fas[cur_id]] for cat_id in ids: new_box = copy.deepcopy(d) id_count = id_count + 1 new_box['id'] = id_count new_box['category_id'] = cat_id expanded_pred.append(new_box) print('Expanding original {} preds to {} preds'.format( len(self.lvis_dt.dataset['annotations']), len(expanded_pred) )) self.lvis_dt.dataset['annotations'] = expanded_pred self.lvis_dt._create_index() # per-image per-category evaluation results self.eval_imgs = defaultdict(list) self.eval = {} # accumulated evaluation results self._gts = defaultdict(list) # gt for evaluation self._dts = defaultdict(list) # dt for evaluation self.params = Params(iou_type=iou_type) # parameters self.results = OrderedDict() self.ious = {} # ious between all gts and dts self.params.img_ids = sorted(self.lvis_gt.get_img_ids()) self.params.cat_ids = sorted(self.lvis_gt.get_cat_ids()) def _to_mask(self, anns, lvis): for ann in anns: rle = lvis.ann_to_rle(ann) ann["segmentation"] = rle def _prepare(self): """Prepare self._gts and self._dts for evaluation based on params.""" cat_ids = self.params.cat_ids if self.params.cat_ids else None gts = self.lvis_gt.load_anns( self.lvis_gt.get_ann_ids(img_ids=self.params.img_ids, cat_ids=cat_ids) ) dts = self.lvis_dt.load_anns( self.lvis_dt.get_ann_ids(img_ids=self.params.img_ids, cat_ids=cat_ids) ) # convert ground truth to mask if iou_type == 'segm' if self.params.iou_type == "segm": self._to_mask(gts, self.lvis_gt) self._to_mask(dts, self.lvis_dt) for gt in gts: self._gts[gt["image_id"], gt["category_id"]].append(gt) # For federated dataset evaluation we will filter out all dt for an # image which belong to categories not present in gt and not present in # the negative list for an image. In other words detector is not penalized # for categories about which we don't have gt information about their # presence or absence in an image. img_data = self.lvis_gt.load_imgs(ids=self.params.img_ids) # per image map of categories not present in image img_nl = {d["id"]: d["neg_category_ids"] for d in img_data} # per image list of categories present in image img_pl = {d["id"]: d["pos_category_ids"] for d in img_data} # img_pl = defaultdict(set) for ann in gts: # img_pl[ann["image_id"]].add(ann["category_id"]) assert ann["category_id"] in img_pl[ann["image_id"]] # print('check pos ids OK.') for dt in dts: img_id, cat_id = dt["image_id"], dt["category_id"] if cat_id not in img_nl[img_id] and cat_id not in img_pl[img_id]: continue self._dts[img_id, cat_id].append(dt) def evaluate(self): """ Run per image evaluation on given images and store results (a list of dict) in self.eval_imgs. """ self.logger.info("Running per image evaluation.") self.logger.info("Evaluate annotation type *{}*".format(self.params.iou_type)) self.params.img_ids = list(np.unique(self.params.img_ids)) if self.params.use_cats: cat_ids = self.params.cat_ids else: cat_ids = [-1] self._prepare() self.ious = { (img_id, cat_id): self.compute_iou(img_id, cat_id) for img_id in self.params.img_ids for cat_id in cat_ids } # loop through images, area range, max detection number print('Evaluating ...') self.eval_imgs = [ self.evaluate_img_google(img_id, cat_id, area_rng) for cat_id in cat_ids for area_rng in self.params.area_rng for img_id in self.params.img_ids ] def _get_gt_dt(self, img_id, cat_id): """Create gt, dt which are list of anns/dets. If use_cats is true only anns/dets corresponding to tuple (img_id, cat_id) will be used. Else, all anns/dets in image are used and cat_id is not used. """ if self.params.use_cats: gt = self._gts[img_id, cat_id] dt = self._dts[img_id, cat_id] else: gt = [ _ann for _cat_id in self.params.cat_ids for _ann in self._gts[img_id, cat_id] ] dt = [ _ann for _cat_id in self.params.cat_ids for _ann in self._dts[img_id, cat_id] ] return gt, dt def compute_iou(self, img_id, cat_id): gt, dt = self._get_gt_dt(img_id, cat_id) if len(gt) == 0 and len(dt) == 0: return [] # Sort detections in decreasing order of score. idx = np.argsort([-d["score"] for d in dt], kind="mergesort") dt = [dt[i] for i in idx] # iscrowd = [int(False)] * len(gt) iscrowd = [int('iscrowd' in g and g['iscrowd'] > 0) for g in gt] if self.params.iou_type == "segm": ann_type = "segmentation" elif self.params.iou_type == "bbox": ann_type = "bbox" else: raise ValueError("Unknown iou_type for iou computation.") gt = [g[ann_type] for g in gt] dt = [d[ann_type] for d in dt] # compute iou between each dt and gt region # will return array of shape len(dt), len(gt) ious = mask_utils.iou(dt, gt, iscrowd) return ious def evaluate_img_google(self, img_id, cat_id, area_rng): gt, dt = self._get_gt_dt(img_id, cat_id) if len(gt) == 0 and len(dt) == 0: return None if len(dt) == 0: return { "image_id": img_id, "category_id": cat_id, "area_rng": area_rng, "dt_ids": [], "dt_matches": np.array([], dtype=np.int32).reshape(1, -1), "dt_scores": [], "dt_ignore": np.array([], dtype=np.int32).reshape(1, -1), 'num_gt': len(gt) } no_crowd_inds = [i for i, g in enumerate(gt) \ if ('iscrowd' not in g) or g['iscrowd'] == 0] crowd_inds = [i for i, g in enumerate(gt) \ if 'iscrowd' in g and g['iscrowd'] == 1] dt_idx = np.argsort([-d["score"] for d in dt], kind="mergesort") if len(self.ious[img_id, cat_id]) > 0: ious = self.ious[img_id, cat_id] iou = ious[:, no_crowd_inds] iou = iou[dt_idx] ioa = ious[:, crowd_inds] ioa = ioa[dt_idx] else: iou = np.zeros((len(dt_idx), 0)) ioa = np.zeros((len(dt_idx), 0)) scores = np.array([dt[i]['score'] for i in dt_idx]) num_detected_boxes = len(dt) tp_fp_labels = np.zeros(num_detected_boxes, dtype=bool) is_matched_to_group_of = np.zeros(num_detected_boxes, dtype=bool) def compute_match_iou(iou): max_overlap_gt_ids = np.argmax(iou, axis=1) is_gt_detected = np.zeros(iou.shape[1], dtype=bool) for i in range(num_detected_boxes): gt_id = max_overlap_gt_ids[i] is_evaluatable = (not tp_fp_labels[i] and iou[i, gt_id] >= 0.5 and not is_matched_to_group_of[i]) if is_evaluatable: if not is_gt_detected[gt_id]: tp_fp_labels[i] = True is_gt_detected[gt_id] = True def compute_match_ioa(ioa): scores_group_of = np.zeros(ioa.shape[1], dtype=float) tp_fp_labels_group_of = np.ones( ioa.shape[1], dtype=float) max_overlap_group_of_gt_ids = np.argmax(ioa, axis=1) for i in range(num_detected_boxes): gt_id = max_overlap_group_of_gt_ids[i] is_evaluatable = (not tp_fp_labels[i] and ioa[i, gt_id] >= 0.5 and not is_matched_to_group_of[i]) if is_evaluatable: is_matched_to_group_of[i] = True scores_group_of[gt_id] = max(scores_group_of[gt_id], scores[i]) selector = np.where((scores_group_of > 0) & (tp_fp_labels_group_of > 0)) scores_group_of = scores_group_of[selector] tp_fp_labels_group_of = tp_fp_labels_group_of[selector] return scores_group_of, tp_fp_labels_group_of if iou.shape[1] > 0: compute_match_iou(iou) scores_box_group_of = np.ndarray([0], dtype=float) tp_fp_labels_box_group_of = np.ndarray([0], dtype=float) if ioa.shape[1] > 0: scores_box_group_of, tp_fp_labels_box_group_of = compute_match_ioa(ioa) valid_entries = (~is_matched_to_group_of) scores = np.concatenate( (scores[valid_entries], scores_box_group_of)) tp_fps = np.concatenate( (tp_fp_labels[valid_entries].astype(float), tp_fp_labels_box_group_of)) return { "image_id": img_id, "category_id": cat_id, "area_rng": area_rng, "dt_matches": np.array([1 if x > 0 else 0 for x in tp_fps], dtype=np.int32).reshape(1, -1), "dt_scores": [x for x in scores], "dt_ignore": np.array([0 for x in scores], dtype=np.int32).reshape(1, -1), 'num_gt': len(gt) } def accumulate(self): """Accumulate per image evaluation results and store the result in self.eval. """ self.logger.info("Accumulating evaluation results.") if not self.eval_imgs: self.logger.warn("Please run evaluate first.") if self.params.use_cats: cat_ids = self.params.cat_ids else: cat_ids = [-1] num_thrs = 1 num_recalls = 1 num_cats = len(cat_ids) num_area_rngs = 1 num_imgs = len(self.params.img_ids) # -1 for absent categories precision = -np.ones( (num_thrs, num_recalls, num_cats, num_area_rngs) ) recall = -np.ones((num_thrs, num_cats, num_area_rngs)) # Initialize dt_pointers dt_pointers = {} for cat_idx in range(num_cats): dt_pointers[cat_idx] = {} for area_idx in range(num_area_rngs): dt_pointers[cat_idx][area_idx] = {} # Per category evaluation for cat_idx in range(num_cats): Nk = cat_idx * num_area_rngs * num_imgs for area_idx in range(num_area_rngs): Na = area_idx * num_imgs E = [ self.eval_imgs[Nk + Na + img_idx] for img_idx in range(num_imgs) ] # Remove elements which are None E = [e for e in E if not e is None] if len(E) == 0: continue dt_scores = np.concatenate([e["dt_scores"] for e in E], axis=0) dt_idx = np.argsort(-dt_scores, kind="mergesort") dt_scores = dt_scores[dt_idx] dt_m = np.concatenate([e["dt_matches"] for e in E], axis=1)[:, dt_idx] dt_ig = np.concatenate([e["dt_ignore"] for e in E], axis=1)[:, dt_idx] num_gt = sum([e['num_gt'] for e in E]) if num_gt == 0: continue tps = np.logical_and(dt_m, np.logical_not(dt_ig)) fps = np.logical_and(np.logical_not(dt_m), np.logical_not(dt_ig)) tp_sum = np.cumsum(tps, axis=1).astype(dtype=np.float) fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float) dt_pointers[cat_idx][area_idx] = { "tps": tps, "fps": fps, } for iou_thr_idx, (tp, fp) in enumerate(zip(tp_sum, fp_sum)): tp = np.array(tp) fp = np.array(fp) num_tp = len(tp) rc = tp / num_gt if num_tp: recall[iou_thr_idx, cat_idx, area_idx] = rc[ -1 ] else: recall[iou_thr_idx, cat_idx, area_idx] = 0 # np.spacing(1) ~= eps pr = tp / (fp + tp + np.spacing(1)) pr = pr.tolist() for i in range(num_tp - 1, 0, -1): if pr[i] > pr[i - 1]: pr[i - 1] = pr[i] mAP = compute_average_precision( np.array(pr, np.float).reshape(-1), np.array(rc, np.float).reshape(-1)) precision[iou_thr_idx, :, cat_idx, area_idx] = mAP self.eval = { "params": self.params, "counts": [num_thrs, num_recalls, num_cats, num_area_rngs], "date": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "precision": precision, "recall": recall, "dt_pointers": dt_pointers, } def _summarize(self, summary_type): s = self.eval["precision"] if len(s[s > -1]) == 0: mean_s = -1 else: mean_s = np.mean(s[s > -1]) # print(s.reshape(1, 1, -1, 1)) return mean_s def summarize(self): """Compute and display summary metrics for evaluation results.""" if not self.eval: raise RuntimeError("Please run accumulate() first.") max_dets = self.params.max_dets self.results["AP50"] = self._summarize('ap') def run(self): """Wrapper function which calculates the results.""" self.evaluate() self.accumulate() self.summarize() def print_results(self): template = " {:<18} {} @[ IoU={:<9} | area={:>6s} | maxDets={:>3d} catIds={:>3s}] = {:0.3f}" for key, value in self.results.items(): max_dets = self.params.max_dets if "AP" in key: title = "Average Precision" _type = "(AP)" else: title = "Average Recall" _type = "(AR)" if len(key) > 2 and key[2].isdigit(): iou_thr = (float(key[2:]) / 100) iou = "{:0.2f}".format(iou_thr) else: iou = "{:0.2f}:{:0.2f}".format( self.params.iou_thrs[0], self.params.iou_thrs[-1] ) cat_group_name = "all" area_rng = "all" print(template.format(title, _type, iou, area_rng, max_dets, cat_group_name, value)) def get_results(self): if not self.results: self.logger.warn("results is empty. Call run().") return self.results class Params: def __init__(self, iou_type): self.img_ids = [] self.cat_ids = [] # np.arange causes trouble. the data point on arange is slightly # larger than the true value self.iou_thrs = np.linspace( 0.5, 0.95, int(np.round((0.95 - 0.5) / 0.05)) + 1, endpoint=True ) self.google_style = True # print('Using google style PR curve') self.iou_thrs = self.iou_thrs[:1] self.max_dets = 1000 self.area_rng = [ [0 ** 2, 1e5 ** 2], ] self.area_rng_lbl = ["all"] self.use_cats = 1 self.iou_type = iou_type class OIDEvaluator(DatasetEvaluator): def __init__(self, dataset_name, cfg, distributed, output_dir=None): self._distributed = distributed self._output_dir = output_dir self._cpu_device = torch.device("cpu") self._logger = logging.getLogger(__name__) self._metadata = MetadataCatalog.get(dataset_name) json_file = PathManager.get_local_path(self._metadata.json_file) self._oid_api = LVIS(json_file) # Test set json files do not contain annotations (evaluation must be # performed using the LVIS evaluation server). self._do_evaluation = len(self._oid_api.get_ann_ids()) > 0 self._mask_on = cfg.MODEL.MASK_ON def reset(self): self._predictions = [] self._oid_results = [] def process(self, inputs, outputs): for input, output in zip(inputs, outputs): prediction = {"image_id": input["image_id"]} instances = output["instances"].to(self._cpu_device) prediction["instances"] = instances_to_coco_json( instances, input["image_id"]) self._predictions.append(prediction) def evaluate(self): if self._distributed: comm.synchronize() self._predictions = comm.gather(self._predictions, dst=0) self._predictions = list(itertools.chain(*self._predictions)) if not comm.is_main_process(): return if len(self._predictions) == 0: self._logger.warning("[LVISEvaluator] Did not receive valid predictions.") return {} self._logger.info("Preparing results in the OID format ...") self._oid_results = list( itertools.chain(*[x["instances"] for x in self._predictions])) # unmap the category ids for LVIS (from 0-indexed to 1-indexed) for result in self._oid_results: result["category_id"] += 1 PathManager.mkdirs(self._output_dir) file_path = os.path.join( self._output_dir, "oid_instances_results.json") self._logger.info("Saving results to {}".format(file_path)) with PathManager.open(file_path, "w") as f: f.write(json.dumps(self._oid_results)) f.flush() if not self._do_evaluation: self._logger.info("Annotations are not available for evaluation.") return self._logger.info("Evaluating predictions ...") self._results = OrderedDict() res, mAP = _evaluate_predictions_on_oid( self._oid_api, file_path, eval_seg=self._mask_on, class_names=self._metadata.get("thing_classes"), ) self._results['bbox'] = res mAP_out_path = os.path.join(self._output_dir, "oid_mAP.npy") self._logger.info('Saving mAP to' + mAP_out_path) np.save(mAP_out_path, mAP) return copy.deepcopy(self._results) def _evaluate_predictions_on_oid( oid_gt, oid_results_path, eval_seg=False, class_names=None): logger = logging.getLogger(__name__) metrics = ["AP50", "AP50_expand"] results = {} oid_eval = OIDEval(oid_gt, oid_results_path, 'bbox', expand_pred_label=False) oid_eval.run() oid_eval.print_results() results["AP50"] = oid_eval.get_results()["AP50"] if eval_seg: oid_eval = OIDEval(oid_gt, oid_results_path, 'segm', expand_pred_label=False) oid_eval.run() oid_eval.print_results() results["AP50_segm"] = oid_eval.get_results()["AP50"] else: oid_eval = OIDEval(oid_gt, oid_results_path, 'bbox', expand_pred_label=True) oid_eval.run() oid_eval.print_results() results["AP50_expand"] = oid_eval.get_results()["AP50"] mAP = np.zeros(len(class_names)) - 1 precisions = oid_eval.eval['precision'] assert len(class_names) == precisions.shape[2] results_per_category = [] id2apiid = sorted(oid_gt.get_cat_ids()) inst_aware_ap, inst_count = 0, 0 for idx, name in enumerate(class_names): precision = precisions[:, :, idx, 0] precision = precision[precision > -1] ap = np.mean(precision) if precision.size else float("nan") inst_num = len(oid_gt.get_ann_ids(cat_ids=[id2apiid[idx]])) if inst_num > 0: results_per_category.append(("{} {}".format( name.replace(' ', '_'), inst_num if inst_num < 1000 else '{:.1f}k'.format(inst_num / 1000)), float(ap * 100))) inst_aware_ap += inst_num * ap inst_count += inst_num mAP[idx] = ap # logger.info("{} {} {:.2f}".format(name, inst_num, ap * 100)) inst_aware_ap = inst_aware_ap * 100 / inst_count N_COLS = min(6, len(results_per_category) * 2) results_flatten = list(itertools.chain(*results_per_category)) results_2d = itertools.zip_longest(*[results_flatten[i::N_COLS] for i in range(N_COLS)]) table = tabulate( results_2d, tablefmt="pipe", floatfmt=".3f", headers=["category", "AP"] * (N_COLS // 2), numalign="left", ) logger.info("Per-category {} AP: \n".format('bbox') + table) logger.info("Instance-aware {} AP: {:.4f}".format('bbox', inst_aware_ap)) logger.info("Evaluation results for bbox: \n" + \ create_small_table(results)) return results, mAP ================================================ FILE: vldet/modeling/backbone/swintransformer.py ================================================ # -------------------------------------------------------- # Swin Transformer # Copyright (c) 2021 Microsoft # Licensed under The MIT License [see LICENSE for details] # Written by Ze Liu, Yutong Lin, Yixuan Wei # -------------------------------------------------------- # Copyright (c) Facebook, Inc. and its affiliates. # Modified by Xingyi Zhou from https://github.com/SwinTransformer/Swin-Transformer-Object-Detection/blob/master/mmdet/models/backbones/swin_transformer.py import torch import torch.nn as nn import torch.nn.functional as F import torch.utils.checkpoint as checkpoint import numpy as np from timm.models.layers import DropPath, to_2tuple, trunc_normal_ from detectron2.layers import ShapeSpec from detectron2.modeling.backbone.backbone import Backbone from detectron2.modeling.backbone.build import BACKBONE_REGISTRY from detectron2.modeling.backbone.fpn import FPN from centernet.modeling.backbone.fpn_p5 import LastLevelP6P7_P5 from centernet.modeling.backbone.bifpn import BiFPN # from .checkpoint import load_checkpoint class Mlp(nn.Module): """ Multilayer perceptron.""" def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.): super().__init__() out_features = out_features or in_features hidden_features = hidden_features or in_features self.fc1 = nn.Linear(in_features, hidden_features) self.act = act_layer() self.fc2 = nn.Linear(hidden_features, out_features) self.drop = nn.Dropout(drop) def forward(self, x): x = self.fc1(x) x = self.act(x) x = self.drop(x) x = self.fc2(x) x = self.drop(x) return x def window_partition(x, window_size): """ Args: x: (B, H, W, C) window_size (int): window size Returns: windows: (num_windows*B, window_size, window_size, C) """ B, H, W, C = x.shape x = x.view(B, H // window_size, window_size, W // window_size, window_size, C) windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C) return windows def window_reverse(windows, window_size, H, W): """ Args: windows: (num_windows*B, window_size, window_size, C) window_size (int): Window size H (int): Height of image W (int): Width of image Returns: x: (B, H, W, C) """ B = int(windows.shape[0] / (H * W / window_size / window_size)) x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1) x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1) return x class WindowAttention(nn.Module): """ Window based multi-head self attention (W-MSA) module with relative position bias. It supports both of shifted and non-shifted window. Args: dim (int): Number of input channels. window_size (tuple[int]): The height and width of the window. num_heads (int): Number of attention heads. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set attn_drop (float, optional): Dropout ratio of attention weight. Default: 0.0 proj_drop (float, optional): Dropout ratio of output. Default: 0.0 """ def __init__(self, dim, window_size, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0., proj_drop=0.): super().__init__() self.dim = dim self.window_size = window_size # Wh, Ww self.num_heads = num_heads head_dim = dim // num_heads self.scale = qk_scale or head_dim ** -0.5 # define a parameter table of relative position bias self.relative_position_bias_table = nn.Parameter( torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads)) # 2*Wh-1 * 2*Ww-1, nH # get pair-wise relative position index for each token inside the window coords_h = torch.arange(self.window_size[0]) coords_w = torch.arange(self.window_size[1]) coords = torch.stack(torch.meshgrid([coords_h, coords_w])) # 2, Wh, Ww coords_flatten = torch.flatten(coords, 1) # 2, Wh*Ww relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :] # 2, Wh*Ww, Wh*Ww relative_coords = relative_coords.permute(1, 2, 0).contiguous() # Wh*Ww, Wh*Ww, 2 relative_coords[:, :, 0] += self.window_size[0] - 1 # shift to start from 0 relative_coords[:, :, 1] += self.window_size[1] - 1 relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1 relative_position_index = relative_coords.sum(-1) # Wh*Ww, Wh*Ww self.register_buffer("relative_position_index", relative_position_index) self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) self.attn_drop = nn.Dropout(attn_drop) self.proj = nn.Linear(dim, dim) self.proj_drop = nn.Dropout(proj_drop) trunc_normal_(self.relative_position_bias_table, std=.02) self.softmax = nn.Softmax(dim=-1) def forward(self, x, mask=None): """ Forward function. Args: x: input features with shape of (num_windows*B, N, C) mask: (0/-inf) mask with shape of (num_windows, Wh*Ww, Wh*Ww) or None """ B_, N, C = x.shape qkv = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4) q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) q = q * self.scale attn = (q @ k.transpose(-2, -1)) relative_position_bias = self.relative_position_bias_table[self.relative_position_index.view(-1)].view( self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1) # Wh*Ww,Wh*Ww,nH relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous() # nH, Wh*Ww, Wh*Ww attn = attn + relative_position_bias.unsqueeze(0) if mask is not None: nW = mask.shape[0] attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0) attn = attn.view(-1, self.num_heads, N, N) attn = self.softmax(attn) else: attn = self.softmax(attn) attn = self.attn_drop(attn) x = (attn @ v).transpose(1, 2).reshape(B_, N, C) x = self.proj(x) x = self.proj_drop(x) return x class SwinTransformerBlock(nn.Module): """ Swin Transformer Block. Args: dim (int): Number of input channels. num_heads (int): Number of attention heads. window_size (int): Window size. shift_size (int): Shift size for SW-MSA. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set. drop (float, optional): Dropout rate. Default: 0.0 attn_drop (float, optional): Attention dropout rate. Default: 0.0 drop_path (float, optional): Stochastic depth rate. Default: 0.0 act_layer (nn.Module, optional): Activation layer. Default: nn.GELU norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm """ def __init__(self, dim, num_heads, window_size=7, shift_size=0, mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm): super().__init__() self.dim = dim self.num_heads = num_heads self.window_size = window_size self.shift_size = shift_size self.mlp_ratio = mlp_ratio assert 0 <= self.shift_size < self.window_size, "shift_size must in 0-window_size" self.norm1 = norm_layer(dim) self.attn = WindowAttention( dim, window_size=to_2tuple(self.window_size), num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop) self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() self.norm2 = norm_layer(dim) mlp_hidden_dim = int(dim * mlp_ratio) self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) self.H = None self.W = None def forward(self, x, mask_matrix): """ Forward function. Args: x: Input feature, tensor size (B, H*W, C). H, W: Spatial resolution of the input feature. mask_matrix: Attention mask for cyclic shift. """ B, L, C = x.shape H, W = self.H, self.W assert L == H * W, "input feature has wrong size" shortcut = x x = self.norm1(x) x = x.view(B, H, W, C) # pad feature maps to multiples of window size pad_l = pad_t = 0 pad_r = (self.window_size - W % self.window_size) % self.window_size pad_b = (self.window_size - H % self.window_size) % self.window_size x = F.pad(x, (0, 0, pad_l, pad_r, pad_t, pad_b)) _, Hp, Wp, _ = x.shape # cyclic shift if self.shift_size > 0: shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2)) attn_mask = mask_matrix else: shifted_x = x attn_mask = None # partition windows x_windows = window_partition(shifted_x, self.window_size) # nW*B, window_size, window_size, C x_windows = x_windows.view(-1, self.window_size * self.window_size, C) # nW*B, window_size*window_size, C # W-MSA/SW-MSA attn_windows = self.attn(x_windows, mask=attn_mask) # nW*B, window_size*window_size, C # merge windows attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C) shifted_x = window_reverse(attn_windows, self.window_size, Hp, Wp) # B H' W' C # reverse cyclic shift if self.shift_size > 0: x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2)) else: x = shifted_x if pad_r > 0 or pad_b > 0: x = x[:, :H, :W, :].contiguous() x = x.view(B, H * W, C) # FFN x = shortcut + self.drop_path(x) x = x + self.drop_path(self.mlp(self.norm2(x))) return x class PatchMerging(nn.Module): """ Patch Merging Layer Args: dim (int): Number of input channels. norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm """ def __init__(self, dim, norm_layer=nn.LayerNorm): super().__init__() self.dim = dim self.reduction = nn.Linear(4 * dim, 2 * dim, bias=False) self.norm = norm_layer(4 * dim) def forward(self, x, H, W): """ Forward function. Args: x: Input feature, tensor size (B, H*W, C). H, W: Spatial resolution of the input feature. """ B, L, C = x.shape assert L == H * W, "input feature has wrong size" x = x.view(B, H, W, C) # padding pad_input = (H % 2 == 1) or (W % 2 == 1) if pad_input: x = F.pad(x, (0, 0, 0, W % 2, 0, H % 2)) x0 = x[:, 0::2, 0::2, :] # B H/2 W/2 C x1 = x[:, 1::2, 0::2, :] # B H/2 W/2 C x2 = x[:, 0::2, 1::2, :] # B H/2 W/2 C x3 = x[:, 1::2, 1::2, :] # B H/2 W/2 C x = torch.cat([x0, x1, x2, x3], -1) # B H/2 W/2 4*C x = x.view(B, -1, 4 * C) # B H/2*W/2 4*C x = self.norm(x) x = self.reduction(x) return x class BasicLayer(nn.Module): """ A basic Swin Transformer layer for one stage. Args: dim (int): Number of feature channels depth (int): Depths of this stage. num_heads (int): Number of attention head. window_size (int): Local window size. Default: 7. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4. qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set. drop (float, optional): Dropout rate. Default: 0.0 attn_drop (float, optional): Attention dropout rate. Default: 0.0 drop_path (float | tuple[float], optional): Stochastic depth rate. Default: 0.0 norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm downsample (nn.Module | None, optional): Downsample layer at the end of the layer. Default: None use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False. """ def __init__(self, dim, depth, num_heads, window_size=7, mlp_ratio=4., qkv_bias=True, qk_scale=None, drop=0., attn_drop=0., drop_path=0., norm_layer=nn.LayerNorm, downsample=None, use_checkpoint=False): super().__init__() self.window_size = window_size self.shift_size = window_size // 2 self.depth = depth self.use_checkpoint = use_checkpoint # build blocks self.blocks = nn.ModuleList([ SwinTransformerBlock( dim=dim, num_heads=num_heads, window_size=window_size, shift_size=0 if (i % 2 == 0) else window_size // 2, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, drop=drop, attn_drop=attn_drop, drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path, norm_layer=norm_layer) for i in range(depth)]) # patch merging layer if downsample is not None: self.downsample = downsample(dim=dim, norm_layer=norm_layer) else: self.downsample = None def forward(self, x, H, W): """ Forward function. Args: x: Input feature, tensor size (B, H*W, C). H, W: Spatial resolution of the input feature. """ # calculate attention mask for SW-MSA Hp = int(np.ceil(H / self.window_size)) * self.window_size Wp = int(np.ceil(W / self.window_size)) * self.window_size img_mask = torch.zeros((1, Hp, Wp, 1), device=x.device) # 1 Hp Wp 1 h_slices = (slice(0, -self.window_size), slice(-self.window_size, -self.shift_size), slice(-self.shift_size, None)) w_slices = (slice(0, -self.window_size), slice(-self.window_size, -self.shift_size), slice(-self.shift_size, None)) cnt = 0 for h in h_slices: for w in w_slices: img_mask[:, h, w, :] = cnt cnt += 1 mask_windows = window_partition(img_mask, self.window_size) # nW, window_size, window_size, 1 mask_windows = mask_windows.view(-1, self.window_size * self.window_size) attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2) attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0)) for blk in self.blocks: blk.H, blk.W = H, W if self.use_checkpoint: x = checkpoint.checkpoint(blk, x, attn_mask) else: x = blk(x, attn_mask) if self.downsample is not None: x_down = self.downsample(x, H, W) Wh, Ww = (H + 1) // 2, (W + 1) // 2 return x, H, W, x_down, Wh, Ww else: return x, H, W, x, H, W class PatchEmbed(nn.Module): """ Image to Patch Embedding Args: patch_size (int): Patch token size. Default: 4. in_chans (int): Number of input image channels. Default: 3. embed_dim (int): Number of linear projection output channels. Default: 96. norm_layer (nn.Module, optional): Normalization layer. Default: None """ def __init__(self, patch_size=4, in_chans=3, embed_dim=96, norm_layer=None): super().__init__() patch_size = to_2tuple(patch_size) self.patch_size = patch_size self.in_chans = in_chans self.embed_dim = embed_dim self.proj = nn.Conv2d(in_chans, embed_dim, kernel_size=patch_size, stride=patch_size) if norm_layer is not None: self.norm = norm_layer(embed_dim) else: self.norm = None def forward(self, x): """Forward function.""" # padding _, _, H, W = x.size() if W % self.patch_size[1] != 0: x = F.pad(x, (0, self.patch_size[1] - W % self.patch_size[1])) if H % self.patch_size[0] != 0: x = F.pad(x, (0, 0, 0, self.patch_size[0] - H % self.patch_size[0])) x = self.proj(x) # B C Wh Ww if self.norm is not None: Wh, Ww = x.size(2), x.size(3) x = x.flatten(2).transpose(1, 2) x = self.norm(x) x = x.transpose(1, 2).view(-1, self.embed_dim, Wh, Ww) return x class SwinTransformer(Backbone): """ Swin Transformer backbone. A PyTorch impl of : `Swin Transformer: Hierarchical Vision Transformer using Shifted Windows` - https://arxiv.org/pdf/2103.14030 Args: pretrain_img_size (int): Input image size for training the pretrained model, used in absolute postion embedding. Default 224. patch_size (int | tuple(int)): Patch size. Default: 4. in_chans (int): Number of input image channels. Default: 3. embed_dim (int): Number of linear projection output channels. Default: 96. depths (tuple[int]): Depths of each Swin Transformer stage. num_heads (tuple[int]): Number of attention head of each stage. window_size (int): Window size. Default: 7. mlp_ratio (float): Ratio of mlp hidden dim to embedding dim. Default: 4. qkv_bias (bool): If True, add a learnable bias to query, key, value. Default: True qk_scale (float): Override default qk scale of head_dim ** -0.5 if set. drop_rate (float): Dropout rate. attn_drop_rate (float): Attention dropout rate. Default: 0. drop_path_rate (float): Stochastic depth rate. Default: 0.2. norm_layer (nn.Module): Normalization layer. Default: nn.LayerNorm. ape (bool): If True, add absolute position embedding to the patch embedding. Default: False. patch_norm (bool): If True, add normalization after patch embedding. Default: True. out_indices (Sequence[int]): Output from which stages. frozen_stages (int): Stages to be frozen (stop grad and set eval mode). -1 means not freezing any parameters. use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False. """ def __init__(self, pretrain_img_size=224, patch_size=4, in_chans=3, embed_dim=96, depths=[2, 2, 6, 2], num_heads=[3, 6, 12, 24], window_size=7, mlp_ratio=4., qkv_bias=True, qk_scale=None, drop_rate=0., attn_drop_rate=0., drop_path_rate=0.2, norm_layer=nn.LayerNorm, ape=False, patch_norm=True, out_indices=(0, 1, 2, 3), frozen_stages=-1, use_checkpoint=False): super().__init__() self.pretrain_img_size = pretrain_img_size self.num_layers = len(depths) self.embed_dim = embed_dim self.ape = ape self.patch_norm = patch_norm self.out_indices = out_indices self.frozen_stages = frozen_stages # split image into non-overlapping patches self.patch_embed = PatchEmbed( patch_size=patch_size, in_chans=in_chans, embed_dim=embed_dim, norm_layer=norm_layer if self.patch_norm else None) # absolute position embedding if self.ape: pretrain_img_size = to_2tuple(pretrain_img_size) patch_size = to_2tuple(patch_size) patches_resolution = [pretrain_img_size[0] // patch_size[0], pretrain_img_size[1] // patch_size[1]] self.absolute_pos_embed = nn.Parameter(torch.zeros(1, embed_dim, patches_resolution[0], patches_resolution[1])) trunc_normal_(self.absolute_pos_embed, std=.02) self.pos_drop = nn.Dropout(p=drop_rate) # stochastic depth dpr = [x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] # stochastic depth decay rule # build layers self.layers = nn.ModuleList() for i_layer in range(self.num_layers): layer = BasicLayer( dim=int(embed_dim * 2 ** i_layer), depth=depths[i_layer], num_heads=num_heads[i_layer], window_size=window_size, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[sum(depths[:i_layer]):sum(depths[:i_layer + 1])], norm_layer=norm_layer, downsample=PatchMerging if (i_layer < self.num_layers - 1) else None, use_checkpoint=use_checkpoint) self.layers.append(layer) num_features = [int(embed_dim * 2 ** i) for i in range(self.num_layers)] self.num_features = num_features # add a norm layer for each output for i_layer in out_indices: layer = norm_layer(num_features[i_layer]) layer_name = f'norm{i_layer}' self.add_module(layer_name, layer) self._freeze_stages() self._out_features = ['swin{}'.format(i) for i in self.out_indices] self._out_feature_channels = { 'swin{}'.format(i): self.embed_dim * 2 ** i for i in self.out_indices } self._out_feature_strides = { 'swin{}'.format(i): 2 ** (i + 2) for i in self.out_indices } self._size_devisibility = 32 def _freeze_stages(self): if self.frozen_stages >= 0: self.patch_embed.eval() for param in self.patch_embed.parameters(): param.requires_grad = False if self.frozen_stages >= 1 and self.ape: self.absolute_pos_embed.requires_grad = False if self.frozen_stages >= 2: self.pos_drop.eval() for i in range(0, self.frozen_stages - 1): m = self.layers[i] m.eval() for param in m.parameters(): param.requires_grad = False def init_weights(self, pretrained=None): """Initialize the weights in backbone. Args: pretrained (str, optional): Path to pre-trained weights. Defaults to None. """ def _init_weights(m): if isinstance(m, nn.Linear): trunc_normal_(m.weight, std=.02) if isinstance(m, nn.Linear) and m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.LayerNorm): nn.init.constant_(m.bias, 0) nn.init.constant_(m.weight, 1.0) if isinstance(pretrained, str): self.apply(_init_weights) # load_checkpoint(self, pretrained, strict=False) elif pretrained is None: self.apply(_init_weights) else: raise TypeError('pretrained must be a str or None') def forward(self, x): """Forward function.""" x = self.patch_embed(x) Wh, Ww = x.size(2), x.size(3) if self.ape: # interpolate the position embedding to the corresponding size absolute_pos_embed = F.interpolate(self.absolute_pos_embed, size=(Wh, Ww), mode='bicubic') x = (x + absolute_pos_embed).flatten(2).transpose(1, 2) # B Wh*Ww C else: x = x.flatten(2).transpose(1, 2) x = self.pos_drop(x) # outs = [] outs = {} for i in range(self.num_layers): layer = self.layers[i] x_out, H, W, x, Wh, Ww = layer(x, Wh, Ww) if i in self.out_indices: norm_layer = getattr(self, f'norm{i}') x_out = norm_layer(x_out) out = x_out.view(-1, H, W, self.num_features[i]).permute(0, 3, 1, 2).contiguous() # outs.append(out) outs['swin{}'.format(i)] = out return outs def train(self, mode=True): """Convert the model into training mode while keep layers freezed.""" super(SwinTransformer, self).train(mode) self._freeze_stages() size2config = { 'T': { 'window_size': 7, 'embed_dim': 96, 'depth': [2, 2, 6, 2], 'num_heads': [3, 6, 12, 24], 'drop_path_rate': 0.2, 'pretrained': 'models/swin_tiny_patch4_window7_224.pth' }, 'S': { 'window_size': 7, 'embed_dim': 96, 'depth': [2, 2, 18, 2], 'num_heads': [3, 6, 12, 24], 'drop_path_rate': 0.2, 'pretrained': 'models/swin_small_patch4_window7_224.pth' }, 'B': { 'window_size': 7, 'embed_dim': 128, 'depth': [2, 2, 18, 2], 'num_heads': [4, 8, 16, 32], 'drop_path_rate': 0.3, 'pretrained': 'models/swin_base_patch4_window7_224.pth' }, 'B-22k': { 'window_size': 7, 'embed_dim': 128, 'depth': [2, 2, 18, 2], 'num_heads': [4, 8, 16, 32], 'drop_path_rate': 0.3, 'pretrained': 'models/swin_base_patch4_window7_224_22k.pth' }, 'B-22k-384': { 'window_size': 12, 'embed_dim': 128, 'depth': [2, 2, 18, 2], 'num_heads': [4, 8, 16, 32], 'drop_path_rate': 0.3, 'pretrained': 'models/swin_base_patch4_window12_384_22k.pth' }, 'L-22k': { 'window_size': 7, 'embed_dim': 192, 'depth': [2, 2, 18, 2], 'num_heads': [6, 12, 24, 48], 'drop_path_rate': 0.3, # TODO (xingyi): this is unclear 'pretrained': 'models/swin_large_patch4_window7_224_22k.pth' }, 'L-22k-384': { 'window_size': 12, 'embed_dim': 192, 'depth': [2, 2, 18, 2], 'num_heads': [6, 12, 24, 48], 'drop_path_rate': 0.3, # TODO (xingyi): this is unclear 'pretrained': 'models/swin_large_patch4_window12_384_22k.pth' } } @BACKBONE_REGISTRY.register() def build_swintransformer_backbone(cfg, input_shape): """ """ config = size2config[cfg.MODEL.SWIN.SIZE] out_indices = cfg.MODEL.SWIN.OUT_FEATURES model = SwinTransformer( embed_dim=config['embed_dim'], window_size=config['window_size'], depths=config['depth'], num_heads=config['num_heads'], drop_path_rate=config['drop_path_rate'], out_indices=out_indices, frozen_stages=-1, use_checkpoint=cfg.MODEL.SWIN.USE_CHECKPOINT ) # print('Initializing', config['pretrained']) model.init_weights(config['pretrained']) return model @BACKBONE_REGISTRY.register() def build_swintransformer_fpn_backbone(cfg, input_shape: ShapeSpec): """ """ bottom_up = build_swintransformer_backbone(cfg, input_shape) in_features = cfg.MODEL.FPN.IN_FEATURES out_channels = cfg.MODEL.FPN.OUT_CHANNELS backbone = FPN( bottom_up=bottom_up, in_features=in_features, out_channels=out_channels, norm=cfg.MODEL.FPN.NORM, top_block=LastLevelP6P7_P5(out_channels, out_channels), fuse_type=cfg.MODEL.FPN.FUSE_TYPE, ) return backbone @BACKBONE_REGISTRY.register() def build_swintransformer_bifpn_backbone(cfg, input_shape: ShapeSpec): """ """ bottom_up = build_swintransformer_backbone(cfg, input_shape) in_features = cfg.MODEL.FPN.IN_FEATURES backbone = BiFPN( cfg=cfg, bottom_up=bottom_up, in_features=in_features, out_channels=cfg.MODEL.BIFPN.OUT_CHANNELS, norm=cfg.MODEL.BIFPN.NORM, num_levels=cfg.MODEL.BIFPN.NUM_LEVELS, num_bifpn=cfg.MODEL.BIFPN.NUM_BIFPN, separable_conv=cfg.MODEL.BIFPN.SEPARABLE_CONV, ) return backbone ================================================ FILE: vldet/modeling/backbone/timm.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (c) Facebook, Inc. and its affiliates. import math from os.path import join import numpy as np import copy from functools import partial import torch from torch import nn import torch.utils.model_zoo as model_zoo import torch.nn.functional as F import fvcore.nn.weight_init as weight_init from detectron2.modeling.backbone import FPN from detectron2.modeling.backbone.build import BACKBONE_REGISTRY from detectron2.layers.batch_norm import get_norm, FrozenBatchNorm2d from detectron2.modeling.backbone import Backbone from timm import create_model from timm.models.helpers import build_model_with_cfg from timm.models.registry import register_model from timm.models.resnet import ResNet, Bottleneck from timm.models.resnet import default_cfgs as default_cfgs_resnet class CustomResNet(ResNet): def __init__(self, **kwargs): self.out_indices = kwargs.pop('out_indices') super().__init__(**kwargs) def forward(self, x): x = self.conv1(x) x = self.bn1(x) x = self.act1(x) x = self.maxpool(x) ret = [x] x = self.layer1(x) ret.append(x) x = self.layer2(x) ret.append(x) x = self.layer3(x) ret.append(x) x = self.layer4(x) ret.append(x) return [ret[i] for i in self.out_indices] def load_pretrained(self, cached_file): data = torch.load(cached_file, map_location='cpu') if 'state_dict' in data: self.load_state_dict(data['state_dict']) else: self.load_state_dict(data) model_params = { 'resnet50': dict(block=Bottleneck, layers=[3, 4, 6, 3]), 'resnet50_in21k': dict(block=Bottleneck, layers=[3, 4, 6, 3]), } def create_timm_resnet(variant, out_indices, pretrained=False, **kwargs): params = model_params[variant] default_cfgs_resnet['resnet50_in21k'] = \ copy.deepcopy(default_cfgs_resnet['resnet50']) default_cfgs_resnet['resnet50_in21k']['url'] = \ 'https://miil-public-eu.oss-eu-central-1.aliyuncs.com/model-zoo/ImageNet_21K_P/models/resnet50_miil_21k.pth' default_cfgs_resnet['resnet50_in21k']['num_classes'] = 11221 return build_model_with_cfg( CustomResNet, variant, pretrained, default_cfg=default_cfgs_resnet[variant], out_indices=out_indices, pretrained_custom_load=True, **params, **kwargs) class LastLevelP6P7_P5(nn.Module): """ """ def __init__(self, in_channels, out_channels): super().__init__() self.num_levels = 2 self.in_feature = "p5" self.p6 = nn.Conv2d(in_channels, out_channels, 3, 2, 1) self.p7 = nn.Conv2d(out_channels, out_channels, 3, 2, 1) for module in [self.p6, self.p7]: weight_init.c2_xavier_fill(module) def forward(self, c5): p6 = self.p6(c5) p7 = self.p7(F.relu(p6)) return [p6, p7] def freeze_module(x): """ """ for p in x.parameters(): p.requires_grad = False FrozenBatchNorm2d.convert_frozen_batchnorm(x) return x class TIMM(Backbone): def __init__(self, base_name, out_levels, freeze_at=0, norm='FrozenBN'): super().__init__() out_indices = [x - 1 for x in out_levels] if 'resnet' in base_name: self.base = create_timm_resnet( base_name, out_indices=out_indices, pretrained=False) elif 'eff' in base_name: self.base = create_model( base_name, features_only=True, out_indices=out_indices, pretrained=True) else: assert 0, base_name feature_info = [dict(num_chs=f['num_chs'], reduction=f['reduction']) \ for i, f in enumerate(self.base.feature_info)] self._out_features = ['layer{}'.format(x) for x in out_levels] self._out_feature_channels = { 'layer{}'.format(l): feature_info[l - 1]['num_chs'] for l in out_levels} self._out_feature_strides = { 'layer{}'.format(l): feature_info[l - 1]['reduction'] for l in out_levels} self._size_divisibility = max(self._out_feature_strides.values()) if 'resnet' in base_name: self.freeze(freeze_at) if norm == 'FrozenBN': self = FrozenBatchNorm2d.convert_frozen_batchnorm(self) def freeze(self, freeze_at=0): """ """ if freeze_at >= 1: print('Frezing', self.base.conv1) self.base.conv1 = freeze_module(self.base.conv1) if freeze_at >= 2: print('Frezing', self.base.layer1) self.base.layer1 = freeze_module(self.base.layer1) def forward(self, x): features = self.base(x) ret = {k: v for k, v in zip(self._out_features, features)} return ret @property def size_divisibility(self): return self._size_divisibility @BACKBONE_REGISTRY.register() def build_timm_backbone(cfg, input_shape): model = TIMM( cfg.MODEL.TIMM.BASE_NAME, cfg.MODEL.TIMM.OUT_LEVELS, freeze_at=cfg.MODEL.TIMM.FREEZE_AT, norm=cfg.MODEL.TIMM.NORM, ) return model @BACKBONE_REGISTRY.register() def build_p67_timm_fpn_backbone(cfg, input_shape): """ """ bottom_up = build_timm_backbone(cfg, input_shape) in_features = cfg.MODEL.FPN.IN_FEATURES out_channels = cfg.MODEL.FPN.OUT_CHANNELS backbone = FPN( bottom_up=bottom_up, in_features=in_features, out_channels=out_channels, norm=cfg.MODEL.FPN.NORM, top_block=LastLevelP6P7_P5(out_channels, out_channels), fuse_type=cfg.MODEL.FPN.FUSE_TYPE, ) return backbone @BACKBONE_REGISTRY.register() def build_p35_timm_fpn_backbone(cfg, input_shape): """ """ bottom_up = build_timm_backbone(cfg, input_shape) in_features = cfg.MODEL.FPN.IN_FEATURES out_channels = cfg.MODEL.FPN.OUT_CHANNELS backbone = FPN( bottom_up=bottom_up, in_features=in_features, out_channels=out_channels, norm=cfg.MODEL.FPN.NORM, top_block=None, fuse_type=cfg.MODEL.FPN.FUSE_TYPE, ) return backbone ================================================ FILE: vldet/modeling/debug.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import cv2 import numpy as np import torch import torch.nn.functional as F import os COLORS = ((np.random.rand(1300, 3) * 0.4 + 0.6) * 255).astype( np.uint8).reshape(1300, 1, 1, 3) def _get_color_image(heatmap): heatmap = heatmap.reshape( heatmap.shape[0], heatmap.shape[1], heatmap.shape[2], 1) if heatmap.shape[0] == 1: color_map = (heatmap * np.ones((1, 1, 1, 3), np.uint8) * 255).max( axis=0).astype(np.uint8) # H, W, 3 else: color_map = (heatmap * COLORS[:heatmap.shape[0]]).max(axis=0).astype(np.uint8) # H, W, 3 return color_map def _blend_image(image, color_map, a=0.7): color_map = cv2.resize(color_map, (image.shape[1], image.shape[0])) ret = np.clip(image * (1 - a) + color_map * a, 0, 255).astype(np.uint8) return ret def _blend_image_heatmaps(image, color_maps, a=0.7): merges = np.zeros((image.shape[0], image.shape[1], 3), np.float32) for color_map in color_maps: color_map = cv2.resize(color_map, (image.shape[1], image.shape[0])) merges = np.maximum(merges, color_map) ret = np.clip(image * (1 - a) + merges * a, 0, 255).astype(np.uint8) return ret def _decompose_level(x, shapes_per_level, N): ''' x: LNHiWi x C ''' x = x.view(x.shape[0], -1) ret = [] st = 0 for l in range(len(shapes_per_level)): ret.append([]) h = shapes_per_level[l][0].int().item() w = shapes_per_level[l][1].int().item() for i in range(N): ret[l].append(x[st + h * w * i:st + h * w * (i + 1)].view( h, w, -1).permute(2, 0, 1)) st += h * w * N return ret def _imagelist_to_tensor(images): images = [x for x in images] image_sizes = [x.shape[-2:] for x in images] h = max([size[0] for size in image_sizes]) w = max([size[1] for size in image_sizes]) S = 32 h, w = ((h - 1) // S + 1) * S, ((w - 1) // S + 1) * S images = [F.pad(x, (0, w - x.shape[2], 0, h - x.shape[1], 0, 0)) \ for x in images] images = torch.stack(images) return images def _ind2il(ind, shapes_per_level, N): r = ind l = 0 S = 0 while r - S >= N * shapes_per_level[l][0] * shapes_per_level[l][1]: S += N * shapes_per_level[l][0] * shapes_per_level[l][1] l += 1 i = (r - S) // (shapes_per_level[l][0] * shapes_per_level[l][1]) return i, l def debug_train( images, gt_instances, flattened_hms, reg_targets, labels, pos_inds, shapes_per_level, locations, strides): ''' images: N x 3 x H x W flattened_hms: LNHiWi x C shapes_per_level: L x 2 [(H_i, W_i)] locations: LNHiWi x 2 ''' reg_inds = torch.nonzero( reg_targets.max(dim=1)[0] > 0).squeeze(1) N = len(images) images = _imagelist_to_tensor(images) repeated_locations = [torch.cat([loc] * N, dim=0) \ for loc in locations] locations = torch.cat(repeated_locations, dim=0) gt_hms = _decompose_level(flattened_hms, shapes_per_level, N) masks = flattened_hms.new_zeros((flattened_hms.shape[0], 1)) masks[pos_inds] = 1 masks = _decompose_level(masks, shapes_per_level, N) for i in range(len(images)): image = images[i].detach().cpu().numpy().transpose(1, 2, 0) color_maps = [] for l in range(len(gt_hms)): color_map = _get_color_image( gt_hms[l][i].detach().cpu().numpy()) color_maps.append(color_map) cv2.imshow('gthm_{}'.format(l), color_map) blend = _blend_image_heatmaps(image.copy(), color_maps) if gt_instances is not None: bboxes = gt_instances[i].gt_boxes.tensor for j in range(len(bboxes)): bbox = bboxes[j] cv2.rectangle( blend, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), (0, 0, 255), 3, cv2.LINE_AA) for j in range(len(pos_inds)): image_id, l = _ind2il(pos_inds[j], shapes_per_level, N) if image_id != i: continue loc = locations[pos_inds[j]] cv2.drawMarker( blend, (int(loc[0]), int(loc[1])), (0, 255, 255), markerSize=(l + 1) * 16) for j in range(len(reg_inds)): image_id, l = _ind2il(reg_inds[j], shapes_per_level, N) if image_id != i: continue ltrb = reg_targets[reg_inds[j]] ltrb *= strides[l] loc = locations[reg_inds[j]] bbox = [(loc[0] - ltrb[0]), (loc[1] - ltrb[1]), (loc[0] + ltrb[2]), (loc[1] + ltrb[3])] cv2.rectangle( blend, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), (255, 0, 0), 1, cv2.LINE_AA) cv2.circle(blend, (int(loc[0]), int(loc[1])), 2, (255, 0, 0), -1) cv2.imshow('blend', blend) cv2.waitKey() def debug_test( images, logits_pred, reg_pred, agn_hm_pred=[], preds=[], vis_thresh=0.3, debug_show_name=False, mult_agn=False): ''' images: N x 3 x H x W class_target: LNHiWi x C cat_agn_heatmap: LNHiWi shapes_per_level: L x 2 [(H_i, W_i)] ''' N = len(images) for i in range(len(images)): image = images[i].detach().cpu().numpy().transpose(1, 2, 0) result = image.copy().astype(np.uint8) pred_image = image.copy().astype(np.uint8) color_maps = [] L = len(logits_pred) for l in range(L): if logits_pred[0] is not None: stride = min(image.shape[0], image.shape[1]) / min( logits_pred[l][i].shape[1], logits_pred[l][i].shape[2]) else: stride = min(image.shape[0], image.shape[1]) / min( agn_hm_pred[l][i].shape[1], agn_hm_pred[l][i].shape[2]) stride = stride if stride < 60 else 64 if stride < 100 else 128 if logits_pred[0] is not None: if mult_agn: logits_pred[l][i] = logits_pred[l][i] * agn_hm_pred[l][i] color_map = _get_color_image( logits_pred[l][i].detach().cpu().numpy()) color_maps.append(color_map) cv2.imshow('predhm_{}'.format(l), color_map) if debug_show_name: from detectron2.data.datasets.lvis_v1_categories import LVIS_CATEGORIES cat2name = [x['name'] for x in LVIS_CATEGORIES] for j in range(len(preds[i].scores) if preds is not None else 0): if preds[i].scores[j] > vis_thresh: bbox = preds[i].proposal_boxes[j] \ if preds[i].has('proposal_boxes') else \ preds[i].pred_boxes[j] bbox = bbox.tensor[0].detach().cpu().numpy().astype(np.int32) cat = int(preds[i].pred_classes[j]) \ if preds[i].has('pred_classes') else 0 cl = COLORS[cat, 0, 0] cv2.rectangle( pred_image, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), (int(cl[0]), int(cl[1]), int(cl[2])), 2, cv2.LINE_AA) if debug_show_name: txt = '{}{:.1f}'.format( cat2name[cat] if cat > 0 else '', preds[i].scores[j]) font = cv2.FONT_HERSHEY_SIMPLEX cat_size = cv2.getTextSize(txt, font, 0.5, 2)[0] cv2.rectangle( pred_image, (int(bbox[0]), int(bbox[1] - cat_size[1] - 2)), (int(bbox[0] + cat_size[0]), int(bbox[1] - 2)), (int(cl[0]), int(cl[1]), int(cl[2])), -1) cv2.putText( pred_image, txt, (int(bbox[0]), int(bbox[1] - 2)), font, 0.5, (0, 0, 0), thickness=1, lineType=cv2.LINE_AA) if agn_hm_pred[l] is not None: agn_hm_ = agn_hm_pred[l][i, 0, :, :, None].detach().cpu().numpy() agn_hm_ = (agn_hm_ * np.array([255, 255, 255]).reshape( 1, 1, 3)).astype(np.uint8) cv2.imshow('agn_hm_{}'.format(l), agn_hm_) blend = _blend_image_heatmaps(image.copy(), color_maps) cv2.imshow('blend', blend) cv2.imshow('preds', pred_image) cv2.waitKey() global cnt cnt = 0 def debug_second_stage(images, instances, proposals=None, vis_thresh=0.3, save_debug=False, debug_show_name=False, image_labels=[], save_debug_path='output/save_debug/', bgr=False): images = _imagelist_to_tensor(images) if 'COCO' in save_debug_path: from detectron2.data.datasets.builtin_meta import COCO_CATEGORIES cat2name = [x['name'] for x in COCO_CATEGORIES] else: from detectron2.data.datasets.lvis_v1_categories import LVIS_CATEGORIES cat2name = ['({}){}'.format(x['frequency'], x['name']) \ for x in LVIS_CATEGORIES] for i in range(len(images)): image = images[i].detach().cpu().numpy().transpose(1, 2, 0).astype(np.uint8).copy() if bgr: image = image[:, :, ::-1].copy() if instances[i].has('gt_boxes'): bboxes = instances[i].gt_boxes.tensor.cpu().numpy() scores = np.ones(bboxes.shape[0]) cats = instances[i].gt_classes.cpu().numpy() else: bboxes = instances[i].pred_boxes.tensor.cpu().numpy() scores = instances[i].scores.cpu().numpy() cats = instances[i].pred_classes.cpu().numpy() for j in range(len(bboxes)): if scores[j] > vis_thresh: bbox = bboxes[j] cl = COLORS[cats[j], 0, 0] cl = (int(cl[0]), int(cl[1]), int(cl[2])) cv2.rectangle( image, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), cl, 2, cv2.LINE_AA) if debug_show_name: cat = cats[j] txt = '{}{:.1f}'.format( cat2name[cat] if cat > 0 else '', scores[j]) font = cv2.FONT_HERSHEY_SIMPLEX cat_size = cv2.getTextSize(txt, font, 0.5, 2)[0] cv2.rectangle( image, (int(bbox[0]), int(bbox[1] - cat_size[1] - 2)), (int(bbox[0] + cat_size[0]), int(bbox[1] - 2)), (int(cl[0]), int(cl[1]), int(cl[2])), -1) cv2.putText( image, txt, (int(bbox[0]), int(bbox[1] - 2)), font, 0.5, (0, 0, 0), thickness=1, lineType=cv2.LINE_AA) if proposals is not None: proposal_image = images[i].detach().cpu().numpy().transpose(1, 2, 0).astype(np.uint8).copy() if bgr: proposal_image = proposal_image.copy() else: proposal_image = proposal_image[:, :, ::-1].copy() bboxes = proposals[i].proposal_boxes.tensor.cpu().numpy() if proposals[i].has('scores'): scores = proposals[i].scores.detach().cpu().numpy() else: scores = proposals[i].objectness_logits.detach().cpu().numpy() # selected = -1 # if proposals[i].has('image_loss'): # selected = proposals[i].image_loss.argmin() if proposals[i].has('selected'): selected = proposals[i].selected else: selected = [-1 for _ in range(len(bboxes))] for j in range(len(bboxes)): if scores[j] > vis_thresh or selected[j] >= 0: bbox = bboxes[j] cl = (209, 159, 83) th = 2 if selected[j] >= 0: cl = (0, 0, 0xa4) th = 4 cv2.rectangle( proposal_image, (int(bbox[0]), int(bbox[1])), (int(bbox[2]), int(bbox[3])), cl, th, cv2.LINE_AA) if selected[j] >= 0 and debug_show_name: cat = selected[j].item() txt = '{}'.format(cat2name[cat]) font = cv2.FONT_HERSHEY_SIMPLEX cat_size = cv2.getTextSize(txt, font, 0.5, 2)[0] cv2.rectangle( proposal_image, (int(bbox[0]), int(bbox[1] - cat_size[1] - 2)), (int(bbox[0] + cat_size[0]), int(bbox[1] - 2)), (int(cl[0]), int(cl[1]), int(cl[2])), -1) cv2.putText( proposal_image, txt, (int(bbox[0]), int(bbox[1] - 2)), font, 0.5, (0, 0, 0), thickness=1, lineType=cv2.LINE_AA) if save_debug: global cnt cnt = (cnt + 1) % 5000 if not os.path.exists(save_debug_path): os.mkdir(save_debug_path) save_name = '{}/{:05d}.jpg'.format(save_debug_path, cnt) if i < len(image_labels): image_label = image_labels[i] save_name = '{}/{:05d}'.format(save_debug_path, cnt) for x in image_label: class_name = cat2name[x] save_name = save_name + '|{}'.format(class_name) save_name = save_name + '.jpg' cv2.imwrite(save_name, proposal_image) else: cv2.imshow('image', image) if proposals is not None: cv2.imshow('proposals', proposal_image) cv2.waitKey() ================================================ FILE: vldet/modeling/meta_arch/custom_rcnn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import logging import numpy as np from typing import Dict, List, Optional, Tuple import torch from torch import nn import json from detectron2.utils.events import get_event_storage from detectron2.config import configurable from detectron2.structures import ImageList, Instances, Boxes import detectron2.utils.comm as comm from detectron2.modeling.meta_arch.build import META_ARCH_REGISTRY from detectron2.modeling.meta_arch.rcnn import GeneralizedRCNN from detectron2.modeling.postprocessing import detector_postprocess from detectron2.utils.visualizer import Visualizer, _create_text_labels from detectron2.data.detection_utils import convert_image_to_rgb from torch.cuda.amp import autocast from ..text.text_encoder import build_text_encoder, build_RN50_text_encoder from ..utils import load_class_freq, get_fed_loss_inds import clip @META_ARCH_REGISTRY.register() class CustomRCNN(GeneralizedRCNN): ''' Add image labels ''' @configurable def __init__( self, with_image_labels = False, dataset_loss_weight = [], fp16 = False, sync_caption_batch = False, roi_head_name = '', cap_batch_ratio = 4, with_caption = False, dynamic_classifier = False, cfg = None, **kwargs): """ """ self.with_image_labels = with_image_labels self.dataset_loss_weight = dataset_loss_weight self.fp16 = fp16 self.with_caption = with_caption self.sync_caption_batch = sync_caption_batch self.roi_head_name = roi_head_name self.cap_batch_ratio = cap_batch_ratio self.return_proposal = False super().__init__(**kwargs) assert self.proposal_generator is not None if self.with_caption: self.rn50_model, self.tokenizer = build_RN50_text_encoder(pretrain=True) for v in self.rn50_model.parameters(): v.requires_grad = False @classmethod def from_config(cls, cfg): ret = super().from_config(cfg) from detectron2.utils.registry import Registry from detectron2.layers import ShapeSpec input_shape = ShapeSpec(channels=len(cfg.MODEL.PIXEL_MEAN)) ret.update({ 'with_image_labels': cfg.WITH_IMAGE_LABELS, 'dataset_loss_weight': cfg.MODEL.DATASET_LOSS_WEIGHT, 'fp16': cfg.FP16, 'with_caption': cfg.MODEL.WITH_CAPTION, 'sync_caption_batch': cfg.MODEL.SYNC_CAPTION_BATCH, 'roi_head_name': cfg.MODEL.ROI_HEADS.NAME, 'cap_batch_ratio': cfg.MODEL.CAP_BATCH_RATIO, }) ret['cfg'] = cfg return ret def inference( self, batched_inputs: Tuple[Dict[str, torch.Tensor]], detected_instances: Optional[List[Instances]] = None, do_postprocess: bool = True, ): assert not self.training assert detected_instances is None images = self.preprocess_image(batched_inputs) features = self.backbone(images.tensor) proposals, _ = self.proposal_generator(images, features, None) results, _ = self.roi_heads(images, features, proposals) if do_postprocess: assert not torch.jit.is_scripting(), \ "Scripting is not supported for postprocess." return CustomRCNN._postprocess( results, batched_inputs, images.image_sizes) else: return results def forward(self, batched_inputs: List[Dict[str, torch.Tensor]]): """ Add ann_type Ignore proposal loss when training with image labels """ if not self.training: return self.inference(batched_inputs) images = self.preprocess_image(batched_inputs) ann_type = 'box' gt_instances = [x["instances"].to(self.device) for x in batched_inputs] if self.with_image_labels: for inst, x in zip(gt_instances, batched_inputs): inst._ann_type = x['ann_type'] inst._pos_category_ids = x['pos_category_ids'] ann_types = [x['ann_type'] for x in batched_inputs] assert len(set(ann_types)) == 1 ann_type = ann_types[0] if ann_type in ['prop', 'proptag']: for t in gt_instances: t.gt_classes *= 0 if self.fp16: # TODO (zhouxy): improve with autocast(): features = self.backbone(images.tensor.half()) features = {k: v.float() for k, v in features.items()} else: features = self.backbone(images.tensor) cls_features, cls_inds, caption_features = None, None, None if self.with_caption and 'caption' in ann_type: inds = [torch.randint(len(x['captions']), (1,))[0].item() \ for x in batched_inputs] caps = [x['captions'][ind] for ind, x in zip(inds, batched_inputs)] text = self.tokenizer.tokenize(caps).to(self.device) caption_features = self.rn50_model.encode_text(text) if self.sync_caption_batch: caption_features = self._sync_caption_features( caption_features, ann_type, len(batched_inputs)) classifier_info = cls_features, cls_inds, caption_features proposals, proposal_losses = self.proposal_generator( images, features, gt_instances) if self.roi_head_name in ['StandardROIHeads', 'CascadeROIHeads']: proposals, detector_losses = self.roi_heads( images, features, proposals, gt_instances) else: proposals, detector_losses = self.roi_heads( images, features, proposals, gt_instances, ann_type=ann_type, classifier_info=classifier_info) if self.vis_period > 0: storage = get_event_storage() if storage.iter % self.vis_period == 0: self.visualize_training(batched_inputs, proposals) losses = {} losses.update(detector_losses) if self.with_image_labels: if ann_type in ['box', 'prop', 'proptag']: losses.update(proposal_losses) else: # ignore proposal loss for non-bbox data losses.update({k: v * 0 for k, v in proposal_losses.items()}) else: losses.update(proposal_losses) if len(self.dataset_loss_weight) > 0: dataset_sources = [x['dataset_source'] for x in batched_inputs] assert len(set(dataset_sources)) == 1 dataset_source = dataset_sources[0] for k in losses: losses[k] *= self.dataset_loss_weight[dataset_source] if self.return_proposal: return proposals, losses else: return losses def _sync_caption_features(self, caption_features, ann_type, BS): has_caption_feature = (caption_features is not None) BS = (BS * self.cap_batch_ratio) if (ann_type == 'box') else BS rank = torch.full( (BS, 1), comm.get_rank(), dtype=torch.float32, device=self.device) if not has_caption_feature: caption_features = rank.new_zeros((BS, 512)) caption_features = torch.cat([caption_features, rank], dim=1) global_caption_features = comm.all_gather(caption_features) caption_features = torch.cat( [x.to(self.device) for x in global_caption_features], dim=0) \ if has_caption_feature else None # (NB) x (D + 1) return caption_features def _sample_cls_inds(self, gt_instances, ann_type='box'): if ann_type == 'box': gt_classes = torch.cat( [x.gt_classes for x in gt_instances]) C = len(self.freq_weight) freq_weight = self.freq_weight else: gt_classes = torch.cat( [torch.tensor( x._pos_category_ids, dtype=torch.long, device=x.gt_classes.device) \ for x in gt_instances]) C = self.num_classes freq_weight = None assert gt_classes.max() < C, '{} {}'.format(gt_classes.max(), C) inds = get_fed_loss_inds( gt_classes, self.num_sample_cats, C, weight=freq_weight) cls_id_map = gt_classes.new_full( (self.num_classes + 1,), len(inds)) cls_id_map[inds] = torch.arange(len(inds), device=cls_id_map.device) return inds, cls_id_map ================================================ FILE: vldet/modeling/roi_heads/res5_roi_heads.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import inspect import logging import numpy as np from typing import Dict, List, Optional, Tuple import torch from torch import nn import torch.nn.functional as F from detectron2.config import configurable from detectron2.layers import ShapeSpec, nonzero_tuple from detectron2.structures import Boxes, ImageList, Instances, pairwise_iou from detectron2.utils.events import get_event_storage from detectron2.utils.registry import Registry from detectron2.modeling.box_regression import Box2BoxTransform from detectron2.modeling.roi_heads.fast_rcnn import fast_rcnn_inference from detectron2.modeling.roi_heads.roi_heads import ROI_HEADS_REGISTRY, Res5ROIHeads from detectron2.modeling.roi_heads.cascade_rcnn import CascadeROIHeads, _ScaleGradient from detectron2.modeling.roi_heads.box_head import build_box_head from .vldet_fast_rcnn import VLDetFastRCNNOutputLayers from ..debug import debug_second_stage from torch.cuda.amp import autocast @ROI_HEADS_REGISTRY.register() class CustomRes5ROIHeads(Res5ROIHeads): @configurable def __init__(self, **kwargs): cfg = kwargs.pop('cfg') super().__init__(**kwargs) stage_channel_factor = 2 ** 3 out_channels = cfg.MODEL.RESNETS.RES2_OUT_CHANNELS * stage_channel_factor self.with_image_labels = cfg.WITH_IMAGE_LABELS self.ws_num_props = cfg.MODEL.ROI_BOX_HEAD.WS_NUM_PROPS self.add_image_box = cfg.MODEL.ROI_BOX_HEAD.ADD_IMAGE_BOX self.add_feature_to_prop = cfg.MODEL.ROI_BOX_HEAD.ADD_FEATURE_TO_PROP self.image_box_size = cfg.MODEL.ROI_BOX_HEAD.IMAGE_BOX_SIZE self.dis_loss_weight = cfg.MODEL.ROI_BOX_HEAD.DIS_LOSS_WEIGHT self.box_predictor = VLDetFastRCNNOutputLayers( cfg, ShapeSpec(channels=out_channels, height=1, width=1) ) self.save_debug = cfg.SAVE_DEBUG self.save_debug_path = cfg.SAVE_DEBUG_PATH if self.save_debug: self.debug_show_name = cfg.DEBUG_SHOW_NAME self.vis_thresh = cfg.VIS_THRESH self.pixel_mean = torch.Tensor(cfg.MODEL.PIXEL_MEAN).to( torch.device(cfg.MODEL.DEVICE)).view(3, 1, 1) self.pixel_std = torch.Tensor(cfg.MODEL.PIXEL_STD).to( torch.device(cfg.MODEL.DEVICE)).view(3, 1, 1) self.bgr = (cfg.INPUT.FORMAT == 'BGR') self.use_caption = cfg.MODEL.ROI_BOX_HEAD.USE_CAPTION self.use_ot = cfg.MODEL.ROI_BOX_HEAD.USE_OT self.use_distill = cfg.MODEL.ROI_BOX_HEAD.USE_DISTILL self.disloss = nn.KLDivLoss(reduction='batchmean') @classmethod def from_config(cls, cfg, input_shape): ret = super().from_config(cfg, input_shape) ret['cfg'] = cfg return ret def _shared_roi_transform(self, features: List[torch.Tensor], boxes: List[Boxes]): x = self.pooler(features, boxes) return self.res5(x) def forward(self, images, features, proposals, targets=None, ann_type='box', classifier_info=(None,None,None)): ''' enable debug and image labels classifier_info is shared across the batch ''' if not self.save_debug: del images if self.training: if ann_type in ['box']: proposals = self.label_and_sample_proposals( proposals, targets) else: proposals = self.get_top_proposals(proposals) proposal_boxes = [x.proposal_boxes for x in proposals] box_features = self._shared_roi_transform( [features[f] for f in self.in_features], proposal_boxes ) predictions = self.box_predictor( box_features.mean(dim=[2, 3]), ann_type, classifier_info=classifier_info) if self.add_feature_to_prop: feats_per_image = box_features.mean(dim=[2, 3]).split( [len(p) for p in proposals], dim=0) for feat, p in zip(feats_per_image, proposals): p.feat = feat if self.training: del features if (ann_type != 'box'): image_labels = [x._pos_category_ids for x in targets] losses = {} if self.use_caption: caption_losses = self.box_predictor.image_label_losses( predictions, proposals, image_labels, classifier_info=classifier_info, ann_type=ann_type) losses.update({'caption_loss': caption_losses}) if self.use_ot == 'BCE': ot_loss = self.box_predictor.align_BCE_loss( predictions, proposals, targets, classifier_info=classifier_info, ann_type=ann_type) losses.update({'ot_loss': ot_loss}) elif self.use_ot == 'contrastive': ot_loss = self.box_predictor.align_contrastive_loss( predictions, proposals, targets, classifier_info=classifier_info, ann_type=ann_type) losses.update({'ot_loss': ot_loss}) elif self.use_ot == 'sinkhorn': ot_loss = self.box_predictor.align_sinkhorn_loss( predictions, proposals, targets, classifier_info=classifier_info, ann_type=ann_type) losses.update({'ot_loss': ot_loss}) assert 'loss_cls' not in losses assert 'loss_box_reg' not in losses losses['loss_cls'] = predictions[0].new_zeros([1])[0] losses['loss_box_reg'] = predictions[0].new_zeros([1])[0] else: losses = self.box_predictor.losses( (predictions[0], predictions[1]), proposals) if self.with_image_labels: if self.use_caption: assert 'caption_loss' not in losses losses['caption_loss'] = predictions[0].new_zeros([1])[0] if self.use_ot: assert 'ot_loss' not in losses losses['ot_loss'] = predictions[0].new_zeros([1])[0] if self.save_debug: denormalizer = lambda x: x * self.pixel_std + self.pixel_mean if ann_type != 'box': image_labels = [x._pos_category_ids for x in targets] else: image_labels = [[] for x in targets] debug_second_stage( [denormalizer(x.clone()) for x in images], targets, proposals=proposals, save_debug=self.save_debug, debug_show_name=self.debug_show_name, vis_thresh=self.vis_thresh, image_labels=image_labels, save_debug_path=self.save_debug_path, bgr=self.bgr) return proposals, losses else: pred_instances, _ = self.box_predictor.inference(predictions, proposals) pred_instances = self.forward_with_given_boxes(features, pred_instances) if self.save_debug: denormalizer = lambda x: x * self.pixel_std + self.pixel_mean debug_second_stage( [denormalizer(x.clone()) for x in images], pred_instances, proposals=proposals, save_debug=self.save_debug, debug_show_name=self.debug_show_name, vis_thresh=self.vis_thresh, save_debug_path=self.save_debug_path, bgr=self.bgr) return pred_instances, {} def get_top_proposals(self, proposals): for i in range(len(proposals)): proposals[i].proposal_boxes.clip(proposals[i].image_size) proposals = [p[:self.ws_num_props] for p in proposals] for i, p in enumerate(proposals): p.proposal_boxes.tensor = p.proposal_boxes.tensor.detach() if self.add_image_box: proposals[i] = self._add_image_box(p) return proposals def _add_image_box(self, p, use_score=False): image_box = Instances(p.image_size) n = 1 h, w = p.image_size if self.image_box_size < 1.0: f = self.image_box_size image_box.proposal_boxes = Boxes( p.proposal_boxes.tensor.new_tensor( [w * (1. - f) / 2., h * (1. - f) / 2., w * (1. - (1. - f) / 2.), h * (1. - (1. - f) / 2.)] ).view(n, 4)) else: image_box.proposal_boxes = Boxes( p.proposal_boxes.tensor.new_tensor( [0, 0, w, h]).view(n, 4)) if use_score: image_box.scores = \ p.objectness_logits.new_ones(n) image_box.pred_classes = \ p.objectness_logits.new_zeros(n, dtype=torch.long) image_box.objectness_logits = \ p.objectness_logits.new_ones(n) else: image_box.objectness_logits = \ p.objectness_logits.new_ones(n) return Instances.cat([p, image_box]) ================================================ FILE: vldet/modeling/roi_heads/vldet_fast_rcnn.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import logging import math import json import numpy as np from typing import Dict, Union import torch from fvcore.nn import giou_loss, smooth_l1_loss from torch import nn from torch.nn import functional as F import fvcore.nn.weight_init as weight_init import detectron2.utils.comm as comm from detectron2.config import configurable from detectron2.layers import ShapeSpec, batched_nms, cat, cross_entropy, nonzero_tuple from detectron2.structures import Boxes, Instances from detectron2.utils.events import get_event_storage from detectron2.modeling.box_regression import Box2BoxTransform from detectron2.modeling.roi_heads.fast_rcnn import FastRCNNOutputLayers from detectron2.modeling.roi_heads.fast_rcnn import fast_rcnn_inference from detectron2.modeling.roi_heads.fast_rcnn import _log_classification_stats from scipy.optimize import linear_sum_assignment from torch.cuda.amp import autocast from ..utils import load_class_freq, get_fed_loss_inds, load_ot_weight_mask from .zero_shot_classifier import ZeroShotClassifier, SinkhornDistance __all__ = ["VLDetFastRCNNOutputLayers"] class VLDetFastRCNNOutputLayers(FastRCNNOutputLayers): @configurable def __init__( self, input_shape: ShapeSpec, *, mult_proposal_score=False, cls_score=None, sync_caption_batch = False, use_sigmoid_ce = False, use_fed_loss = False, ignore_zero_cats = False, fed_loss_num_cat = 50, dynamic_classifier = False, image_label_loss = '', use_zeroshot_cls = False, image_loss_weight = 0.1, with_softmax_prop = False, caption_weight = 1.0, neg_cap_weight = 1.0, ot_loss_weight = 1.0, add_image_box = False, debug = False, prior_prob = 0.01, cat_freq_path = '', fed_loss_freq_weight = 0.5, softmax_weak_loss = False, ws_num_props = 32, **kwargs, ): super().__init__( input_shape=input_shape, **kwargs, ) self.mult_proposal_score = mult_proposal_score self.sync_caption_batch = sync_caption_batch self.use_sigmoid_ce = use_sigmoid_ce self.use_fed_loss = use_fed_loss self.ignore_zero_cats = ignore_zero_cats self.fed_loss_num_cat = fed_loss_num_cat self.dynamic_classifier = dynamic_classifier self.image_label_loss = image_label_loss self.use_zeroshot_cls = use_zeroshot_cls self.image_loss_weight = image_loss_weight self.with_softmax_prop = with_softmax_prop self.caption_weight = caption_weight self.neg_cap_weight = neg_cap_weight self.ot_loss_weight = ot_loss_weight self.add_image_box = add_image_box self.softmax_weak_loss = softmax_weak_loss self.debug = debug self.ws_num_props = ws_num_props if softmax_weak_loss: assert image_label_loss in ['max_size'] if self.use_sigmoid_ce: bias_value = -math.log((1 - prior_prob) / prior_prob) nn.init.constant_(self.cls_score.bias, bias_value) if self.use_fed_loss or self.ignore_zero_cats: freq_weight = load_class_freq(cat_freq_path, fed_loss_freq_weight) self.register_buffer('freq_weight', freq_weight) else: self.freq_weight = None if self.use_fed_loss and len(self.freq_weight) < self.num_classes: print('Extending federated loss weight') self.freq_weight = torch.cat( [self.freq_weight, self.freq_weight.new_zeros( self.num_classes - len(self.freq_weight))] ) assert (not self.dynamic_classifier) or (not self.use_fed_loss) input_size = input_shape.channels * \ (input_shape.width or 1) * (input_shape.height or 1) if self.use_zeroshot_cls: del self.cls_score del self.bbox_pred assert cls_score is not None self.cls_score = cls_score self.bbox_pred = nn.Sequential( nn.Linear(input_size, input_size), nn.ReLU(inplace=True), nn.Linear(input_size, 4) ) weight_init.c2_xavier_fill(self.bbox_pred[0]) nn.init.normal_(self.bbox_pred[-1].weight, std=0.001) nn.init.constant_(self.bbox_pred[-1].bias, 0) if self.with_softmax_prop: self.prop_score = nn.Sequential( nn.Linear(input_size, input_size), nn.ReLU(inplace=True), nn.Linear(input_size, self.num_classes + 1), ) weight_init.c2_xavier_fill(self.prop_score[0]) nn.init.normal_(self.prop_score[-1].weight, mean=0, std=0.001) nn.init.constant_(self.prop_score[-1].bias, 0) self.sinkhorn = SinkhornDistance(eps = 1e-3, max_iter=100) @classmethod def from_config(cls, cfg, input_shape): ret = super().from_config(cfg, input_shape) ret.update({ 'mult_proposal_score': cfg.MODEL.ROI_BOX_HEAD.MULT_PROPOSAL_SCORE, 'sync_caption_batch': cfg.MODEL.SYNC_CAPTION_BATCH, 'use_sigmoid_ce': cfg.MODEL.ROI_BOX_HEAD.USE_SIGMOID_CE, 'use_fed_loss': cfg.MODEL.ROI_BOX_HEAD.USE_FED_LOSS, 'ignore_zero_cats': cfg.MODEL.ROI_BOX_HEAD.IGNORE_ZERO_CATS, 'fed_loss_num_cat': cfg.MODEL.ROI_BOX_HEAD.FED_LOSS_NUM_CAT, 'dynamic_classifier': cfg.MODEL.DYNAMIC_CLASSIFIER, 'image_label_loss': cfg.MODEL.ROI_BOX_HEAD.IMAGE_LABEL_LOSS, 'use_zeroshot_cls': cfg.MODEL.ROI_BOX_HEAD.USE_ZEROSHOT_CLS, 'image_loss_weight': cfg.MODEL.ROI_BOX_HEAD.IMAGE_LOSS_WEIGHT, 'with_softmax_prop': cfg.MODEL.ROI_BOX_HEAD.WITH_SOFTMAX_PROP, 'caption_weight': cfg.MODEL.ROI_BOX_HEAD.CAPTION_WEIGHT, 'neg_cap_weight': cfg.MODEL.ROI_BOX_HEAD.NEG_CAP_WEIGHT, 'add_image_box': cfg.MODEL.ROI_BOX_HEAD.ADD_IMAGE_BOX, 'debug': cfg.DEBUG or cfg.SAVE_DEBUG or cfg.IS_DEBUG, 'prior_prob': cfg.MODEL.ROI_BOX_HEAD.PRIOR_PROB, 'cat_freq_path': cfg.MODEL.ROI_BOX_HEAD.CAT_FREQ_PATH, 'ot_loss_weight': cfg.MODEL.ROI_BOX_HEAD.OT_LOSS_WEIGHT, 'fed_loss_freq_weight': cfg.MODEL.ROI_BOX_HEAD.FED_LOSS_FREQ_WEIGHT, 'softmax_weak_loss': cfg.MODEL.ROI_BOX_HEAD.SOFTMAX_WEAK_LOSS, 'ws_num_props': cfg.MODEL.ROI_BOX_HEAD.WS_NUM_PROPS, }) if ret['use_zeroshot_cls']: ret['cls_score'] = ZeroShotClassifier(cfg, input_shape) return ret def losses(self, predictions, proposals, \ use_advanced_loss=True, classifier_info=(None,None,None)): """ enable advanced loss """ scores, proposal_deltas = predictions gt_classes = ( cat([p.gt_classes for p in proposals], dim=0) if len(proposals) else torch.empty(0) ) num_classes = self.num_classes if self.dynamic_classifier: _, cls_id_map = classifier_info[1] gt_classes = cls_id_map[gt_classes] num_classes = scores.shape[1] - 1 assert cls_id_map[self.num_classes] == num_classes _log_classification_stats(scores, gt_classes) if len(proposals): proposal_boxes = cat([p.proposal_boxes.tensor for p in proposals], dim=0) # Nx4 assert not proposal_boxes.requires_grad, "Proposals should not require gradients!" gt_boxes = cat( [(p.gt_boxes if p.has("gt_boxes") else p.proposal_boxes).tensor for p in proposals], dim=0, ) else: proposal_boxes = gt_boxes = torch.empty((0, 4), device=proposal_deltas.device) if self.use_sigmoid_ce: loss_cls = self.sigmoid_cross_entropy_loss(scores, gt_classes) else: loss_cls = self.softmax_cross_entropy_loss(scores, gt_classes) return { "loss_cls": loss_cls, "loss_box_reg": self.box_reg_loss( proposal_boxes, gt_boxes, proposal_deltas, gt_classes, num_classes=num_classes) } def sigmoid_cross_entropy_loss(self, pred_class_logits, gt_classes): if pred_class_logits.numel() == 0: return pred_class_logits.new_zeros([1])[0] # This is more robust than .sum() * 0. B = pred_class_logits.shape[0] C = pred_class_logits.shape[1] - 1 target = pred_class_logits.new_zeros(B, C + 1) target[range(len(gt_classes)), gt_classes] = 1 # B x (C + 1) target = target[:, :C] # B x C weight = 1 if self.use_fed_loss and (self.freq_weight is not None): # fedloss appeared = get_fed_loss_inds( gt_classes, num_sample_cats=self.fed_loss_num_cat, C=C, weight=self.freq_weight) appeared_mask = appeared.new_zeros(C + 1) appeared_mask[appeared] = 1 # C + 1 appeared_mask = appeared_mask[:C] fed_w = appeared_mask.view(1, C).expand(B, C) weight = weight * fed_w.float() if self.ignore_zero_cats and (self.freq_weight is not None): w = (self.freq_weight.view(-1) > 1e-4).float() weight = weight * w.view(1, C).expand(B, C) cls_loss = F.binary_cross_entropy_with_logits( pred_class_logits[:, :-1], target, reduction='none') # B x C loss = torch.sum(cls_loss * weight) / B return loss def softmax_cross_entropy_loss(self, pred_class_logits, gt_classes): """ change _no_instance handling """ if pred_class_logits.numel() == 0: return pred_class_logits.new_zeros([1])[0] if self.ignore_zero_cats and (self.freq_weight is not None): zero_weight = torch.cat([ (self.freq_weight.view(-1) > 1e-4).float(), self.freq_weight.new_ones(1)]) # C + 1 loss = F.cross_entropy( pred_class_logits, gt_classes, weight=zero_weight, reduction="mean") elif self.use_fed_loss and (self.freq_weight is not None): # fedloss C = pred_class_logits.shape[1] - 1 appeared = get_fed_loss_inds( gt_classes, num_sample_cats=self.fed_loss_num_cat, C=C, weight=self.freq_weight) appeared_mask = appeared.new_zeros(C + 1).float() appeared_mask[appeared] = 1. # C + 1 appeared_mask[C] = 1. loss = F.cross_entropy( pred_class_logits, gt_classes, weight=appeared_mask, reduction="mean") else: loss = F.cross_entropy( pred_class_logits, gt_classes, reduction="mean") return loss def box_reg_loss( self, proposal_boxes, gt_boxes, pred_deltas, gt_classes, num_classes=-1): """ Allow custom background index """ num_classes = num_classes if num_classes > 0 else self.num_classes box_dim = proposal_boxes.shape[1] # 4 or 5 fg_inds = nonzero_tuple((gt_classes >= 0) & (gt_classes < num_classes))[0] if pred_deltas.shape[1] == box_dim: # cls-agnostic regression fg_pred_deltas = pred_deltas[fg_inds] else: fg_pred_deltas = pred_deltas.view(-1, self.num_classes, box_dim)[ fg_inds, gt_classes[fg_inds] ] if self.box_reg_loss_type == "smooth_l1": gt_pred_deltas = self.box2box_transform.get_deltas( proposal_boxes[fg_inds], gt_boxes[fg_inds], ) loss_box_reg = smooth_l1_loss( fg_pred_deltas, gt_pred_deltas, self.smooth_l1_beta, reduction="sum" ) elif self.box_reg_loss_type == "giou": fg_pred_boxes = self.box2box_transform.apply_deltas( fg_pred_deltas, proposal_boxes[fg_inds] ) loss_box_reg = giou_loss(fg_pred_boxes, gt_boxes[fg_inds], reduction="sum") else: raise ValueError(f"Invalid bbox reg loss type '{self.box_reg_loss_type}'") return loss_box_reg / max(gt_classes.numel(), 1.0) def inference(self, predictions, proposals): """ enable use proposal boxes """ predictions = (predictions[0], predictions[1]) boxes = self.predict_boxes(predictions, proposals) scores = self.predict_probs(predictions, proposals) if self.mult_proposal_score: proposal_scores = [p.get('objectness_logits') for p in proposals] scores = [(s * ps[:, None]) ** 0.5 \ for s, ps in zip(scores, proposal_scores)] image_shapes = [x.image_size for x in proposals] return fast_rcnn_inference( boxes, scores, image_shapes, self.test_score_thresh, self.test_nms_thresh, self.test_topk_per_image, ) def predict_probs(self, predictions, proposals): """ support sigmoid """ # scores, _ = predictions scores = predictions[0] num_inst_per_image = [len(p) for p in proposals] if self.use_sigmoid_ce: probs = scores.sigmoid() else: probs = F.softmax(scores, dim=-1) return probs.split(num_inst_per_image, dim=0) def align_sinkhorn_loss(self, predictions, proposals, targets, classifier_info=(None,None,None), ann_type='image'): scores, proposal_deltas, proj_region, zs_weight = predictions batch_size = len(proposals) num_inst_per_image = [len(p) for p in proposals] proj_region = proj_region.split(num_inst_per_image, dim=0) loss = proj_region[0].new_zeros([1])[0] #(proj_region[0][:,0]).repeat((zs_weight.size()[1]-1,1)) for ii, (proj_r, target) in enumerate(zip(proj_region, targets)): proj_r = proj_r[:-1,:].permute(1,0) target_ids = target._pos_category_ids similarity_for_img = torch.mm(zs_weight.permute(1,0), proj_r) ot_similarity = torch.zeros(int(len(target_ids))+1, proj_r.size()[1]).to(zs_weight.device) for i, ind in enumerate(target_ids): ot_similarity[i] = similarity_for_img[ind] ot_similarity[-1] = similarity_for_img[-1] distance_for_ot = -ot_similarity mu = zs_weight.new_ones(int(len(target_ids)+1)) nu = zs_weight.new_ones(self.ws_num_props) _, pi = self.sinkhorn(mu, nu, distance_for_ot) rescale_factor, _ = pi.max(dim=1) pi = pi / rescale_factor.unsqueeze(1) max_assigned_units, matched_gt_inds = torch.max(pi, dim=0) fg_mask = matched_gt_inds != int(len(target_ids)) tag_gt_cate = target._pos_category_ids tag_gt_cate.append(len(zs_weight[0])-1) matched_gt_inds = [tag_gt_cate[i] for i in matched_gt_inds] target_for_ot = torch.zeros_like(similarity_for_img) y_s = torch.tensor(range(0,self.ws_num_props)).to(zs_weight.device) target_for_ot[matched_gt_inds,y_s] = 1. for xx_s, yy_s in zip(matched_gt_inds, y_s): match_num = 0 word_features = torch.zeros_like(zs_weight) proposal_feature = torch.zeros_like(proj_r[:, 0]) proposal_feature = proj_r[:, yy_s] word_features[:, match_num] = zs_weight[:, xx_s] match_num = match_num + 1 for jj in range(len(targets)): if jj == ii: continue other_concepts = targets[jj] neg_concept_list = [other_concept_id for other_concept_id in other_concepts._pos_category_ids \ if other_concept_id not in targets[ii]._pos_category_ids] word_features[:, match_num:len(neg_concept_list)+match_num] = zs_weight[:, neg_concept_list] match_num = match_num + len(neg_concept_list) optimize_matrix = word_features[:,:match_num].permute(1,0) @ proposal_feature target_matrix = torch.zeros(match_num).to(zs_weight.device) target_matrix[0] = 1 loss += torch.sum(F.binary_cross_entropy_with_logits(optimize_matrix, target_matrix, reduction='none')) return loss/batch_size * self.ot_loss_weight def align_contrastive_loss(self, predictions, proposals, targets, classifier_info=(None,None,None), ann_type='image'): scores, proposal_deltas, proj_region, zs_weight = predictions batch_size = len(proposals) num_inst_per_image = [len(p) for p in proposals] proj_region = proj_region.split(num_inst_per_image, dim=0) loss = proj_region[0].new_zeros([1])[0] #(proj_region[0][:,0]).repeat((zs_weight.size()[1]-1,1)) for ii, (proj_r, target) in enumerate(zip(proj_region, targets)): proj_r = proj_r[:-1,:].permute(1,0) target_ids = target._pos_category_ids similarity_for_img = torch.mm(zs_weight.permute(1,0), proj_r) ss = similarity_for_img[:-1,:] norm_similarity_for_img = ss-ss.min() ot_similarity = torch.zeros(int(len(target_ids)), proj_r.size()[1]).to(zs_weight.device) for i, ind in enumerate(target_ids): ot_similarity[i] = norm_similarity_for_img[ind] distance_for_ot = -ot_similarity x_s, y_s = linear_sum_assignment(distance_for_ot.detach().cpu()) x_s = [target_ids[i] for i in x_s] neg_concept_list=[] for other_concepts in targets[:ii]+targets[ii+1:]: neg_concept_list = neg_concept_list + [other_concept_id for other_concept_id in other_concepts._pos_category_ids \ if other_concept_id not in targets[ii]._pos_category_ids] for xx_s, yy_s in zip(x_s, y_s): match_num = 0 proposal_feature = torch.zeros_like(proj_r[:,0]) word_features = torch.zeros_like(zs_weight) proposal_feature = proj_r[:, yy_s] word_features[:, match_num] = zs_weight[:, xx_s] match_num = match_num + 1 word_features[:, match_num:len(neg_concept_list)+match_num] = zs_weight[:, neg_concept_list] match_num = match_num + len(neg_concept_list) optimize_matrix = word_features[:,:match_num].permute(1,0) @ proposal_feature target_matrix = torch.zeros_like(optimize_matrix).to(zs_weight.device) target_matrix[0] = 1 loss += torch.sum(F.binary_cross_entropy_with_logits(optimize_matrix, target_matrix, reduction='none')) return loss/batch_size * self.ot_loss_weight def align_BCE_loss(self, predictions, proposals, targets, classifier_info=(None,None,None), ann_type='image'): scores, proposal_deltas, proj_region, zs_weight = predictions batch_size = len(proposals) num_inst_per_image = [len(p) for p in proposals] proj_region = proj_region.split(num_inst_per_image, dim=0) loss = torch.zeros_like(proj_region[0][:-1,0]).repeat((zs_weight.size()[1]-1,1)) for ii, (proj_r, target) in enumerate(zip(proj_region, targets)): proj_r = proj_r[:-1,:].permute(1,0) similarity_for_img = torch.mm(zs_weight.permute(1,0), proj_r) ss = similarity_for_img[:-1,:] norm_similarity_for_img = ss-ss.min() ot_similarity = torch.zeros(int(len(target._pos_category_ids)), proj_r.size()[1]).to(zs_weight.device) for i, ind in enumerate(target._pos_category_ids): ot_similarity[i] = norm_similarity_for_img[ind] distance_for_ot = -ot_similarity x_s, y_s = linear_sum_assignment(distance_for_ot.detach().cpu()) x_s = [target._pos_category_ids[i] for i in x_s]#[np.ndarray.tolist(x_s)] target = torch.zeros_like(similarity_for_img) target[x_s,y_s] = 1. loss += F.binary_cross_entropy_with_logits(similarity_for_img[:-1], target[:-1], reduction='none') return torch.sum(loss)/batch_size * self.ot_loss_weight def image_label_losses(self, predictions, proposals, image_labels, \ classifier_info=(None,None,None), ann_type='image'): ''' Inputs: scores: N x (C + 1) image_labels B x 1 ''' num_inst_per_image = [len(p) for p in proposals] scores = predictions[0] scores = scores.split(num_inst_per_image, dim=0) # B x n x (C + 1) if self.with_softmax_prop: prop_scores = predictions[2].split(num_inst_per_image, dim=0) else: prop_scores = [None for _ in num_inst_per_image] B = len(scores) img_box_count = 0 select_size_count = 0 select_x_count = 0 select_y_count = 0 max_score_count = 0 storage = get_event_storage() loss = scores[0].new_zeros([1])[0] caption_loss = scores[0].new_zeros([1])[0] for idx, (score, labels, prop_score, p) in enumerate(zip( scores, image_labels, prop_scores, proposals)): if score.shape[0] == 0: loss += score.new_zeros([1])[0] continue if 'caption' in ann_type: score, caption_loss_img = self._caption_loss( score, classifier_info, idx, B) caption_loss += self.caption_weight * caption_loss_img if ann_type == 'caption': continue if self.debug: p.selected = score.new_zeros( (len(p),), dtype=torch.long) - 1 neg_concept_list = [] for neg_label in image_labels[:idx]+image_labels[idx+1:]: other_concepts = neg_label neg_concept_list += [other_concept_id for other_concept_id in other_concepts \ if other_concept_id not in labels] for i_l, label in enumerate(labels): if self.dynamic_classifier: if idx == 0 and i_l == 0 and comm.is_main_process(): storage.put_scalar('stats_label', label) label = classifier_info[1][1][label] assert label < score.shape[1] if self.image_label_loss in ['wsod', 'wsddn']: loss_i, ind = self._wsddn_loss(score, prop_score, label) elif self.image_label_loss == 'max_score': loss_i, ind = self._max_score_loss(score, label) elif self.image_label_loss == 'max_size': loss_i, ind = self._max_size_loss(score, label, neg_concept_list, p) elif self.image_label_loss == 'first': loss_i, ind = self._first_loss(score, label) elif self.image_label_loss == 'image': loss_i, ind = self._image_loss(score, label) elif self.image_label_loss == 'min_loss': loss_i, ind = self._min_loss_loss(score, label) else: assert 0 loss += loss_i / len(labels) if type(ind) == type([]): img_box_count = sum(ind) / len(ind) if self.debug: for ind_i in ind: p.selected[ind_i] = label else: img_box_count = ind select_size_count = p[ind].proposal_boxes.area() / \ (p.image_size[0] * p.image_size[1]) max_score_count = score[ind, label].sigmoid() select_x_count = (p.proposal_boxes.tensor[ind, 0] + \ p.proposal_boxes.tensor[ind, 2]) / 2 / p.image_size[1] select_y_count = (p.proposal_boxes.tensor[ind, 1] + \ p.proposal_boxes.tensor[ind, 3]) / 2 / p.image_size[0] if self.debug: p.selected[ind] = label loss = loss / B storage.put_scalar('stats_l_image', loss.item()) if 'caption' in ann_type: caption_loss = caption_loss / B loss = loss + caption_loss storage.put_scalar('stats_l_caption', caption_loss.item()) if comm.is_main_process(): storage.put_scalar('pool_stats', img_box_count) storage.put_scalar('stats_select_size', select_size_count) storage.put_scalar('stats_select_x', select_x_count) storage.put_scalar('stats_select_y', select_y_count) storage.put_scalar('stats_max_label_score', max_score_count) return loss * self.image_loss_weight def forward(self, x, ann_type='box', classifier_info=(None,None,None)): """ enable classifier_info """ if x.dim() > 2: x = torch.flatten(x, start_dim=1) scores = [] proj_region = None zs_weight = None if classifier_info[0] is not None: cls_scores, proj_region, zs_weight = self.cls_score(x, ann_type ,classifier=classifier_info[0]) scores.append(cls_scores) else: cls_scores, proj_region, zs_weight = self.cls_score(x, ann_type) scores.append(cls_scores) if classifier_info[2] is not None: cap_cls = classifier_info[2] if self.sync_caption_batch: caption_scores, _, _ = self.cls_score(x, classifier=cap_cls[:, :-1]) else: caption_scores, _, _ = self.cls_score(x, classifier=cap_cls) scores.append(caption_scores) scores = torch.cat(scores, dim=1) # B x C' or B x N or B x (C'+N) proposal_deltas = self.bbox_pred(x) if self.with_softmax_prop: prop_score = self.prop_score(x) return scores, proposal_deltas, prop_score else: return scores, proposal_deltas, proj_region, zs_weight def _caption_loss(self, score, classifier_info, idx, B): assert (classifier_info[2] is not None) assert self.add_image_box cls_and_cap_num = score.shape[1] cap_num = classifier_info[2].shape[0] score, caption_score = score.split( [cls_and_cap_num - cap_num, cap_num], dim=1) # n x (C + 1), n x B caption_score = caption_score[-1:] # 1 x B # -1: image level box caption_target = caption_score.new_zeros( caption_score.shape) # 1 x B or 1 x MB, M: num machines if self.sync_caption_batch: # caption_target: 1 x MB rank = comm.get_rank() global_idx = B * rank + idx assert (classifier_info[2][ global_idx, -1] - rank) ** 2 < 1e-8, \ '{} {} {} {} {}'.format( rank, global_idx, classifier_info[2][global_idx, -1], classifier_info[2].shape, classifier_info[2][:, -1]) caption_target[:, global_idx] = 1. else: assert caption_score.shape[1] == B caption_target[:, idx] = 1. caption_loss_img = F.binary_cross_entropy_with_logits( caption_score, caption_target, reduction='none') if self.sync_caption_batch: fg_mask = (caption_target > 0.5).float() assert (fg_mask.sum().item() - 1.) ** 2 < 1e-8, '{} {}'.format( fg_mask.shape, fg_mask) pos_loss = (caption_loss_img * fg_mask).sum() neg_loss = (caption_loss_img * (1. - fg_mask)).sum() caption_loss_img = pos_loss + self.neg_cap_weight * neg_loss else: caption_loss_img = caption_loss_img.sum() return score, caption_loss_img def _wsddn_loss(self, score, prop_score, label): assert prop_score is not None loss = 0 final_score = score.sigmoid() * \ F.softmax(prop_score, dim=0) # B x (C + 1) img_score = torch.clamp( torch.sum(final_score, dim=0), min=1e-10, max=1-1e-10) # (C + 1) target = img_score.new_zeros(img_score.shape) # (C + 1) target[label] = 1. loss += F.binary_cross_entropy(img_score, target) ind = final_score[:, label].argmax() return loss, ind def _max_score_loss(self, score, label): loss = 0 target = score.new_zeros(score.shape[1]) target[label] = 1. ind = score[:, label].argmax().item() loss += F.binary_cross_entropy_with_logits( score[ind], target, reduction='sum') return loss, ind def _min_loss_loss(self, score, label): loss = 0 target = score.new_zeros(score.shape) target[:, label] = 1. with torch.no_grad(): x = F.binary_cross_entropy_with_logits( score, target, reduction='none').sum(dim=1) # n ind = x.argmin().item() loss += F.binary_cross_entropy_with_logits( score[ind], target[0], reduction='sum') return loss, ind def _first_loss(self, score, label): loss = 0 target = score.new_zeros(score.shape[1]) target[label] = 1. ind = 0 loss += F.binary_cross_entropy_with_logits( score[ind], target, reduction='sum') return loss, ind def _image_loss(self, score, label): assert self.add_image_box target = score.new_zeros(score.shape[1]) target[label] = 1. ind = score.shape[0] - 1 loss = F.binary_cross_entropy_with_logits( score[ind], target, reduction='sum') return loss, ind def _max_size_loss(self, score, label, neg_concept_list, p): loss = 0 target = score.new_zeros(score.shape[1]) target[label] = 1. sizes = p.proposal_boxes.area() ind = sizes[:-1].argmax().item() if len(sizes) > 1 else 0 if self.softmax_weak_loss: loss += F.cross_entropy( score[ind:ind+1], score.new_tensor(label, dtype=torch.long).view(1), reduction='sum') else: loss += F.binary_cross_entropy_with_logits( score[ind][[label]+neg_concept_list], target[[label]+neg_concept_list], reduction='sum') #81 category return loss, ind def put_label_distribution(storage, hist_name, hist_counts, num_classes): """ """ ht_min, ht_max = 0, num_classes hist_edges = torch.linspace( start=ht_min, end=ht_max, steps=num_classes + 1, dtype=torch.float32) hist_params = dict( tag=hist_name, min=ht_min, max=ht_max, num=float(hist_counts.sum()), sum=float((hist_counts * torch.arange(len(hist_counts))).sum()), sum_squares=float(((hist_counts * torch.arange(len(hist_counts))) ** 2).sum()), bucket_limits=hist_edges[1:].tolist(), bucket_counts=hist_counts.tolist(), global_step=storage._iter, ) storage._histograms.append(hist_params) ================================================ FILE: vldet/modeling/roi_heads/vldet_roi_heads.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import copy import numpy as np import json import math import torch from torch import nn from torch.autograd.function import Function from typing import Dict, List, Optional, Tuple, Union from torch.nn import functional as F from detectron2.config import configurable from detectron2.layers import ShapeSpec from detectron2.layers import batched_nms from detectron2.structures import Boxes, Instances, pairwise_iou from detectron2.utils.events import get_event_storage from detectron2.modeling.box_regression import Box2BoxTransform from detectron2.modeling.roi_heads.fast_rcnn import fast_rcnn_inference from detectron2.modeling.roi_heads.roi_heads import ROI_HEADS_REGISTRY, StandardROIHeads from detectron2.modeling.roi_heads.cascade_rcnn import CascadeROIHeads, _ScaleGradient from detectron2.modeling.roi_heads.box_head import build_box_head from .vldet_fast_rcnn import VLDetFastRCNNOutputLayers from ..debug import debug_second_stage from torch.cuda.amp import autocast @ROI_HEADS_REGISTRY.register() class DeticCascadeROIHeads(CascadeROIHeads): @configurable def __init__( self, *, mult_proposal_score: bool = False, with_image_labels: bool = False, add_image_box: bool = False, image_box_size: float = 1.0, ws_num_props: int = 512, add_feature_to_prop: bool = False, mask_weight: float = 1.0, one_class_per_proposal: bool = False, use_ot: str = 'contrastive', use_caption: bool = False, **kwargs, ): super().__init__(**kwargs) self.mult_proposal_score = mult_proposal_score self.with_image_labels = with_image_labels self.add_image_box = add_image_box self.image_box_size = image_box_size self.ws_num_props = ws_num_props self.add_feature_to_prop = add_feature_to_prop self.mask_weight = mask_weight self.one_class_per_proposal = one_class_per_proposal self.use_ot = use_ot self.use_caption = use_caption @classmethod def from_config(cls, cfg, input_shape): ret = super().from_config(cfg, input_shape) ret.update({ 'mult_proposal_score': cfg.MODEL.ROI_BOX_HEAD.MULT_PROPOSAL_SCORE, 'with_image_labels': cfg.WITH_IMAGE_LABELS, 'add_image_box': cfg.MODEL.ROI_BOX_HEAD.ADD_IMAGE_BOX, 'image_box_size': cfg.MODEL.ROI_BOX_HEAD.IMAGE_BOX_SIZE, 'ws_num_props': cfg.MODEL.ROI_BOX_HEAD.WS_NUM_PROPS, 'add_feature_to_prop': cfg.MODEL.ROI_BOX_HEAD.ADD_FEATURE_TO_PROP, 'mask_weight': cfg.MODEL.ROI_HEADS.MASK_WEIGHT, 'one_class_per_proposal': cfg.MODEL.ROI_HEADS.ONE_CLASS_PER_PROPOSAL, 'use_ot':cfg.MODEL.ROI_BOX_HEAD.USE_OT, 'use_caption': cfg.MODEL.ROI_BOX_HEAD.USE_CAPTION, }) return ret @classmethod def _init_box_head(self, cfg, input_shape): ret = super()._init_box_head(cfg, input_shape) del ret['box_predictors'] cascade_bbox_reg_weights = cfg.MODEL.ROI_BOX_CASCADE_HEAD.BBOX_REG_WEIGHTS box_predictors = [] for box_head, bbox_reg_weights in zip(ret['box_heads'], \ cascade_bbox_reg_weights): box_predictors.append( VLDetFastRCNNOutputLayers( cfg, box_head.output_shape, box2box_transform=Box2BoxTransform(weights=bbox_reg_weights) )) ret['box_predictors'] = box_predictors return ret def _forward_box(self, features, proposals, targets=None, ann_type='box', classifier_info=(None,None,None)): """ Add mult proposal scores at testing Add ann_type """ if (not self.training) and self.mult_proposal_score: if len(proposals) > 0 and proposals[0].has('scores'): proposal_scores = [p.get('scores') for p in proposals] else: proposal_scores = [p.get('objectness_logits') for p in proposals] features = [features[f] for f in self.box_in_features] head_outputs = [] # (predictor, predictions, proposals) prev_pred_boxes = None image_sizes = [x.image_size for x in proposals] for k in range(self.num_cascade_stages): if k > 0: proposals = self._create_proposals_from_boxes( prev_pred_boxes, image_sizes, logits=[p.objectness_logits for p in proposals], ann_type=ann_type) if self.training and ann_type in ['box']: proposals = self._match_and_label_boxes( proposals, k, targets) predictions = self._run_stage(features, proposals, k, ann_type=ann_type, classifier_info=classifier_info) prev_pred_boxes = self.box_predictor[k].predict_boxes( (predictions[0], predictions[1]), proposals) head_outputs.append((self.box_predictor[k], predictions, proposals)) if self.training: losses = {} storage = get_event_storage() for stage, (predictor, predictions, proposals) in enumerate(head_outputs): with storage.name_scope("stage{}".format(stage)): if ann_type != 'box': stage_losses = {} if ann_type in ['image', 'caption', 'captiontag']: image_labels = [x._pos_category_ids for x in targets] ######################### if self.use_caption: caption_losses = predictor.image_label_losses( predictions, proposals, image_labels, classifier_info=classifier_info, ann_type=ann_type) stage_losses['caption_loss'] = caption_losses ########################## if self.use_ot == 'BCE': ot_loss = predictor.align_BCE_loss( predictions, proposals, targets, classifier_info=classifier_info, ann_type=ann_type) stage_losses['ot_loss'] = ot_loss elif self.use_ot == 'contrastive': ot_loss = predictor.align_contrastive_loss( predictions, proposals, targets, classifier_info=classifier_info, ann_type=ann_type) stage_losses['ot_loss'] = ot_loss elif self.use_ot == 'sinkhorn': ot_loss = predictor.align_sinkhorn_loss( predictions, proposals, targets, classifier_info=classifier_info, ann_type=ann_type) stage_losses['ot_loss'] = ot_loss stage_losses['loss_cls'] = predictions[0].new_zeros([1])[0] stage_losses['loss_box_reg'] = predictions[0].new_zeros([1])[0] else: # supervised stage_losses = predictor.losses( (predictions[0], predictions[1]), proposals, classifier_info=classifier_info) if self.use_ot != '': stage_losses['ot_loss'] = \ predictions[0].new_zeros([1])[0] if self.use_caption: stage_losses['caption_loss'] = \ predictions[0].new_zeros([1])[0] losses.update({k + "_stage{}".format(stage): v \ for k, v in stage_losses.items()}) return losses else: # Each is a list[Tensor] of length #image. Each tensor is Ri x (K+1) scores_per_stage = [h[0].predict_probs(h[1], h[2]) for h in head_outputs] scores = [ sum(list(scores_per_image)) * (1.0 / self.num_cascade_stages) for scores_per_image in zip(*scores_per_stage) ] if self.mult_proposal_score: scores = [(s * ps[:, None]) ** 0.5 \ for s, ps in zip(scores, proposal_scores)] if self.one_class_per_proposal: scores = [s * (s == s[:, :-1].max(dim=1)[0][:, None]).float() for s in scores] predictor, predictions, proposals = head_outputs[-1] boxes = predictor.predict_boxes( (predictions[0], predictions[1]), proposals) pred_instances, _ = fast_rcnn_inference( boxes, scores, image_sizes, predictor.test_score_thresh, predictor.test_nms_thresh, predictor.test_topk_per_image, ) return pred_instances def forward(self, images, features, proposals, targets=None, ann_type='box', classifier_info=(None,None,None)): ''' enable debug and image labels classifier_info is shared across the batch ''' if self.training: if ann_type in ['box', 'prop', 'proptag']: proposals = self.label_and_sample_proposals( proposals, targets) else: proposals = self.get_top_proposals(proposals) losses = self._forward_box(features, proposals, targets, \ ann_type=ann_type, classifier_info=classifier_info) if ann_type == 'box' and targets[0].has('gt_masks'): mask_losses = self._forward_mask(features, proposals) losses.update({k: v * self.mask_weight \ for k, v in mask_losses.items()}) losses.update(self._forward_keypoint(features, proposals)) else: losses.update(self._get_empty_mask_loss( features, proposals, device=proposals[0].objectness_logits.device)) return proposals, losses else: pred_instances = self._forward_box( features, proposals, classifier_info=classifier_info) pred_instances = self.forward_with_given_boxes(features, pred_instances) return pred_instances, {} def get_top_proposals(self, proposals): for i in range(len(proposals)): proposals[i].proposal_boxes.clip(proposals[i].image_size) ########## inds = proposals[i].proposal_boxes.nonempty() if False in inds: proposals[i] = proposals[i][inds] ########## proposals = [p[:self.ws_num_props] for p in proposals] for i, p in enumerate(proposals): p.proposal_boxes.tensor = p.proposal_boxes.tensor.detach() if self.add_image_box: proposals[i] = self._add_image_box(p) return proposals def _add_image_box(self, p): image_box = Instances(p.image_size) n = 1 h, w = p.image_size f = self.image_box_size image_box.proposal_boxes = Boxes( p.proposal_boxes.tensor.new_tensor( [w * (1. - f) / 2., h * (1. - f) / 2., w * (1. - (1. - f) / 2.), h * (1. - (1. - f) / 2.)] ).view(n, 4)) image_box.objectness_logits = p.objectness_logits.new_ones(n) return Instances.cat([p, image_box]) def _get_empty_mask_loss(self, features, proposals, device): if self.mask_on: return {'loss_mask': torch.zeros( (1, ), device=device, dtype=torch.float32)[0]} else: return {} def _create_proposals_from_boxes(self, boxes, image_sizes, logits, ann_type): """ Add objectness_logits """ boxes = [Boxes(b.detach()) for b in boxes] proposals = [] for boxes_per_image, image_size, logit in zip( boxes, image_sizes, logits): boxes_per_image.clip(image_size) if self.training: inds = boxes_per_image.nonempty() boxes_per_image = boxes_per_image[inds] logit = logit[inds] prop = Instances(image_size) prop.proposal_boxes = boxes_per_image prop.objectness_logits = logit proposals.append(prop) return proposals def _run_stage(self, features, proposals, stage, \ ann_type='box', classifier_info=(None,None,None)): """ Support classifier_info and add_feature_to_prop """ if proposals == None: box_features = features box_features = _ScaleGradient.apply(box_features, 1.0 / self.num_cascade_stages) box_features = self.box_head[stage](box_features) #[[4096(8*512), 1024]] return box_features pool_boxes = [x.proposal_boxes for x in proposals] box_features = self.box_pooler(features, pool_boxes) #[4096(8*512), 256, 7, 7] box_features = _ScaleGradient.apply(box_features, 1.0 / self.num_cascade_stages) box_features = self.box_head[stage](box_features) #[[4096(8*512), 1024]] if self.add_feature_to_prop: feats_per_image = box_features.split( [len(p) for p in proposals], dim=0) for feat, p in zip(feats_per_image, proposals): p.feat = feat return self.box_predictor[stage]( box_features, ann_type, classifier_info=classifier_info) ================================================ FILE: vldet/modeling/roi_heads/zero_shot_classifier.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import numpy as np import torch from torch import nn from torch.nn import functional as F from detectron2.config import configurable from detectron2.layers import Linear, ShapeSpec class ZeroShotClassifier(nn.Module): @configurable def __init__( self, input_shape: ShapeSpec, *, num_classes: int, zs_weight_path: str, detection_weight_path: str, zs_weight_dim: int = 512, use_bias: float = 0.0, norm_weight: bool = True, norm_temperature: float = 50.0, share_proj_v_dim = 1024, share_proj_l_dim = 1024, ): super().__init__() if isinstance(input_shape, int): # some backward compatibility input_shape = ShapeSpec(channels=input_shape) input_size = input_shape.channels * (input_shape.width or 1) * (input_shape.height or 1) self.norm_weight = norm_weight self.norm_temperature = norm_temperature self.use_bias = use_bias < 0 if self.use_bias: self.cls_bias = nn.Parameter(torch.ones(1) * use_bias) self.linear = nn.Linear(share_proj_v_dim, share_proj_l_dim) #zs head if zs_weight_path == 'rand': zs_weight = torch.randn((zs_weight_dim, num_classes)) nn.init.normal_(zs_weight, std=0.01) else: zs_weight = torch.tensor( torch.load(zs_weight_path), dtype=torch.float32).permute(1, 0).contiguous() # D x C zs_weight = torch.cat( [zs_weight, zs_weight.new_zeros((zs_weight_dim, 1))], dim=1) # D x (C + 1) # detection head if detection_weight_path == 'rand': detection_weight = torch.randn((zs_weight_dim, num_classes)) nn.init.normal_(zs_weight, std=0.01) else: detection_weight = torch.tensor( torch.load(detection_weight_path), dtype=torch.float32).permute(1, 0).contiguous() # D x C detection_weight = torch.cat( [detection_weight, detection_weight.new_zeros((zs_weight_dim, 1))], dim=1) # D x (C + 1) if self.norm_weight: zs_weight = F.normalize(zs_weight, p=2, dim=0) detection_weight = F.normalize(detection_weight, p=2, dim=0) if zs_weight_path == 'rand': self.zs_weight = nn.Parameter(zs_weight) else: self.register_buffer('zs_weight', zs_weight) if detection_weight_path == 'rand': self.detection_weight = nn.Parameter(detection_weight) else: self.register_buffer('detection_weight', detection_weight) assert self.detection_weight.shape[1] == num_classes + 1, self.detection_weight.shape @classmethod def from_config(cls, cfg, input_shape): return { 'input_shape': input_shape, 'num_classes': cfg.MODEL.ROI_HEADS.NUM_CLASSES, 'zs_weight_path': cfg.MODEL.ROI_BOX_HEAD.ZEROSHOT_WEIGHT_PATH, 'detection_weight_path': cfg.MODEL.ROI_BOX_HEAD.DETECTION_WEIGHT_PATH, 'zs_weight_dim': cfg.MODEL.ROI_BOX_HEAD.ZEROSHOT_WEIGHT_DIM, 'use_bias': cfg.MODEL.ROI_BOX_HEAD.USE_BIAS, 'norm_weight': cfg.MODEL.ROI_BOX_HEAD.NORM_WEIGHT, 'norm_temperature': cfg.MODEL.ROI_BOX_HEAD.NORM_TEMP, 'share_proj_v_dim': cfg.MODEL.SHARE_PROJ_V_DIM, 'share_proj_l_dim': cfg.MODEL.SHARE_PROJ_L_DIM, } def forward(self, input_x, ann_type='box', classifier=None): ''' Inputs: x: B x D' classifier_info: (C', C' x D) ''' proj_x = self.linear(input_x) if classifier is not None: zs_weight = classifier.permute(1, 0).contiguous() # D x C' zs_weight = F.normalize(zs_weight, p=2, dim=0) \ if self.norm_weight else zs_weight else: if self.training and ann_type != 'box': zs_weight = self.zs_weight else: zs_weight = self.detection_weight if self.norm_weight: proj_x = self.norm_temperature * F.normalize(proj_x, p=2, dim=1) x = torch.mm(proj_x, zs_weight) if self.use_bias: x = x + self.cls_bias return x, proj_x, zs_weight class SinkhornDistance(torch.nn.Module): def __init__(self, eps=1e-3, max_iter=100, reduction='none'): super(SinkhornDistance, self).__init__() self.eps = eps self.max_iter = max_iter self.reduction = reduction def forward(self, mu, nu, C): u = torch.ones_like(mu) v = torch.ones_like(nu) # Sinkhorn iterations for i in range(self.max_iter): v = self.eps * \ (torch.log( nu + 1e-8) - torch.logsumexp(self.M(C, u, v).transpose(-2, -1), dim=-1)) + v u = self.eps * \ (torch.log( mu + 1e-8) - torch.logsumexp(self.M(C, u, v), dim=-1)) + u U, V = u, v # Transport plan pi = diag(a)*K*diag(b) pi = torch.exp( self.M(C, U, V)).detach() # Sinkhorn distance cost = torch.sum( pi * C, dim=(-2, -1)) return cost, pi def M(self, C, u, v): ''' "Modified cost for logarithmic updates" "$M_{ij} = (-c_{ij} + u_i + v_j) / epsilon$" ''' return (-C + u.unsqueeze(-1) + v.unsqueeze(-2)) / self.eps ================================================ FILE: vldet/modeling/text/text_encoder.py ================================================ # This code is modified from https://github.com/openai/CLIP/blob/main/clip/clip.py # Modified by Xingyi Zhou # The original code is under MIT license # Copyright (c) Facebook, Inc. and its affiliates. from typing import Union, List from collections import OrderedDict import torch from torch import nn import torch from clip.simple_tokenizer import SimpleTokenizer as _Tokenizer __all__ = ["tokenize"] count = 0 class LayerNorm(nn.LayerNorm): """Subclass torch's LayerNorm to handle fp16.""" def forward(self, x: torch.Tensor): orig_type = x.dtype ret = super().forward(x.type(torch.float32)) return ret.type(orig_type) class QuickGELU(nn.Module): def forward(self, x: torch.Tensor): return x * torch.sigmoid(1.702 * x) class ResidualAttentionBlock(nn.Module): def __init__(self, d_model: int, n_head: int, attn_mask: torch.Tensor = None): super().__init__() self.attn = nn.MultiheadAttention(d_model, n_head) self.ln_1 = LayerNorm(d_model) self.mlp = nn.Sequential(OrderedDict([ ("c_fc", nn.Linear(d_model, d_model * 4)), ("gelu", QuickGELU()), ("c_proj", nn.Linear(d_model * 4, d_model)) ])) self.ln_2 = LayerNorm(d_model) self.attn_mask = attn_mask def attention(self, x: torch.Tensor): self.attn_mask = self.attn_mask.to(dtype=x.dtype, device=x.device) if self.attn_mask is not None else None return self.attn(x, x, x, need_weights=False, attn_mask=self.attn_mask)[0] def forward(self, x: torch.Tensor): x = x + self.attention(self.ln_1(x)) x = x + self.mlp(self.ln_2(x)) return x class Transformer(nn.Module): def __init__(self, width: int, layers: int, heads: int, attn_mask: torch.Tensor = None): super().__init__() self.width = width self.layers = layers self.resblocks = nn.Sequential( *[ResidualAttentionBlock(width, heads, attn_mask) \ for _ in range(layers)]) def forward(self, x: torch.Tensor): return self.resblocks(x) class CLIPTEXT(nn.Module): def __init__(self, embed_dim=512, # text context_length=77, vocab_size=49408, transformer_width=512, transformer_heads=8, transformer_layers=12 ): super().__init__() self._tokenizer = _Tokenizer() self.context_length = context_length self.transformer = Transformer( width=transformer_width, layers=transformer_layers, heads=transformer_heads, attn_mask=self.build_attention_mask() ) self.vocab_size = vocab_size self.token_embedding = nn.Embedding(vocab_size, transformer_width) self.positional_embedding = nn.Parameter(torch.empty(self.context_length, transformer_width)) self.ln_final = LayerNorm(transformer_width) self.text_projection = nn.Parameter(torch.empty(transformer_width, embed_dim)) # self.logit_scale = nn.Parameter(torch.ones([]) * np.log(1 / 0.07)) self.initialize_parameters() def initialize_parameters(self): nn.init.normal_(self.token_embedding.weight, std=0.02) nn.init.normal_(self.positional_embedding, std=0.01) proj_std = (self.transformer.width ** -0.5) * ((2 * self.transformer.layers) ** -0.5) attn_std = self.transformer.width ** -0.5 fc_std = (2 * self.transformer.width) ** -0.5 for block in self.transformer.resblocks: nn.init.normal_(block.attn.in_proj_weight, std=attn_std) nn.init.normal_(block.attn.out_proj.weight, std=proj_std) nn.init.normal_(block.mlp.c_fc.weight, std=fc_std) nn.init.normal_(block.mlp.c_proj.weight, std=proj_std) if self.text_projection is not None: nn.init.normal_(self.text_projection, std=self.transformer.width ** -0.5) def build_attention_mask(self): # lazily create causal attention mask, with full attention between the vision tokens # pytorch uses additive attention mask; fill with -inf mask = torch.empty(self.context_length, self.context_length) mask.fill_(float("-inf")) mask.triu_(1) # zero out the lower diagonal return mask @property def device(self): return self.text_projection.device @property def dtype(self): return self.text_projection.dtype def tokenize(self, texts: Union[str, List[str]], \ context_length: int = 77) -> torch.LongTensor: """ """ if isinstance(texts, str): texts = [texts] sot_token = self._tokenizer.encoder["<|startoftext|>"] eot_token = self._tokenizer.encoder["<|endoftext|>"] all_tokens = [[sot_token] + self._tokenizer.encode(text) + [eot_token] for text in texts] result = torch.zeros(len(all_tokens), context_length, dtype=torch.long) for i, tokens in enumerate(all_tokens): if len(tokens) > context_length: st = torch.randint( len(tokens) - context_length + 1, (1,))[0].item() tokens = tokens[st: st + context_length] # raise RuntimeError(f"Input {texts[i]} is too long for context length {context_length}") result[i, :len(tokens)] = torch.tensor(tokens) return result def encode_text(self, text): x = self.token_embedding(text).type(self.dtype) # [batch_size, n_ctx, d_model] x = x + self.positional_embedding.type(self.dtype) x = x.permute(1, 0, 2) # NLD -> LND x = self.transformer(x) x = x.permute(1, 0, 2) # LND -> NLD x = self.ln_final(x).type(self.dtype) # take features from the eot embedding (eot_token is the highest number in each sequence) x = x[torch.arange(x.shape[0]), text.argmax(dim=-1)] @ self.text_projection return x def forward(self, captions): ''' captions: list of strings ''' text = self.tokenize(captions).to(self.device) # B x L x D features = self.encode_text(text) # B x D return features def build_text_encoder(pretrain=True, visual_type="ViT-B/32"): clip_dict = { "visual_type": ["embed_dim", "context_length", "vocab_size", "transformer_width", "transformer_heads", "transformer_layers"], "RN50": [1024, 77, 49408, 512, 8, 12], "ViT-B/32": [512, 77, 49408, 512, 8, 12], } text_encoder = CLIPTEXT(**{k: v for k, v in zip(clip_dict['visual_type'], clip_dict[visual_type])}) if pretrain: import clip if visual_type == 'RN50': pretrained_model, _ = clip.load("RN50", device='cpu') elif visual_type == 'ViT-B/32': pretrained_model, _ = clip.load("ViT-B/32", device='cpu') else: raise NotImplementedError state_dict = pretrained_model.state_dict() to_delete_keys = ["logit_scale", "input_resolution", \ "context_length", "vocab_size"] + \ [k for k in state_dict.keys() if k.startswith('visual.')] for k in to_delete_keys: if k in state_dict: del state_dict[k] print('Loading pretrained CLIP') text_encoder.load_state_dict(state_dict) return text_encoder class my_tokenizer(): def __init__(self): super().__init__() self._tokenizer = _Tokenizer() def tokenize(self, texts: Union[str, List[str]], \ context_length: int = 77) -> torch.LongTensor: """ """ if isinstance(texts, str): texts = [texts] sot_token = self._tokenizer.encoder["<|startoftext|>"] eot_token = self._tokenizer.encoder["<|endoftext|>"] all_tokens = [[sot_token] + self._tokenizer.encode(text) + [eot_token] for text in texts] result = torch.zeros(len(all_tokens), context_length, dtype=torch.long) for i, tokens in enumerate(all_tokens): if len(tokens) > context_length: st = torch.randint( len(tokens) - context_length + 1, (1,))[0].item() tokens = tokens[st: st + context_length] # raise RuntimeError(f"Input {texts[i]} is too long for context length {context_length}") result[i, :len(tokens)] = torch.tensor(tokens) return result def build_RN50_text_encoder(pretrain=True): if pretrain: import clip model, _ = clip.load('RN50', device='cpu') tokenizer = my_tokenizer() return model, tokenizer ================================================ FILE: vldet/modeling/utils.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import torch import json import numpy as np from torch.nn import functional as F from ..data.datasets import coco_zeroshot def load_ot_weight_mask(): weight_mask = coco_zeroshot._zero_shot_setting_weight_mask return weight_mask def load_class_freq( path='datasets/metadata/lvis_v1_train_cat_info.json', freq_weight=1.0): cat_info = json.load(open(path, 'r')) cat_info = torch.tensor( [c['image_count'] for c in sorted(cat_info, key=lambda x: x['id'])]) freq_weight = cat_info.float() ** freq_weight return freq_weight def get_fed_loss_inds(gt_classes, num_sample_cats, C, weight=None): appeared = torch.unique(gt_classes) # C' prob = appeared.new_ones(C + 1).float() prob[-1] = 0 if len(appeared) < num_sample_cats: if weight is not None: prob[:C] = weight.float().clone() prob[appeared] = 0 more_appeared = torch.multinomial( prob, num_sample_cats - len(appeared), replacement=False) appeared = torch.cat([appeared, more_appeared]) return appeared def reset_cls_test(model, cls_path, num_classes): model.roi_heads.num_classes = num_classes if type(cls_path) == str: print('Resetting zs_weight', cls_path) zs_weight = torch.tensor( np.load(cls_path), dtype=torch.float32).permute(1, 0).contiguous() # D x C else: zs_weight = cls_path zs_weight = torch.cat( [zs_weight, zs_weight.new_zeros((zs_weight.shape[0], 1))], dim=1) # D x (C + 1) if model.roi_heads.box_predictor[0].cls_score.norm_weight: zs_weight = F.normalize(zs_weight, p=2, dim=0) zs_weight = zs_weight.to(model.device) for k in range(len(model.roi_heads.box_predictor)): del model.roi_heads.box_predictor[k].cls_score.zs_weight del model.roi_heads.box_predictor[k].cls_score.detection_weight model.roi_heads.box_predictor[k].cls_score.zs_weight = zs_weight model.roi_heads.box_predictor[k].cls_score.detection_weight = zs_weight ================================================ FILE: vldet/predictor.py ================================================ # Copyright (c) Facebook, Inc. and its affiliates. import atexit import bisect import multiprocessing as mp from collections import deque import cv2 import torch from detectron2.data import MetadataCatalog from detectron2.engine.defaults import DefaultPredictor from detectron2.utils.video_visualizer import VideoVisualizer from detectron2.utils.visualizer import ColorMode, Visualizer from .modeling.utils import reset_cls_test def get_clip_embeddings(vocabulary, prompt='a '): from vldet.modeling.text.text_encoder import build_text_encoder text_encoder = build_text_encoder(pretrain=True, visual_type="RN50") text_encoder.eval() texts = [prompt + x for x in vocabulary] emb = text_encoder(texts).detach().permute(1, 0).contiguous().cpu() return emb BUILDIN_CLASSIFIER = { 'lvis': 'datasets/metadata/lvis_v1_clip_a+cname.npy', 'objects365': 'datasets/metadata/o365_clip_a+cnamefix.npy', 'openimages': 'datasets/metadata/oid_clip_a+cname.npy', 'coco': 'datasets/metadata/coco_clip_a+cname.npy', } BUILDIN_METADATA_PATH = { 'lvis': 'lvis_v1_val', 'objects365': 'objects365_v2_val', 'openimages': 'oid_val_expanded', 'coco': 'coco_2017_val', } class VisualizationDemo(object): def __init__(self, cfg, args, instance_mode=ColorMode.IMAGE, parallel=False): """ Args: cfg (CfgNode): instance_mode (ColorMode): parallel (bool): whether to run the model in different processes from visualization. Useful since the visualization logic can be slow. """ if args.vocabulary == 'custom': self.metadata = MetadataCatalog.get("__unused") self.metadata.thing_classes = args.custom_vocabulary.split(',') classifier = get_clip_embeddings(self.metadata.thing_classes) else: self.metadata = MetadataCatalog.get( BUILDIN_METADATA_PATH[args.vocabulary]) classifier = BUILDIN_CLASSIFIER[args.vocabulary] num_classes = len(self.metadata.thing_classes) self.cpu_device = torch.device("cpu") self.instance_mode = instance_mode self.parallel = parallel if parallel: num_gpu = torch.cuda.device_count() self.predictor = AsyncPredictor(cfg, num_gpus=num_gpu) else: self.predictor = DefaultPredictor(cfg) reset_cls_test(self.predictor.model, classifier, num_classes) def run_on_image(self, image): """ Args: image (np.ndarray): an image of shape (H, W, C) (in BGR order). This is the format used by OpenCV. Returns: predictions (dict): the output of the model. vis_output (VisImage): the visualized image output. """ vis_output = None predictions = self.predictor(image) # Convert image from OpenCV BGR format to Matplotlib RGB format. image = image[:, :, ::-1] visualizer = Visualizer(image, self.metadata, instance_mode=self.instance_mode) if "panoptic_seg" in predictions: panoptic_seg, segments_info = predictions["panoptic_seg"] vis_output = visualizer.draw_panoptic_seg_predictions( panoptic_seg.to(self.cpu_device), segments_info ) else: if "sem_seg" in predictions: vis_output = visualizer.draw_sem_seg( predictions["sem_seg"].argmax(dim=0).to(self.cpu_device) ) if "instances" in predictions: instances = predictions["instances"].to(self.cpu_device) vis_output = visualizer.draw_instance_predictions(predictions=instances) return predictions, vis_output def _frame_from_video(self, video): while video.isOpened(): success, frame = video.read() if success: yield frame else: break def run_on_video(self, video): """ Visualizes predictions on frames of the input video. Args: video (cv2.VideoCapture): a :class:`VideoCapture` object, whose source can be either a webcam or a video file. Yields: ndarray: BGR visualizations of each video frame. """ video_visualizer = VideoVisualizer(self.metadata, self.instance_mode) def process_predictions(frame, predictions): frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) if "panoptic_seg" in predictions: panoptic_seg, segments_info = predictions["panoptic_seg"] vis_frame = video_visualizer.draw_panoptic_seg_predictions( frame, panoptic_seg.to(self.cpu_device), segments_info ) elif "instances" in predictions: predictions = predictions["instances"].to(self.cpu_device) vis_frame = video_visualizer.draw_instance_predictions(frame, predictions) elif "sem_seg" in predictions: vis_frame = video_visualizer.draw_sem_seg( frame, predictions["sem_seg"].argmax(dim=0).to(self.cpu_device) ) # Converts Matplotlib RGB format to OpenCV BGR format vis_frame = cv2.cvtColor(vis_frame.get_image(), cv2.COLOR_RGB2BGR) return vis_frame frame_gen = self._frame_from_video(video) if self.parallel: buffer_size = self.predictor.default_buffer_size frame_data = deque() for cnt, frame in enumerate(frame_gen): frame_data.append(frame) self.predictor.put(frame) if cnt >= buffer_size: frame = frame_data.popleft() predictions = self.predictor.get() yield process_predictions(frame, predictions) while len(frame_data): frame = frame_data.popleft() predictions = self.predictor.get() yield process_predictions(frame, predictions) else: for frame in frame_gen: yield process_predictions(frame, self.predictor(frame)) class AsyncPredictor: """ A predictor that runs the model asynchronously, possibly on >1 GPUs. Because rendering the visualization takes considerably amount of time, this helps improve throughput a little bit when rendering videos. """ class _StopToken: pass class _PredictWorker(mp.Process): def __init__(self, cfg, task_queue, result_queue): self.cfg = cfg self.task_queue = task_queue self.result_queue = result_queue super().__init__() def run(self): predictor = DefaultPredictor(self.cfg) while True: task = self.task_queue.get() if isinstance(task, AsyncPredictor._StopToken): break idx, data = task result = predictor(data) self.result_queue.put((idx, result)) def __init__(self, cfg, num_gpus: int = 1): """ Args: cfg (CfgNode): num_gpus (int): if 0, will run on CPU """ num_workers = max(num_gpus, 1) self.task_queue = mp.Queue(maxsize=num_workers * 3) self.result_queue = mp.Queue(maxsize=num_workers * 3) self.procs = [] for gpuid in range(max(num_gpus, 1)): cfg = cfg.clone() cfg.defrost() cfg.MODEL.DEVICE = "cuda:{}".format(gpuid) if num_gpus > 0 else "cpu" self.procs.append( AsyncPredictor._PredictWorker(cfg, self.task_queue, self.result_queue) ) self.put_idx = 0 self.get_idx = 0 self.result_rank = [] self.result_data = [] for p in self.procs: p.start() atexit.register(self.shutdown) def put(self, image): self.put_idx += 1 self.task_queue.put((self.put_idx, image)) def get(self): self.get_idx += 1 # the index needed for this request if len(self.result_rank) and self.result_rank[0] == self.get_idx: res = self.result_data[0] del self.result_data[0], self.result_rank[0] return res while True: # make sure the results are returned in the correct order idx, res = self.result_queue.get() if idx == self.get_idx: return res insert = bisect.bisect(self.result_rank, idx) self.result_rank.insert(insert, idx) self.result_data.insert(insert, res) def __len__(self): return self.put_idx - self.get_idx def __call__(self, image): self.put(image) return self.get() def shutdown(self): for _ in self.procs: self.task_queue.put(AsyncPredictor._StopToken()) @property def default_buffer_size(self): return len(self.procs) * 5